Skip to content

Commit

Permalink
Support fetching/checkouts of non-branch/-tag git refs (#322)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
marxarelli authored and VaibhavPage committed Aug 2, 2019
1 parent 30aedd1 commit 6a5a622
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 11 deletions.
7 changes: 7 additions & 0 deletions pkg/apis/sensor/v1alpha1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion pkg/apis/sensor/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 29 additions & 10 deletions store/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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))
Expand All @@ -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
}
Expand All @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions store/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
})
}

0 comments on commit 6a5a622

Please sign in to comment.