From 6a5a62227da9ab9c8a8e7ea3dea8653d5b3e707e Mon Sep 17 00:00:00 2001 From: Daniel Duvall Date: Fri, 2 Aug 2019 08:22:52 -0700 Subject: [PATCH] Support fetching/checkouts of non-branch/-tag git refs (#322) * Support fetching/checkouts of non-branch/-tag git refs Provides a `source.git.ref` field for specifying a specific non-branch non-tag git ref at which to perform a checkout. Providing one will result in a shallow (depth 1) clone of the repo and shallow subsequent fetches. The motivation for this change is to support sourcing of trigger templates from Gerrit patchsets. Gerrit uses non-branch/-tag refs extensively and specifically in this case stores change patchsets in `refs/changes/[slice]/[change]/[patchset]`. While arbitrary refs are kind of an obscure git feature, there very well may be other git based systems utilizing them. * Fixed protobuf field index for Ref --- pkg/apis/sensor/v1alpha1/openapi_generated.go | 7 ++++ pkg/apis/sensor/v1alpha1/types.go | 7 +++- store/git.go | 39 ++++++++++++++----- store/git_test.go | 6 +++ 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/pkg/apis/sensor/v1alpha1/openapi_generated.go b/pkg/apis/sensor/v1alpha1/openapi_generated.go index 1e6455906ca4..4e673bcace78 100644 --- a/pkg/apis/sensor/v1alpha1/openapi_generated.go +++ b/pkg/apis/sensor/v1alpha1/openapi_generated.go @@ -433,6 +433,13 @@ func schema_pkg_apis_sensor_v1alpha1_GitArtifact(ref common.ReferenceCallback) c Format: "", }, }, + "ref": { + SchemaProps: spec.SchemaProps{ + Description: "Ref to use to pull trigger resource. Will result in a shallow clone and fetch.", + Type: []string{"string"}, + Format: "", + }, + }, "remote": { SchemaProps: spec.SchemaProps{ Description: "Remote to manage set of tracked repositories. Defaults to \"origin\". Refer https://git-scm.com/docs/git-remote", diff --git a/pkg/apis/sensor/v1alpha1/types.go b/pkg/apis/sensor/v1alpha1/types.go index 7370b286228c..f325cf653e7c 100644 --- a/pkg/apis/sensor/v1alpha1/types.go +++ b/pkg/apis/sensor/v1alpha1/types.go @@ -466,10 +466,15 @@ type GitArtifact struct { // +optional Tag string `json:"tag,omitempty" protobuf:"bytes,8,opt,name=tag"` + // Ref to use to pull trigger resource. Will result in a shallow clone and + // fetch. + // +optional + Ref string `json:"ref,omitempty" protobuf:"bytes,9,opt,name=ref"` + // Remote to manage set of tracked repositories. Defaults to "origin". // Refer https://git-scm.com/docs/git-remote // +optional - Remote *GitRemoteConfig `json:"remote" protobuf:"bytes,9,opt,name=remote"` + Remote *GitRemoteConfig `json:"remote" protobuf:"bytes,10,opt,name=remote"` } // GitRemoteConfig contains the configuration of a Git remote diff --git a/store/git.go b/store/git.go index 74ca15bc0997..9c7d02727590 100644 --- a/store/git.go +++ b/store/git.go @@ -138,6 +138,13 @@ func (g *GitArtifactReader) readFromRepository(r *git.Repository) ([]byte, error fetchOptions.Auth = auth } + // In the case of a specific given ref, it isn't necessary to fetch anything + // but the single ref + if g.artifact.Ref != "" { + fetchOptions.Depth = 1 + fetchOptions.RefSpecs = []config.RefSpec{config.RefSpec(g.artifact.Ref + ":" + g.artifact.Ref)} + } + if err := r.Fetch(fetchOptions); err != nil && err != git.NoErrAlreadyUpToDate { return nil, fmt.Errorf("failed to fetch. err: %v", err) } @@ -146,17 +153,20 @@ func (g *GitArtifactReader) readFromRepository(r *git.Repository) ([]byte, error return nil, fmt.Errorf("failed to checkout. err: %+v", err) } - pullOpts := &git.PullOptions{ - RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, - ReferenceName: g.getBranchOrTag().Branch, - Force: true, - } - if auth != nil { - pullOpts.Auth = auth - } + // In the case of a specific given ref, it shouldn't be necessary to pull + if g.artifact.Ref != "" { + pullOpts := &git.PullOptions{ + RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, + ReferenceName: g.getBranchOrTag().Branch, + Force: true, + } + if auth != nil { + pullOpts.Auth = auth + } - if err := w.Pull(pullOpts); err != nil && err != git.NoErrAlreadyUpToDate { - return nil, fmt.Errorf("failed to pull latest updates. err: %+v", err) + if err := w.Pull(pullOpts); err != nil && err != git.NoErrAlreadyUpToDate { + return nil, fmt.Errorf("failed to pull latest updates. err: %+v", err) + } } return ioutil.ReadFile(fmt.Sprintf("%s/%s", g.artifact.CloneDirectory, g.artifact.FilePath)) @@ -173,6 +183,9 @@ func (g *GitArtifactReader) getBranchOrTag() *git.CheckoutOptions { if g.artifact.Tag != "" { opts.Branch = plumbing.NewTagReferenceName(g.artifact.Tag) } + if g.artifact.Ref != "" { + opts.Branch = plumbing.ReferenceName(g.artifact.Ref) + } return opts } @@ -197,6 +210,12 @@ func (g *GitArtifactReader) Read() ([]byte, error) { cloneOpt.Auth = auth } + // In the case of a specific given ref, it isn't necessary to have branch + // histories + if g.artifact.Ref != "" { + cloneOpt.Depth = 1 + } + r, err = git.PlainClone(g.artifact.CloneDirectory, false, cloneOpt) if err != nil { return nil, fmt.Errorf("failed to clone repository. err: %+v", err) diff --git a/store/git_test.go b/store/git_test.go index 49244ef010de..9f3432dfd5e6 100644 --- a/store/git_test.go +++ b/store/git_test.go @@ -94,4 +94,10 @@ func TestGetBranchOrTag(t *testing.T) { tag := gar.getBranchOrTag() convey.So(tag.Branch, convey.ShouldNotEqual, "refs/heads/master") }) + + convey.Convey("Given a git artifact with a specific ref, get the ref", t, func() { + gar.artifact.Ref = "refs/something/weird/or/specific" + br := gar.getBranchOrTag() + convey.So(br.Branch, convey.ShouldEqual, "refs/something/weird/or/specific") + }) }