diff --git a/client/client_git_source_test.go b/client/client_git_source_test.go index e711cd00c..71190a1ef 100644 --- a/client/client_git_source_test.go +++ b/client/client_git_source_test.go @@ -543,6 +543,88 @@ func testGitResolveMutatedSource(t *testing.T, sb integration.Sandbox) { checkAllReleasable(t, c, sb, false) } +// testGitResolveCacheAfterFailure resolves the same ref repeatedly in one +// build after a failed and a cached resolve, so the resolver cache is read +// after both have released their lock. +func testGitResolveCacheAfterFailure(t *testing.T, sb integration.Sandbox) { + integration.SkipOnPlatform(t, "windows") + ctx := sb.Context() + c, err := New(ctx, sb.Address()) + require.NoError(t, err) + defer c.Close() + + gitDir := t.TempDir() + gitCommands := []string{ + "git init", + "git config --local user.email test", + "git config --local user.name test", + "echo a > a", + "git add a", + "git commit -m a", + "git branch v1", + "echo b > b", + "git add b", + "git commit -m b", + "git update-server-info", + } + err = runInDir(gitDir, gitCommands...) + require.NoError(t, err) + + cmd := exec.CommandContext(t.Context(), "git", "rev-parse", "v1") + cmd.Dir = gitDir + out, err := cmd.Output() + require.NoError(t, err) + commitV1 := strings.TrimSpace(string(out)) + + cmd = exec.CommandContext(t.Context(), "git", "rev-parse", "HEAD") + cmd.Dir = gitDir + out, err = cmd.Output() + require.NoError(t, err) + commitHead := strings.TrimSpace(string(out)) + + server := httptest.NewServer(http.FileServer(http.Dir(filepath.Clean(gitDir)))) + defer server.Close() + + _, err = c.Build(ctx, SolveOpt{}, "test", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { + resolve := func(checksum string) (*sourceresolver.MetaResponse, error) { + attrs := map[string]string{ + "git.fullurl": server.URL + "/.git", + } + if checksum != "" { + attrs[pb.AttrGitChecksum] = checksum + } + return c.ResolveSourceMetadata(ctx, &pb.SourceOp{ + Identifier: "git://" + strings.TrimPrefix(server.URL, "http://") + "/.git#v1", + Attrs: attrs, + }, sourceresolver.Opt{}) + } + + // failed resolve + _, err := resolve(commitHead) + require.ErrorContains(t, err, "expected checksum to match") + + md, err := resolve("") + require.NoError(t, err) + require.NotNil(t, md.Git) + require.Equal(t, commitV1, md.Git.Checksum) + + // resolved from cache + md, err = resolve("") + require.NoError(t, err) + require.NotNil(t, md.Git) + require.Equal(t, commitV1, md.Git.Checksum) + + // cached value does not match the checksum + _, err = resolve(commitHead) + require.ErrorContains(t, err, "expected checksum to match") + + return gateway.NewResult(), nil + }, nil) + require.NoError(t, err) + + checkAllReleasable(t, c, sb, false) +} + func testGitResolveSourceMetadata(t *testing.T, sb integration.Sandbox) { // TODO: remove once CI has Git for Windows >= 2.56.0.windows.2 // https://github.com/git-for-windows/git/issues/6449 diff --git a/client/client_test.go b/client/client_test.go index 88699a4cb..203ba1117 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -158,6 +158,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){ testGitBundleRoundTrip, testGitBundleRoundTripRegistry, testGitResolveMutatedSource, + testGitResolveCacheAfterFailure, testGitResolveSourceMetadata, // client_http_source_test.go diff --git a/go.mod b/go.mod index 3440d0a24..9ee5e5afe 100644 --- a/go.mod +++ b/go.mod @@ -78,7 +78,7 @@ require ( github.com/spdx/tools-golang v0.5.7 github.com/stretchr/testify v1.12.1 github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 - github.com/tonistiigi/fsutil v0.0.0-20261027232937-ca65610c2295 + github.com/tonistiigi/fsutil v0.0.0-20261103125000-e075fd9305eb github.com/tonistiigi/go-actions-cache v0.0.0-20260930145529-afe8013b5ac4 github.com/tonistiigi/go-archvariant v1.0.0 github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 diff --git a/go.sum b/go.sum index 9d63e66a7..cade79965 100644 --- a/go.sum +++ b/go.sum @@ -507,8 +507,8 @@ github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4= github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY= -github.com/tonistiigi/fsutil v0.0.0-20261027232937-ca65610c2295 h1:ZsuzD6v7DvLAjJ4E1I9nuJ04NnOtE72PM0lOO/ApG/Q= -github.com/tonistiigi/fsutil v0.0.0-20261027232937-ca65610c2295/go.mod h1:oO0lfhK8QAY3alnpiYXcRVjyVx95XGWZE3K2M+AqfcM= +github.com/tonistiigi/fsutil v0.0.0-20261103125000-e075fd9305eb h1:/703X47pvph093rG6sD1wzLd2d4YdWgiPLRjnkV5qYY= +github.com/tonistiigi/fsutil v0.0.0-20261103125000-e075fd9305eb/go.mod h1:oO0lfhK8QAY3alnpiYXcRVjyVx95XGWZE3K2M+AqfcM= github.com/tonistiigi/go-actions-cache v0.0.0-20260930145529-afe8013b5ac4 h1:RkHGDzvazJu1TzNvesQpnN5RwNxvU3e7O2UdJqjaj2Q= github.com/tonistiigi/go-actions-cache v0.0.0-20260930145529-afe8013b5ac4/go.mod h1:PhGlvWoWwfUaY1K5xm8HHjcinBw/rD9FLQTPTxHbxyw= github.com/tonistiigi/go-archvariant v1.0.0 h1:5LC1eDWiBNflnTF1prCiX09yfNHIxDC/aukdhCdTyb0= diff --git a/source/git/source.go b/source/git/source.go index 81594fa61..f798b6fba 100644 --- a/source/git/source.go +++ b/source/git/source.go @@ -600,9 +600,10 @@ func (gs *gitSourceHandler) resolveMetadata(ctx context.Context, jobCtx solver.J } saveResolved := true defer func() { - v := md - if retErr != nil || !saveResolved { - v = nil + // untyped nil, a nil *Metadata would be stored as a value + var v any + if retErr == nil && saveResolved { + v = md } if err := release(v); err != nil { bklog.G(ctx).Warnf("failed to release resolver cache lock for %s: %v", gs.remoteKey(), err) diff --git a/vendor/github.com/tonistiigi/fsutil/diskwriter_unix.go b/vendor/github.com/tonistiigi/fsutil/diskwriter_unix.go index 18d736fa7..9360d1615 100644 --- a/vendor/github.com/tonistiigi/fsutil/diskwriter_unix.go +++ b/vendor/github.com/tonistiigi/fsutil/diskwriter_unix.go @@ -13,7 +13,7 @@ import ( func rewriteMetadata(p string, stat *types.Stat) error { for key, value := range stat.Xattrs { - sysx.Setxattr(p, key, value, 0) + sysx.LSetxattr(p, key, value, 0) } if err := os.Lchown(p, int(stat.Uid), int(stat.Gid)); err != nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 43f521bdf..d8b9a3aaa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1040,7 +1040,7 @@ github.com/theupdateframework/go-tuf/v2/metadata/updater # github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 ## explicit; go 1.21 github.com/tonistiigi/dchapes-mode -# github.com/tonistiigi/fsutil v0.0.0-20261027232937-ca65610c2295 +# github.com/tonistiigi/fsutil v0.0.0-20261103125000-e075fd9305eb ## explicit; go 1.25.0 github.com/tonistiigi/fsutil github.com/tonistiigi/fsutil/copy