Implements no-op before CheckoutTag in gogit

Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
This commit is contained in:
Somtochi Onyekwere 2022-05-06 11:32:56 +01:00 committed by Paulo Gomes
parent 8a3df9da42
commit 2bb3a1fea9
No known key found for this signature in database
GPG Key ID: 9995233870E99BEE
2 changed files with 66 additions and 12 deletions

View File

@ -29,6 +29,7 @@ import (
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/storage/memory" "github.com/go-git/go-git/v5/storage/memory"
"github.com/fluxcd/pkg/gitutil" "github.com/fluxcd/pkg/gitutil"
@ -46,7 +47,7 @@ func CheckoutStrategyForOptions(_ context.Context, opts git.CheckoutOptions) git
case opts.SemVer != "": case opts.SemVer != "":
return &CheckoutSemVer{SemVer: opts.SemVer, RecurseSubmodules: opts.RecurseSubmodules} return &CheckoutSemVer{SemVer: opts.SemVer, RecurseSubmodules: opts.RecurseSubmodules}
case opts.Tag != "": case opts.Tag != "":
return &CheckoutTag{Tag: opts.Tag, RecurseSubmodules: opts.RecurseSubmodules} return &CheckoutTag{Tag: opts.Tag, RecurseSubmodules: opts.RecurseSubmodules, LastRevision: opts.LastRevision}
default: default:
branch := opts.Branch branch := opts.Branch
if branch == "" { if branch == "" {
@ -71,19 +72,11 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, opts *g
ref := plumbing.NewBranchReferenceName(c.Branch) ref := plumbing.NewBranchReferenceName(c.Branch)
// check if previous revision has changed before attempting to clone // check if previous revision has changed before attempting to clone
if c.LastRevision != "" { if c.LastRevision != "" {
config := &config.RemoteConfig{ currentRevision, err := getLastRevision(url, ref, opts, authMethod)
Name: git.DefaultOrigin,
URLs: []string{url},
}
rem := extgogit.NewRemote(memory.NewStorage(), config)
refs, err := rem.List(&extgogit.ListOptions{
Auth: authMethod,
})
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to list remote for '%s': %w", url, err) return nil, err
} }
currentRevision := filterRefs(refs, ref)
if currentRevision != "" && currentRevision == c.LastRevision { if currentRevision != "" && currentRevision == c.LastRevision {
return nil, git.NoChangesError{ return nil, git.NoChangesError{
Message: "no changes since last reconcilation", Message: "no changes since last reconcilation",
@ -119,9 +112,31 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, opts *g
return buildCommitWithRef(cc, ref) return buildCommitWithRef(cc, ref)
} }
func getLastRevision(url string, ref plumbing.ReferenceName, opts *git.AuthOptions, authMethod transport.AuthMethod) (string, error) {
config := &config.RemoteConfig{
Name: git.DefaultOrigin,
URLs: []string{url},
}
rem := extgogit.NewRemote(memory.NewStorage(), config)
listOpts := &extgogit.ListOptions{
Auth: authMethod,
}
if opts != nil && opts.CAFile != nil {
listOpts.CABundle = opts.CAFile
}
refs, err := rem.List(listOpts)
if err != nil {
return "", fmt.Errorf("unable to list remote for '%s': %w", url, err)
}
currentRevision := filterRefs(refs, ref)
return currentRevision, nil
}
type CheckoutTag struct { type CheckoutTag struct {
Tag string Tag string
RecurseSubmodules bool RecurseSubmodules bool
LastRevision string
} }
func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, opts *git.AuthOptions) (*git.Commit, error) { func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, opts *git.AuthOptions) (*git.Commit, error) {
@ -130,6 +145,20 @@ func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, opts *git.
return nil, fmt.Errorf("failed to construct auth method with options: %w", err) return nil, fmt.Errorf("failed to construct auth method with options: %w", err)
} }
ref := plumbing.NewTagReferenceName(c.Tag) ref := plumbing.NewTagReferenceName(c.Tag)
// check if previous revision has changed before attempting to clone
if c.LastRevision != "" {
currentRevision, err := getLastRevision(url, ref, opts, authMethod)
if err != nil {
return nil, err
}
if currentRevision != "" && currentRevision == c.LastRevision {
return nil, git.NoChangesError{
Message: "no changes since last reconcilation",
ObservedRevision: currentRevision,
}
}
}
repo, err := extgogit.PlainCloneContext(ctx, path, false, &extgogit.CloneOptions{ repo, err := extgogit.PlainCloneContext(ctx, path, false, &extgogit.CloneOptions{
URL: url, URL: url,
Auth: authMethod, Auth: authMethod,

View File

@ -137,6 +137,8 @@ func TestCheckoutTag_Checkout(t *testing.T) {
checkoutTag string checkoutTag string
expectTag string expectTag string
expectErr string expectErr string
lastRev string
setLastRev bool
}{ }{
{ {
name: "Tag", name: "Tag",
@ -144,6 +146,20 @@ func TestCheckoutTag_Checkout(t *testing.T) {
checkoutTag: "tag-1", checkoutTag: "tag-1",
expectTag: "tag-1", expectTag: "tag-1",
}, },
{
name: "Skip Tag if last revision hasn't changed",
tag: "tag-2",
checkoutTag: "tag-2",
setLastRev: true,
expectErr: "no changes since last reconcilation",
},
{
name: "Last revision changed",
tag: "tag-3",
checkoutTag: "tag-3",
expectTag: "tag-3",
lastRev: "tag-3/<fake-hash>",
},
{ {
name: "Annotated", name: "Annotated",
tag: "annotated", tag: "annotated",
@ -168,12 +184,13 @@ func TestCheckoutTag_Checkout(t *testing.T) {
} }
var h plumbing.Hash var h plumbing.Hash
var tagHash *plumbing.Reference
if tt.tag != "" { if tt.tag != "" {
h, err = commitFile(repo, "tag", tt.tag, time.Now()) h, err = commitFile(repo, "tag", tt.tag, time.Now())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = tag(repo, h, !tt.annotated, tt.tag, time.Now()) tagHash, err = tag(repo, h, !tt.annotated, tt.tag, time.Now())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -182,10 +199,18 @@ func TestCheckoutTag_Checkout(t *testing.T) {
tag := CheckoutTag{ tag := CheckoutTag{
Tag: tt.checkoutTag, Tag: tt.checkoutTag,
} }
if tt.setLastRev {
tag.LastRevision = fmt.Sprintf("%s/%s", tt.tag, tagHash.Hash().String())
}
if tt.lastRev != "" {
tag.LastRevision = tt.lastRev
}
tmpDir := t.TempDir() tmpDir := t.TempDir()
cc, err := tag.Checkout(context.TODO(), tmpDir, path, nil) cc, err := tag.Checkout(context.TODO(), tmpDir, path, nil)
if tt.expectErr != "" { if tt.expectErr != "" {
g.Expect(err).ToNot(BeNil())
g.Expect(err.Error()).To(ContainSubstring(tt.expectErr)) g.Expect(err.Error()).To(ContainSubstring(tt.expectErr))
g.Expect(cc).To(BeNil()) g.Expect(cc).To(BeNil())
return return