Contributing guidelines and issue reporting guide
Well-formed report checklist
Description of bug
Bug description
BuildKit intentionally gives affected vertices different runtime digests in proxy-network mode. recomputeDigests records the old-to-new digest mapping locally, but loadLLB does not preserve or expose that mapping to diagnostic and provenance consumers. Rewriting an exec digest also changes downstream digests because their input digests change.
This breaks three joins:
Expected: Proxy enforcement does not change diagnostic or provenance associations.
Actual: Frontend-digest lookups fail for affected runtime digests.
Reproduction: errors lose Dockerfile locations
This regression test exercises the production wrapError source-location lookup:
Source-location regression test
func TestProxyNetworkVertexErrorKeepsSourceLocation(t *testing.T) {
for _, tc := range []struct {
name string
proxy bool
}{
{name: "disabled", proxy: false},
{name: "enabled", proxy: true},
} {
t.Run(tc.name, func(t *testing.T) {
def := proxyNetworkTestDefinition(t)
definitionDigest := digest.FromBytes(def.Def[1])
def.Source = &pb.Source{
Infos: []*pb.SourceInfo{{Filename: "Dockerfile"}},
Locations: map[string]*pb.Locations{
definitionDigest.String(): {
Locations: []*pb.Location{{SourceIndex: 0}},
},
},
}
edge, err := loadWithProxyNetwork(t.Context(), def, nil, tc.proxy)
require.NoError(t, err)
runtimeDigest := edge.Vertex.Digest()
t.Logf("definition digest=%s runtime digest=%s", definitionDigest, runtimeDigest)
rp := &resultProxy{req: frontend.SolveRequest{Definition: def}}
wrapped := rp.wrapError(errdefs.WrapVertex(errors.New("boom"), runtimeDigest))
require.Len(t, errdefs.Sources(wrapped), 1)
})
}
}
go test ./solver/llbsolver -run '^TestProxyNetworkVertexErrorKeepsSourceLocation$' -count=1 -v
The disabled subtest passes with equal digests. The enabled subtest has different digests and no source location:
definition digest=sha256:26194252659a6c033bedd158ea0f77bb22094ee1d2105a392af272a994621f71
runtime digest=sha256:5ea906b68b9ea01cf0fdbac9fa58b4c32249eb55f595324c271721e2fb6cfc10
Error: "[]" should have 1 item(s), but has 0
FAIL
Reproduction: provenance build steps lose resource usage
This join-level regression test exercises resource-usage association in package provenance:
Resource-usage regression test
func TestProxyNetworkResourceUsageKeepsBuildStepAssociation(t *testing.T) {
for _, tc := range []struct {
name string
proxy bool
}{
{name: "disabled", proxy: false},
{name: "enabled", proxy: true},
} {
t.Run(tc.name, func(t *testing.T) {
def, execBytes := proxyNetworkBuildConfigDefinition(t)
definitionDigest := digest.FromBytes(execBytes)
runtimeDigest := definitionDigest
if tc.proxy {
salted := append([]byte(nil), execBytes...)
salted = append(salted, []byte("\x00buildkit.proxy-network.v0")...)
runtimeDigest = digest.FromBytes(salted)
}
t.Logf("definition digest=%s runtime digest=%s", definitionDigest, runtimeDigest)
want := &resourcestypes.Samples{Samples: []*resourcestypes.Sample{{}}}
capture := &Capture{Samples: map[digest.Digest]*resourcestypes.Samples{runtimeDigest: want}}
steps, _, err := toBuildSteps(def, capture, true)
require.NoError(t, err)
for _, step := range steps {
if step.Op.GetExec() != nil {
require.Same(t, want, step.ResourceUsage)
return
}
}
t.Fatal("exec build step not found")
})
}
}
func proxyNetworkBuildConfigDefinition(t *testing.T) (*pb.Definition, []byte) {
t.Helper()
marshal := func(op *pb.Op) (digest.Digest, []byte) {
dt, err := op.Marshal()
require.NoError(t, err)
return digest.FromBytes(dt), dt
}
sourceDigest, sourceBytes := marshal(&pb.Op{
Op: &pb.Op_Source{Source: &pb.SourceOp{Identifier: "local://context"}},
})
execDigest, execBytes := marshal(&pb.Op{
Inputs: []*pb.Input{{Digest: sourceDigest.String()}},
Op: &pb.Op_Exec{Exec: &pb.ExecOp{
Meta: &pb.Meta{Args: []string{"true"}},
Mounts: []*pb.Mount{{Input: 0, Dest: pb.RootMount}},
}},
})
_, rootBytes := marshal(&pb.Op{
Inputs: []*pb.Input{{Digest: execDigest.String()}},
})
return &pb.Definition{Def: [][]byte{sourceBytes, execBytes, rootBytes}}, execBytes
}
go test ./solver/llbsolver/provenance -run '^TestProxyNetworkResourceUsageKeepsBuildStepAssociation$' -count=1 -v
The disabled control passes with equal digests. Proxy-network mode produces the same digest difference shown above, and the exec build step has no resource usage:
Error: Not same:
expected: &types.Samples{...}
actual: (*types.Samples)(nil)
FAIL
Version information
- BuildKit
master
- commit bb80b2a
- Go version
go1.26.6 darwin/arm64
Contributing guidelines and issue reporting guide
Well-formed report checklist
Description of bug
Bug description
BuildKit intentionally gives affected vertices different runtime digests in proxy-network mode.
recomputeDigestsrecords the old-to-new digest mapping locally, butloadLLBdoes not preserve or expose that mapping to diagnostic and provenance consumers. Rewriting an exec digest also changes downstream digests because their input digests change.This breaks three joins:
wrapErrorlooks up a runtimeVertexError.Digestin the originalDefinition.Source.Locations, so vertex errors can lose Dockerfile/source locations.op.Digest(), but max-provenance build steps look them up with frontend definition digests.CacheExportResult.EdgeVertex. That value comes from the runtime vertex digest in the cache key and is emitted by the exporter. The definition-derived build-step lookup silently skips unmatched runtime digests.Expected: Proxy enforcement does not change diagnostic or provenance associations.
Actual: Frontend-digest lookups fail for affected runtime digests.
Reproduction: errors lose Dockerfile locations
This regression test exercises the production
wrapErrorsource-location lookup:Source-location regression test
The disabled subtest passes with equal digests. The enabled subtest has different digests and no source location:
Reproduction: provenance build steps lose resource usage
This join-level regression test exercises resource-usage association in
package provenance:Resource-usage regression test
The disabled control passes with equal digests. Proxy-network mode produces the same digest difference shown above, and the exec build step has no resource usage:
Version information
mastergo1.26.6darwin/arm64