build: fix oci-layout named context serialization

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2026-04-23 12:26:02 +02:00
parent 2d58ee6f98
commit dcf533e756
4 changed files with 214 additions and 12 deletions
+10 -6
View File
@@ -917,29 +917,33 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
}
// handle OCI layout
if localPath, ok := strings.CutPrefix(v.Path, "oci-layout://"); ok {
ref, _, err := ocilayout.Parse("oci-layout://" + localPath)
if ref, ok, err := ocilayout.Parse(v.Path); ok {
if err != nil {
return nil, err
}
localPath, dig, tag := ref.Path, ref.Digest.String(), ref.Tag
if dig == "" {
dig, err = resolveDigest(localPath, tag)
localPath := ref.Path
if ref.Digest == "" {
dig, err := resolveDigest(localPath, ref.Tag)
if err != nil {
return nil, errors.Wrapf(err, "oci-layout reference %q could not be resolved", v.Path)
}
ref.Digest = digest.Digest(dig)
}
store, err := local.NewStore(localPath)
if err != nil {
return nil, errors.Wrapf(err, "invalid store at %s", localPath)
}
storeName := identity.NewID()
if target.OCIStores == nil {
target.OCIStores = map[string]content.Store{}
}
target.OCIStores[storeName] = store
target.FrontendAttrs["context:"+k] = "oci-layout://" + storeName + ":" + tag + "@" + dig
ref.Path = storeName
target.FrontendAttrs["context:"+k] = ref.String()
continue
}
+94
View File
@@ -1,10 +1,16 @@
package build
import (
"context"
"testing"
"github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/ocilayout"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/ociindex"
"github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -154,3 +160,91 @@ func TestProxyArgKeyExists(t *testing.T) {
})
}
}
func TestLoadInputsOCILayoutNamedContext(t *testing.T) {
layoutPath := t.TempDir()
idx := ociindex.NewStoreIndex(layoutPath)
manifestDigest := digest.FromString("manifest")
err := idx.Put(ocispecs.Descriptor{
MediaType: ocispecs.MediaTypeImageManifest,
Digest: manifestDigest,
Size: 1,
}, ociindex.Tag("latest"))
require.NoError(t, err)
tests := []struct {
name string
ref string
wantRef ocilayout.Ref
}{
{
name: "digest only",
ref: "oci-layout://" + layoutPath + "@" + manifestDigest.String(),
wantRef: ocilayout.Ref{
Digest: manifestDigest,
},
},
{
name: "tag only",
ref: "oci-layout://" + layoutPath + ":latest",
wantRef: ocilayout.Ref{
Tag: "latest",
Digest: manifestDigest,
},
},
{
name: "tag and digest",
ref: "oci-layout://" + layoutPath + ":latest@" + manifestDigest.String(),
wantRef: ocilayout.Ref{
Tag: "latest",
Digest: manifestDigest,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
target := &client.SolveOpt{
FrontendAttrs: map[string]string{},
}
inp := &Inputs{
ContextPath: "https://example.com/context.tar.gz",
NamedContexts: map[string]NamedContext{
"proxy": {
Path: tt.ref,
},
},
}
release, err := loadInputs(context.Background(), nil, inp, testProgressWriter{}, target)
require.NoError(t, err)
require.NotNil(t, release)
t.Cleanup(release)
attr, ok := target.FrontendAttrs["context:proxy"]
require.True(t, ok)
require.Len(t, target.OCIStores, 1)
parsed, ok, err := ocilayout.Parse(attr)
require.True(t, ok)
require.NoError(t, err)
require.NotEmpty(t, parsed.Path)
assert.Equal(t, tt.wantRef.Tag, parsed.Tag)
assert.Equal(t, tt.wantRef.Digest, parsed.Digest)
target.OCIStores = nil
})
}
}
type testProgressWriter struct{}
func (testProgressWriter) Write(*client.SolveStatus) {}
func (testProgressWriter) WriteBuildRef(string, string) {}
func (testProgressWriter) ValidateLogSource(digest.Digest, any) bool { return true }
func (testProgressWriter) ClearLogSource(any) {}
var _ progress.Writer = testProgressWriter{}