diff options
| -rw-r--r-- | data/datasource_git.go | 52 | ||||
| -rw-r--r-- | data/datasource_git_test.go | 14 | ||||
| -rw-r--r-- | docs/content/datasources.md | 4 | ||||
| -rw-r--r-- | internal/tests/integration/datasources_git_test.go | 7 |
4 files changed, 72 insertions, 5 deletions
diff --git a/data/datasource_git.go b/data/datasource_git.go index 6c2491de..bd1811b4 100644 --- a/data/datasource_git.go +++ b/data/datasource_git.go @@ -14,12 +14,14 @@ import ( "github.com/hairyhenderson/gomplate/v3/base64" "github.com/hairyhenderson/gomplate/v3/env" + "github.com/rs/zerolog" "github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/transport" + "github.com/go-git/go-git/v5/plumbing/transport/client" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/plumbing/transport/ssh" "github.com/go-git/go-git/v5/storage/memory" @@ -141,6 +143,7 @@ func cloneURL(u *url.URL) *url.URL { return out } +// refFromURL - extract the ref from the URL fragment if present func (g gitsource) refFromURL(u *url.URL) plumbing.ReferenceName { switch { case strings.HasPrefix(u.Fragment, "refs/"): @@ -152,6 +155,44 @@ func (g gitsource) refFromURL(u *url.URL) plumbing.ReferenceName { } } +// refFromRemoteHead - extract the ref from the remote HEAD, to work around +// hard-coded 'master' default branch in go-git. +// Should be unnecessary once https://github.com/go-git/go-git/issues/249 is +// fixed. +func (g gitsource) refFromRemoteHead(ctx context.Context, u *url.URL, auth transport.AuthMethod) (plumbing.ReferenceName, error) { + e, err := transport.NewEndpoint(u.String()) + if err != nil { + return "", err + } + + cli, err := client.NewClient(e) + if err != nil { + return "", err + } + + s, err := cli.NewUploadPackSession(e, auth) + if err != nil { + return "", err + } + + info, err := s.AdvertisedReferencesContext(ctx) + if err != nil { + return "", err + } + + refs, err := info.AllReferences() + if err != nil { + return "", err + } + + headRef, ok := refs["HEAD"] + if !ok { + return "", fmt.Errorf("no HEAD ref found") + } + + return headRef.Target(), nil +} + // clone a repo for later reading through http(s), git, or ssh. u must be the URL to the repo // itself, and must have any file path stripped func (g gitsource) clone(ctx context.Context, repoURL *url.URL, depth int) (billy.Filesystem, *git.Repository, error) { @@ -175,6 +216,17 @@ func (g gitsource) clone(ctx context.Context, repoURL *url.URL, depth int) (bill u.Fragment = "" u.RawQuery = "" + // attempt to get the ref from the remote so we don't default to master + if ref == "" { + ref, err = g.refFromRemoteHead(ctx, u, auth) + if err != nil { + zerolog.Ctx(ctx).Warn(). + Stringer("repoURL", u). + Err(err). + Msg("failed to get ref from remote, using default") + } + } + opts := &git.CloneOptions{ URL: u.String(), Auth: auth, diff --git a/data/datasource_git_test.go b/data/datasource_git_test.go index f48393c3..33771410 100644 --- a/data/datasource_git_test.go +++ b/data/datasource_git_test.go @@ -229,10 +229,18 @@ func setupGitRepo(t *testing.T) billy.Filesystem { r, err := git.Init(s, repo) assert.NilError(t, err) + // default to main + h := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.ReferenceName("refs/heads/main")) + err = s.SetReference(h) + assert.NilError(t, err) + // config needs to be created after setting up a "normal" fs repo // this is possibly a bug in git-go? c, err := r.Config() assert.NilError(t, err) + + c.Init.DefaultBranch = "main" + s.SetConfig(c) assert.NilError(t, err) @@ -331,12 +339,12 @@ func TestOpenFileRepo(t *testing.T) { b, _ := ioutil.ReadAll(f) assert.Equal(t, "hello world", string(b)) - _, repo, err := g.clone(ctx, mustParseURL("git+file:///repo#master"), 0) + _, repo, err := g.clone(ctx, mustParseURL("git+file:///repo#main"), 0) assert.NilError(t, err) - ref, err := repo.Reference(plumbing.NewBranchReferenceName("master"), true) + ref, err := repo.Reference(plumbing.NewBranchReferenceName("main"), true) assert.NilError(t, err) - assert.Equal(t, "refs/heads/master", ref.Name().String()) + assert.Equal(t, "refs/heads/main", ref.Name().String()) _, repo, err = g.clone(ctx, mustParseURL("git+file:///repo#refs/tags/v1"), 0) assert.NilError(t, err) diff --git a/docs/content/datasources.md b/docs/content/datasources.md index cf3667da..7743b88e 100644 --- a/docs/content/datasources.md +++ b/docs/content/datasources.md @@ -475,7 +475,7 @@ The _scheme_, _authority_ (with _userinfo_), _path_, and _fragment_ are used, an - the _authority_ component points to the remote git server hostname (and optional port, if applicable). The _userinfo_ subcomponent can be used for authenticated datasources like `git+https` and `git+ssh`. - the _path_ component is a composite of the path to the repository, and the path to the file or directory being referenced within. The `//` sequence (double forward-slash) is used to separate the repository from the path. If no `//` is present in the URL, the datasource will point to the root directory of the repository. - the _fragment_ component can be used to specify which branch or tag to reference. By default, the repository's default branch will be chosen. - - branches can be referenced by short name or by the long form. Valid fragments are `#master`, `#develop`, `#refs/heads/mybranch`, etc... + - branches can be referenced by short name or by the long form. Valid fragments are `#main`, `#master`, `#develop`, `#refs/heads/mybranch`, etc... - tags must use the long form prefixed by `refs/tags/`, i.e. `#refs/tags/v1` for the `v1` tag ### Authentication @@ -701,7 +701,7 @@ This table describes the currently-supported authentication mechanisms and how t _**Note:**_ The secret values listed in the above table can either be set in environment variables or provided in files. This can increase security when using [Docker Swarm Secrets](https://docs.docker.com/engine/swarm/secrets/), for example. To use files, specify the filename by appending `_FILE` to the environment variable, (i.e. `VAULT_USER_ID_FILE`). If the non-file variable is set, this will override any `_FILE` variable and the secret file will be ignored. -### Vault Permissions +### Vault Permissions The correct capabilities must be allowed for the [authenticated](#vault-authentication) credentials. See the [Vault documentation](https://www.vaultproject.io/docs/concepts/policies.html#capabilities) for full details. diff --git a/internal/tests/integration/datasources_git_test.go b/internal/tests/integration/datasources_git_test.go index 70e5e529..50b8c02c 100644 --- a/internal/tests/integration/datasources_git_test.go +++ b/internal/tests/integration/datasources_git_test.go @@ -117,6 +117,13 @@ func TestDatasources_GitHTTPDatasource(t *testing.T) { "-i", `{{ .short.glossary.title}}`, ).run() assertSuccess(t, o, e, err, "example glossary") + + // and one with a default branch of 'main' + o, e, err = cmd(t, + "-c", "data=git+https://github.com/hairyhenderson/git-fixtures.git//small_test.json", + "-i", `{{ .data.foo}}`, + ).run() + assertSuccess(t, o, e, err, "bar") } func TestDatasources_GitSSHDatasource(t *testing.T) { |
