From ef8e5f97dba35e5dc53f469f35f3bf39ff2fb21f Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 24 Sep 2025 17:51:15 -0700 Subject: [PATCH] git: fix issue with checking out annotated tags by full ref If tag was already pulled by --tags or without refs/tags that creates ambigous reference in the shared repository. Signed-off-by: Tonis Tiigi --- source/git/source.go | 12 ++++++-- source/git/source_test.go | 64 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/source/git/source.go b/source/git/source.go index 497990c43..db586df51 100644 --- a/source/git/source.go +++ b/source/git/source.go @@ -531,7 +531,11 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context, g session.Group) (out // local refs are needed so they would be advertised on next fetches. Force is used // in case the ref is a branch and it now points to a different commit sha // TODO: is there a better way to do this? - args = append(args, "--force", ref+":tags/"+ref) + targetRef := ref + if !strings.HasPrefix(ref, "refs/tags/") { + targetRef = "tags/" + ref + } + args = append(args, "--force", ref+":"+targetRef) } if _, err := git.Run(ctx, args...); err != nil { return nil, errors.Wrapf(err, "failed to fetch remote %s", urlutil.RedactCredentials(gs.src.Remote)) @@ -615,7 +619,11 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context, g session.Group) (out pullref := ref if isAnnotatedTag { - pullref += ":refs/tags/" + pullref + targetRef := pullref + if !strings.HasPrefix(pullref, "refs/tags/") { + targetRef = "refs/tags/" + pullref + } + pullref += ":" + targetRef } else if gitutil.IsCommitSHA(ref) { pullref = "refs/buildkit/" + identity.NewID() _, err = git.Run(ctx, "update-ref", pullref, ref) diff --git a/source/git/source_test.go b/source/git/source_test.go index 3d8c55427..848a78752 100644 --- a/source/git/source_test.go +++ b/source/git/source_test.go @@ -639,6 +639,70 @@ func testFetchByTag(t *testing.T, tag, expectedCommitSubject string, isAnnotated require.Contains(t, strings.TrimSpace(string(gitLogOutput)), expectedCommitSubject) } } +func TestFetchAnnotatedTagAfterCloneSHA1(t *testing.T) { + testFetchAnnotatedTagAfterClone(t, "sha1") +} + +func TestFetchAnnotatedTagAfterCloneSHA256(t *testing.T) { + testFetchAnnotatedTagAfterClone(t, "sha256") +} + +func testFetchAnnotatedTagAfterClone(t *testing.T, format string) { + if runtime.GOOS == "windows" { + t.Skip("Depends on unimplemented containerd bind-mount support on Windows") + } + + t.Parallel() + ctx := namespaces.WithNamespace(context.Background(), "buildkit-test") + ctx = logProgressStreams(ctx, t) + + repo := setupGitRepo(t, format) + cmd := exec.Command("git", "rev-parse", "HEAD") + cmd.Dir = repo.mainPath + + out, err := cmd.Output() + require.NoError(t, err) + + expLen := 40 + if format == "sha256" { + expLen = 64 + } + sha := strings.TrimSpace(string(out)) + require.Equal(t, expLen, len(sha)) + + gs := setupGitSource(t, t.TempDir()) + + id := &GitIdentifier{Remote: repo.mainURL, Ref: sha, KeepGitDir: true} + + g, err := gs.Resolve(ctx, id, nil, nil) + require.NoError(t, err) + + key1, pin1, _, done, err := g.CacheKey(ctx, nil, 0) + require.NoError(t, err) + require.True(t, done) + + require.GreaterOrEqual(t, len(key1), expLen+4) + require.Equal(t, expLen, len(pin1)) + + ref, err := g.Snapshot(ctx, nil) + require.NoError(t, err) + ref.Release(context.TODO()) + + id = &GitIdentifier{Remote: repo.mainURL, Ref: "refs/tags/v1.2.3", KeepGitDir: true} + g, err = gs.Resolve(ctx, id, nil, nil) + require.NoError(t, err) + + key1, pin1, _, done, err = g.CacheKey(ctx, nil, 0) + require.NoError(t, err) + require.True(t, done) + + require.GreaterOrEqual(t, len(key1), expLen+4) + require.Equal(t, expLen, len(pin1)) + + ref, err = g.Snapshot(ctx, nil) + require.NoError(t, err) + ref.Release(context.TODO()) +} func TestMultipleTagAccessKeepGitDirSHA1(t *testing.T) { testMultipleTagAccess(t, true, "sha1")