diff --git a/README.md b/README.md index 4865872..877965e 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,47 @@ $ git push --set-upstream ipld:// master Note: Some features like remote tracking are still missing, though the plugin is quite usable. IPNS helper is WIP and doesn't yet do what it should +## Custom API URL + +It is possible to pass the API URL of the running ipfs daemon for cases where the +daemon is running as another user, in a container, in another machine, or +behind a proxy. One can either specify the API URL through the `IPFS_API_URL` +environment variable: + +``` +$ export IPFS_API_URL=http://localhost:5001 + +other git commands... +``` + +Or through the remote URL itself: + +``` +Clone an example repository: +$ git clone ipld://localhost:5001/2347e110c29742a1783134ef45f5bff58b29e40e + +Pull a commit: +$ git pull ipld://localhost:5001/2347e110c29742a1783134ef45f5bff58b29e40e + +Push (remember to end with slash if there is no previous hash): +$ git push --set-upstream ipld://localhost:5001/ master +``` + +If the API URL is **behind an https proxy**, then one can set `IPFS_API_URL` with the proper https URL. It is also possible to use the `ipld+https://` schema on the git command itself: + +``` +Clone an example repository: +$ git clone ipld+https://my.ipfs.domain/my_api/2347e110c29742a1783134ef45f5bff58b29e40e + +Pull a commit: +$ git pull ipld+https://my.ipfs.domain/my_api/2347e110c29742a1783134ef45f5bff58b29e40e + +Push (remember to end with slash if there is no previous hash): +$ git push --set-upstream ipld+https://my.ipfs.domain/my_api/ master +``` + +**Note:** The link `git-remote-ipld+https` must be created manually for this to work. + ## Installation 1. `go get github.com/ipfs-shipyard/git-remote-ipld` 2. `make install` @@ -32,6 +73,9 @@ quite usable. IPNS helper is WIP and doesn't yet do what it should * `fetch: manifest has unsupported version: 2 (we support 3)` on any command - This usually means that tracker data format has changed - Simply do `rm -rf .git/ipld` +* Invalid hash when pushing to a new remote while specifying a custom API URL + - The remote URL **must** end with a forward slash if there is no hash specified: + - `git push --set-upstream ipld://127.0.0.1:5001/ master` ## License MIT diff --git a/cmd/git-remote-ipld/ipld.go b/cmd/git-remote-ipld/ipld.go index 1bf3c14..d29f282 100644 --- a/cmd/git-remote-ipld/ipld.go +++ b/cmd/git-remote-ipld/ipld.go @@ -35,6 +35,7 @@ type refPath struct { type IpnsHandler struct { api *ipfs.Shell + apiURL string remoteName string currentHash string @@ -44,7 +45,11 @@ type IpnsHandler struct { } func (h *IpnsHandler) Initialize(remote *core.Remote) error { - h.api = ipfs.NewLocalShell() + if len(h.apiURL) == 0 { + h.api = ipfs.NewLocalShell() + } else { + h.api = ipfs.NewShell(h.apiURL) + } h.currentHash = h.remoteName return nil } diff --git a/cmd/git-remote-ipld/main.go b/cmd/git-remote-ipld/main.go index 8284ca3..b19e660 100644 --- a/cmd/git-remote-ipld/main.go +++ b/cmd/git-remote-ipld/main.go @@ -11,8 +11,10 @@ import ( ) const ( - IPLD_PREFIX = "ipld://" - IPFS_PREFIX = "ipfs://" + PREFIX_SEP = "://" + + IPLD_PREFIX = "ipld" + PREFIX_SEP + IPFS_PREFIX = "ipfs" + PREFIX_SEP EMPTY_REPO = "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" ) @@ -23,15 +25,55 @@ func Main(args []string, reader io.Reader, writer io.Writer, logger *log.Logger) } remoteName := args[2] - if strings.HasPrefix(remoteName, IPLD_PREFIX) || strings.HasPrefix(remoteName, IPFS_PREFIX) { - remoteName = remoteName[len(IPLD_PREFIX):] + + sepIdx := strings.Index(remoteName, PREFIX_SEP) + prefix := "" + if sepIdx > 0 { + prefix = remoteName[:sepIdx + len(PREFIX_SEP)] + remoteName = remoteName[len(prefix):] + } else { + return fmt.Errorf("Invalid URL: %s", remoteName) + } + + apiPrefixIdx := strings.Index(prefix, "+") + apiPrefix := "" + if apiPrefixIdx == 0 { + return fmt.Errorf("Invalid URL prefix: %s", prefix) + } else if apiPrefixIdx > 0 { + apiPrefix = prefix[apiPrefixIdx+1:] + prefix = prefix[:apiPrefixIdx] + PREFIX_SEP + } + + if prefix != IPLD_PREFIX && prefix != IPFS_PREFIX { + return fmt.Errorf("Unknown URL prefix: %s", prefix) + } + + slashIdx := strings.LastIndex(remoteName, "/") + + apiURL := os.Getenv("IPFS_API_URL") + if slashIdx < 0 { + if len(apiPrefix) != 0 { + return fmt.Errorf("Prefix has been provided for the API, but not an API URL") + } + } else { + apiURL = apiPrefix + remoteName[:slashIdx+1] + } + + // Quirk: The go-ipfs-api throws unexpected redirect if the API URL ends with / + if strings.HasSuffix(apiURL, "/") { + apiURL = apiURL[:len(apiURL)-1] + } + + if len(apiURL) > 0 { + fmt.Fprintf(os.Stderr, "Using API URL: %s\n", apiURL) } + remoteName = remoteName[slashIdx+1:] if remoteName == "" { remoteName = EMPTY_REPO } - remote, err := core.NewRemote(&IpnsHandler{remoteName: remoteName}, reader, writer, logger) + remote, err := core.NewRemote(&IpnsHandler{apiURL: apiURL, remoteName: remoteName}, reader, writer, logger, apiURL) if err != nil { return err } diff --git a/core/fetch.go b/core/fetch.go index a034205..fe67772 100644 --- a/core/fetch.go +++ b/core/fetch.go @@ -42,7 +42,13 @@ type Fetch struct { api *ipfs.Shell } -func NewFetch(gitDir string, tracker *Tracker, provider ObjectProvider) *Fetch { +func NewFetch(gitDir string, tracker *Tracker, provider ObjectProvider, apiURL string) *Fetch { + var api *ipfs.Shell + if len(apiURL) == 0 { + api = ipfs.NewLocalShell() + } else { + api = ipfs.NewShell(apiURL) + } return &Fetch{ objectDir: path.Join(gitDir, "objects"), gitDir: gitDir, @@ -60,7 +66,7 @@ func NewFetch(gitDir string, tracker *Tracker, provider ObjectProvider) *Fetch { doneCh: make(chan []byte), provider: provider, - api: ipfs.NewLocalShell(), + api: api, } } diff --git a/core/push.go b/core/push.go index f81ee11..0fb674d 100644 --- a/core/push.go +++ b/core/push.go @@ -23,6 +23,7 @@ import ( type Push struct { objectDir string gitDir string + apiURL string done uint64 todoc uint64 @@ -40,10 +41,11 @@ type Push struct { NewNode func(hash cid.Cid, data []byte) error } -func NewPush(gitDir string, tracker *Tracker, repo *git.Repository) *Push { +func NewPush(gitDir string, tracker *Tracker, repo *git.Repository, apiURL string) *Push { return &Push{ objectDir: path.Join(gitDir, "objects"), gitDir: gitDir, + apiURL: apiURL, todo: list.New(), log: log.New(os.Stderr, "push: ", 0), @@ -67,7 +69,12 @@ func (p *Push) PushHash(hash string) error { func (p *Push) doWork() error { defer p.wg.Wait() - api := ipfs.NewLocalShell() + var api *ipfs.Shell + if len(p.apiURL) == 0 { + api = ipfs.NewLocalShell() + } else { + api = ipfs.NewShell(p.apiURL) + } intch := make(chan os.Signal, 1) signal.Notify(intch, os.Interrupt) diff --git a/core/remote.go b/core/remote.go index 85a38f0..087c68c 100644 --- a/core/remote.go +++ b/core/remote.go @@ -29,6 +29,8 @@ type Remote struct { Logger *log.Logger localDir string + apiURL string + Repo *git.Repository Tracker *Tracker @@ -37,7 +39,7 @@ type Remote struct { todo []func() (string, error) } -func NewRemote(handler RemoteHandler, reader io.Reader, writer io.Writer, logger *log.Logger) (*Remote, error) { +func NewRemote(handler RemoteHandler, reader io.Reader, writer io.Writer, logger *log.Logger, apiURL string) (*Remote, error) { localDir, err := GetLocalDir() if err != nil { return nil, err @@ -87,11 +89,11 @@ func (r *Remote) Printf(format string, a ...interface{}) (n int, err error) { } func (r *Remote) NewPush() *Push { - return NewPush(r.localDir, r.Tracker, r.Repo) + return NewPush(r.localDir, r.Tracker, r.Repo, r.apiURL) } func (r *Remote) NewFetch() *Fetch { - return NewFetch(r.localDir, r.Tracker, r.Handler.ProvideBlock) + return NewFetch(r.localDir, r.Tracker, r.Handler.ProvideBlock, r.apiURL) } func (r *Remote) Close() error {