summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Henderson <dhenderson@gmail.com>2021-09-24 12:32:18 -0400
committerGitHub <noreply@github.com>2021-09-24 16:32:18 +0000
commit67697bba2ba926954508a05628a838457b25fa0e (patch)
tree97d3e976b24ed23f79a4da5fa34bf7f3e609890f
parentdc1c992939579b15c7d0ba7498065185eb2f6655 (diff)
Attempt to find the correct default git branch (#1217)
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
-rw-r--r--data/datasource_git.go52
-rw-r--r--data/datasource_git_test.go14
-rw-r--r--docs/content/datasources.md4
-rw-r--r--internal/tests/integration/datasources_git_test.go7
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) {