solver: add compatibility-version support

Add solve-wide compatibility-version support for image and oci
exports, with historical goldens and release compatibility tests.

Backfill version 10 for v0.13-v0.14 git artifact behavior, keep
version 20 as current, and reject unsupported zstd on v10.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi committed 2026-04-10 09:24:11 -07:00
1 parent 082e8d8cf3
commit 5fde446dee
60 files changed
+2299 -57

No files matched your search

@@ -0,0 +1,94 @@
name: compatibility-releases
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
on:
workflow_dispatch:
push:
branches:
- 'master'
- 'v[0-9]+.[0-9]+'
env:
GO_VERSION: "1.26"
jobs:
compatibility:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- release: v0.13.0
expected-compatibility-version: "10"
- release: v0.14.0
expected-compatibility-version: "10"
- release: v0.15.0
expected-compatibility-version: "20"
- release: v0.16.0
expected-compatibility-version: "20"
- release: v0.17.0
expected-compatibility-version: "20"
- release: v0.18.0
expected-compatibility-version: "20"
- release: v0.19.0
expected-compatibility-version: "20"
- release: v0.20.0
expected-compatibility-version: "20"
- release: v0.21.0
expected-compatibility-version: "20"
- release: v0.22.0
expected-compatibility-version: "20"
- release: v0.23.0
expected-compatibility-version: "20"
- release: v0.24.0
expected-compatibility-version: "20"
- release: v0.25.0
expected-compatibility-version: "20"
- release: v0.26.0
expected-compatibility-version: "20"
- release: v0.27.0
expected-compatibility-version: "20"
- release: v0.28.0
expected-compatibility-version: "20"
- release: v0.29.0
expected-compatibility-version: "20"
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
with:
platforms: arm64
-
name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: "${{ env.GO_VERSION }}"
cache: true
-
name: Prepare release binaries
run: |
mkdir -p .tmp/compat-bin
docker pull "moby/buildkit:${{ matrix.release }}"
cid=$(docker create "moby/buildkit:${{ matrix.release }}")
trap 'docker rm -f "$cid" >/dev/null 2>&1 || true' EXIT
docker cp "$cid:/usr/bin/buildkitd" .tmp/compat-bin/buildkitd
docker cp "$cid:/usr/bin/buildctl" .tmp/compat-bin/buildctl
chmod +x .tmp/compat-bin/buildkitd .tmp/compat-bin/buildctl
docker rm -f "$cid"
trap - EXIT
echo "$PWD/.tmp/compat-bin" >> "$GITHUB_PATH"
-
name: Test compatibility integration
env:
BUILDKIT_TEST_EXPECTED_COMPATIBILITY_VERSION: ${{ matrix.expected-compatibility-version }}
run: |
go test ./client -run '^TestCompatibilityIntegration/Test(Image|OCI)ExporterCompatibilityVersion$' -count=1 -v
+11 -2
View File
@@ -409,6 +409,7 @@ type SolveRequest struct {
Exporters []*Exporter `protobuf:"bytes,13,rep,name=Exporters,proto3" json:"Exporters,omitempty"`
EnableSessionExporter bool `protobuf:"varint,14,opt,name=EnableSessionExporter,proto3" json:"EnableSessionExporter,omitempty"`
SourcePolicySession string `protobuf:"bytes,15,opt,name=SourcePolicySession,proto3" json:"SourcePolicySession,omitempty"`
CompatibilityVersion int64 `protobuf:"varint,16,opt,name=CompatibilityVersion,proto3" json:"CompatibilityVersion,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -548,6 +549,13 @@ func (x *SolveRequest) GetSourcePolicySession() string {
return ""
}
func (x *SolveRequest) GetCompatibilityVersion() int64 {
if x != nil {
return x.CompatibilityVersion
}
return 0
}
type CacheOptions struct {
state protoimpl.MessageState `protogen:"open.v1"`
// ExportRefDeprecated is deprecated in favor or the new Exports since BuildKit v0.4.0.
@@ -2058,7 +2066,7 @@ const file_github_com_moby_buildkit_api_services_control_control_proto_rawDesc =
" \x01(\tR\n" +
"RecordType\x12\x16\n" +
"\x06Shared\x18\v \x01(\bR\x06Shared\x12\x18\n" +
"\aParents\x18\f \x03(\tR\aParents\"\xa6\b\n" +
"\aParents\x18\f \x03(\tR\aParents\"\xda\b\n" +
"\fSolveRequest\x12\x10\n" +
"\x03Ref\x18\x01 \x01(\tR\x03Ref\x12.\n" +
"\n" +
@@ -2077,7 +2085,8 @@ const file_github_com_moby_buildkit_api_services_control_control_proto_rawDesc =
"\fSourcePolicy\x18\f \x01(\v2%.moby.buildkit.v1.sourcepolicy.PolicyR\fSourcePolicy\x128\n" +
"\tExporters\x18\r \x03(\v2\x1a.moby.buildkit.v1.ExporterR\tExporters\x124\n" +
"\x15EnableSessionExporter\x18\x0e \x01(\bR\x15EnableSessionExporter\x120\n" +
"\x13SourcePolicySession\x18\x0f \x01(\tR\x13SourcePolicySession\x1aJ\n" +
"\x13SourcePolicySession\x18\x0f \x01(\tR\x13SourcePolicySession\x122\n" +
"\x14CompatibilityVersion\x18\x10 \x01(\x03R\x14CompatibilityVersion\x1aJ\n" +
"\x1cExporterAttrsDeprecatedEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a@\n" +
+1
View File
@@ -77,6 +77,7 @@ message SolveRequest {
repeated Exporter Exporters = 13;
bool EnableSessionExporter = 14;
string SourcePolicySession = 15;
int64 CompatibilityVersion = 16;
}
message CacheOptions {
@@ -143,6 +143,7 @@ func (m *SolveRequest) CloneVT() *SolveRequest {
r.SourcePolicy = m.SourcePolicy.CloneVT()
r.EnableSessionExporter = m.EnableSessionExporter
r.SourcePolicySession = m.SourcePolicySession
r.CompatibilityVersion = m.CompatibilityVersion
if rhs := m.ExporterAttrsDeprecated; rhs != nil {
tmpContainer := make(map[string]string, len(rhs))
for k, v := range rhs {
@@ -1043,6 +1044,9 @@ func (this *SolveRequest) EqualVT(that *SolveRequest) bool {
if this.SourcePolicySession != that.SourcePolicySession {
return false
}
if this.CompatibilityVersion != that.CompatibilityVersion {
return false
}
return string(this.unknownFields) == string(that.unknownFields)
}
@@ -2249,6 +2253,13 @@ func (m *SolveRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.CompatibilityVersion != 0 {
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CompatibilityVersion))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x80
}
if len(m.SourcePolicySession) > 0 {
i -= len(m.SourcePolicySession)
copy(dAtA[i:], m.SourcePolicySession)
@@ -4174,6 +4185,9 @@ func (m *SolveRequest) SizeVT() (n int) {
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
if m.CompatibilityVersion != 0 {
n += 2 + protohelpers.SizeOfVarint(uint64(m.CompatibilityVersion))
}
n += len(m.unknownFields)
return n
}
@@ -6326,6 +6340,25 @@ func (m *SolveRequest) UnmarshalVT(dAtA []byte) error {
}
m.SourcePolicySession = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 16:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CompatibilityVersion", wireType)
}
m.CompatibilityVersion = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.CompatibilityVersion |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
+2
View File
@@ -211,6 +211,8 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
testExportAnnotationsMediaTypes,
testExportAttestationsOCIArtifact,
testExportAttestationsImageManifest,
testImageExporterCompatibilityVersion,
testOCIExporterCompatibilityVersion,
testExportedImageLabels,
testAttestationDefaultSubject,
testSourceDateEpochLayerTimestamps,
+879
View File
@@ -0,0 +1,879 @@
package client
import (
"context"
"embed"
"encoding/json"
stderrors "errors"
"fmt"
"maps"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"testing"
"time"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/platforms"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/llb/sourceresolver"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
gateway "github.com/moby/buildkit/frontend/gateway/client"
solvererrdefs "github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/llbsolver/compat"
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
"github.com/moby/buildkit/util/contentutil"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/moby/buildkit/util/testutil/httpserver"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/moby/buildkit/util/testutil/workers"
policyimage "github.com/moby/policy-helpers/image"
digest "github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
)
const compatibilityUpdateEnv = "BUILDKIT_UPDATE_COMPAT_GOLDENS"
const compatibilityExpectedVersionEnv = "BUILDKIT_TEST_EXPECTED_COMPATIBILITY_VERSION"
const compatibilityEpoch = "1445412480"
const compatibilityPlatformString = "linux/arm64"
const compatibilityBusyboxMirrorRef = "busybox_arm64:latest"
const compatibilityBusyboxImageRef = "docker.io/library/" + compatibilityBusyboxMirrorRef
//go:embed testdata/compatibility
var compatibilityGoldens embed.FS
type compatibilityLayerExpectation struct {
MediaType string
Digest string
}
type compatibilityCase struct {
Name string
Attrs map[string]string
TouchExecOutputs bool
}
type compatibilityActual struct {
ManifestDigest string
ConfigDigest string
Layers []compatibilityLayerExpectation
ManifestBytes []byte
ConfigBytes []byte
ManifestJSON string
ConfigJSON string
}
var compatibilityCases = []compatibilityCase{
{
Name: "default-gzip",
Attrs: map[string]string{
"source-date-epoch": compatibilityEpoch,
"rewrite-timestamp": "true",
},
},
{
Name: "gzip-level-0",
Attrs: map[string]string{
"compression": "gzip",
"compression-level": "0",
"force-compression": "true",
"source-date-epoch": compatibilityEpoch,
"rewrite-timestamp": "true",
},
},
{
Name: "gzip-level-9",
Attrs: map[string]string{
"compression": "gzip",
"compression-level": "9",
"force-compression": "true",
"source-date-epoch": compatibilityEpoch,
"rewrite-timestamp": "true",
},
},
{
Name: "uncompressed",
Attrs: map[string]string{
"compression": "uncompressed",
"force-compression": "true",
"source-date-epoch": compatibilityEpoch,
"rewrite-timestamp": "true",
},
},
{
Name: "zstd-oci-types",
Attrs: map[string]string{
"compression": "zstd",
"compression-level": "12",
"force-compression": "true",
"oci-mediatypes": "true",
"source-date-epoch": compatibilityEpoch,
"rewrite-timestamp": "true",
},
},
{
Name: "manifest-annotation",
Attrs: map[string]string{
"annotation-manifest.org.opencontainers.image.title": "compatibility-test",
"source-date-epoch": compatibilityEpoch,
"rewrite-timestamp": "true",
},
},
{
Name: "oci-mediatypes",
Attrs: map[string]string{
"oci-mediatypes": "true",
"force-compression": "true",
"source-date-epoch": compatibilityEpoch,
"rewrite-timestamp": "true",
},
},
}
func TestCompatibilityIntegration(t *testing.T) {
integration.SkipOnPlatform(t, "windows")
mirrors := integration.WithMirroredImages(map[string]string{
"library/" + compatibilityBusyboxMirrorRef: "docker.io/arm64v8/busybox:latest@sha256:1fa89c01cd0473cedbd1a470abb8c139eeb80920edf1bc55de87851bfb63ea11",
})
integration.Run(t, integration.TestFuncs(
testImageExporterCompatibilityVersion,
testOCIExporterCompatibilityVersion,
testImageExporterCompatibilityVersionProvenance,
), mirrors)
}
func testImageExporterCompatibilityVersion(t *testing.T, sb integration.Sandbox) {
workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush, workers.FeatureProvenance, workers.FeatureSourceDateEpoch)
requiresLinux(t)
ctx := sb.Context()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
platform := compatibilityPlatform(t)
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
baseRef, baseConfig := createCompatibilityBaseImage(ctx, t, c, registry, platform)
expectedSet := compatibilityExpectedVersionIsSet()
for _, version := range compatibilityVersionsUnderTest(t) {
ensurePruneAll(t, c, sb)
for _, tc := range compatibilityCases {
t.Run(fmt.Sprintf("image/%s/%s", tc.Name, compatibilityRunName(version, expectedSet)), func(t *testing.T) {
def := createCompatibilityDefinition(t, sb, baseRef, platform, tc)
actual, err := exportCompatibilityImageCase(ctx, t, c, def, registry, tc, version, expectedSet, baseConfig)
assertCompatibilityCaseResult(t, ExporterImage, tc, version, expectedSet, actual, err)
})
}
}
}
func testOCIExporterCompatibilityVersion(t *testing.T, sb integration.Sandbox) {
workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter, workers.FeatureProvenance, workers.FeatureSourceDateEpoch)
requiresLinux(t)
ctx := sb.Context()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
platform := compatibilityPlatform(t)
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
baseRef, baseConfig := createCompatibilityBaseImage(ctx, t, c, registry, platform)
expectedSet := compatibilityExpectedVersionIsSet()
for _, version := range compatibilityVersionsUnderTest(t) {
ensurePruneAll(t, c, sb)
for _, tc := range compatibilityCases {
t.Run(fmt.Sprintf("oci/%s/%s", tc.Name, compatibilityRunName(version, expectedSet)), func(t *testing.T) {
def := createCompatibilityDefinition(t, sb, baseRef, platform, tc)
actual, err := exportCompatibilityOCICase(ctx, t, c, def, tc, version, expectedSet, baseConfig)
assertCompatibilityCaseResult(t, ExporterOCI, tc, version, expectedSet, actual, err)
})
}
}
}
func testImageExporterCompatibilityVersionProvenance(t *testing.T, sb integration.Sandbox) {
workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush, workers.FeatureProvenance, workers.FeatureSourceDateEpoch)
requiresLinux(t)
if compatibilityExpectedVersionIsSet() {
t.Skip("provenance compatibility assertion requires explicit compatibility-version request")
}
ctx := sb.Context()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
platform := compatibilityPlatform(t)
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
baseRef, baseConfig := createCompatibilityBaseImage(ctx, t, c, registry, platform)
ensurePruneAll(t, c, sb)
var tc compatibilityCase
for _, candidate := range compatibilityCases {
if candidate.Name == "default-gzip" {
tc = candidate
break
}
}
require.NotEmpty(t, tc.Name)
def := createCompatibilityDefinition(t, sb, baseRef, platform, tc)
target := fmt.Sprintf("%s/buildkit/compatibility-provenance-v10:latest", registry)
attrs := cloneStringMap(tc.Attrs)
attrs["name"] = target
attrs["push"] = "true"
attrs[exptypes.ExporterImageConfigKey] = string(baseConfig)
_, err = c.Solve(ctx, def, SolveOpt{
CompatibilityVersion: compat.CompatibilityVersion013,
FrontendAttrs: map[string]string{
"attest:provenance": "mode=max,version=v1",
},
Exports: []ExportEntry{{
Type: ExporterImage,
Attrs: attrs,
}},
}, nil)
require.NoError(t, err)
pr, err := readImageCompatibilityProvenance(ctx, target)
require.NoError(t, err)
require.Equal(t, compat.CompatibilityVersion013, pr.BuildDefinition.ExternalParameters.Request.CompatibilityVersion)
}
func compatibilityVersionsUnderTest(t *testing.T) []int {
t.Helper()
versionStr := os.Getenv(compatibilityExpectedVersionEnv)
if versionStr == "" {
return compat.SupportedCompatibilityVersions()
}
version, err := strconv.Atoi(versionStr)
require.NoError(t, err)
require.NoError(t, compat.ValidateCompatibilityVersion(version))
return []int{version}
}
func compatibilityExpectedVersionIsSet() bool {
return os.Getenv(compatibilityExpectedVersionEnv) != ""
}
func compatibilityRunName(version int, expectedSet bool) string {
if expectedSet {
return "default"
}
return "v" + strconv.Itoa(version)
}
func createCompatibilityBaseImage(ctx context.Context, t *testing.T, c *Client, registry string, platform ocispecs.Platform) (string, []byte) {
t.Helper()
baseRef := fmt.Sprintf("%s/buildkit/compatibility-base:latest", registry)
_, err := c.Build(ctx, SolveOpt{
Exports: []ExportEntry{{
Type: ExporterImage,
Attrs: map[string]string{
"name": baseRef,
"push": "true",
"store": "false",
},
}},
}, "", func(ctx context.Context, gw gateway.Client) (*gateway.Result, error) {
baseConfig, err := resolveCompatibilityImageConfig(ctx, gw, platform)
if err != nil {
return nil, err
}
base, err := llb.Image(compatibilityBusyboxImageRef, llb.Platform(platform)).WithImageConfig(baseConfig)
if err != nil {
return nil, err
}
base = base.File(
llb.Mkdir("/base", 0o755, llb.WithParents(true)).
Mkfile("/base/base.txt", 0o644, []byte("compatibility base\n")),
)
def, err := base.Marshal(ctx)
if err != nil {
return nil, err
}
r, err := gw.Solve(ctx, gateway.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, err
}
ref, err := r.SingleRef()
if err != nil {
return nil, err
}
res := gateway.NewResult()
res.SetRef(ref)
res.AddMeta(exptypes.ExporterImageConfigKey, baseConfig)
return res, nil
}, nil)
require.NoError(t, err)
desc, provider, err := contentutil.ProviderFromRef(baseRef)
require.NoError(t, err)
actual := readCompatibilityActualFromProvider(ctx, t, provider, desc)
return baseRef, actual.ConfigBytes
}
func resolveCompatibilityImageConfig(ctx context.Context, gw gateway.Client, platform ocispecs.Platform) ([]byte, error) {
_, _, baseConfig, err := gw.ResolveImageConfig(ctx, compatibilityBusyboxImageRef, sourceresolver.Opt{
ImageOpt: &sourceresolver.ResolveImageOpt{
Platform: &platform,
},
})
return baseConfig, err
}
func createCompatibilityDefinition(t *testing.T, sb integration.Sandbox, baseRef string, platform ocispecs.Platform, tc compatibilityCase) *llb.Definition {
t.Helper()
httpModTime := time.Date(2021, time.January, 2, 3, 4, 5, 0, time.UTC)
httpSrv := httpserver.NewTestServer(map[string]*httpserver.Response{
"/artifact.txt": {
Content: []byte("http fixture\n"),
LastModified: &httpModTime,
},
})
t.Cleanup(httpSrv.Close)
gitDir := t.TempDir()
err := runInDirEnv(gitDir, []string{
"GIT_AUTHOR_DATE=2020-01-02T03:04:05Z",
"GIT_COMMITTER_DATE=2020-01-02T03:04:05Z",
}, []string{
"git init",
"git config --local user.email test@example.com",
"git config --local user.name test",
"git checkout -b main",
"mkdir -p sub",
"printf 'git fixture\\n' > git.txt",
"printf 'nested fixture\\n' > sub/nested.txt",
"git add .",
"git commit -m fixture",
"git update-server-info",
}...)
require.NoError(t, err)
gitSrv := httptest.NewServer(http.FileServer(http.Dir(filepath.Clean(gitDir))))
t.Cleanup(gitSrv.Close)
httpState := llb.HTTP(httpSrv.URL+"/artifact.txt", llb.Filename("artifact.txt"))
gitState := llb.Git(gitSrv.URL+"/.git", "", llb.GitRef("main"))
st := llb.Image(baseRef, llb.Platform(platform)).File(
llb.Mkdir("/inputs/git", 0o755, llb.WithParents(true)).
Mkfile("/inputs/llb.txt", 0o644, []byte("llb fixture\n")).
Copy(httpState, "/artifact.txt", "/inputs/http.txt").
Copy(gitState, "/git.txt", "/inputs/git/git.txt").
Copy(gitState, "/sub/nested.txt", "/inputs/git/nested.txt"),
)
execScript := `sh -eux -c '
mkdir -p /foo/bar/generated/deeper
echo exec-fixture > /foo/bar/generated/exec.txt
echo nested-exec > /foo/bar/generated/deeper/nested.txt
`
if tc.TouchExecOutputs {
execScript += `touch -t 201903040506.07 /foo/bar/generated /foo/bar/generated/deeper
touch -t 201903040506.07 /foo/bar/generated/exec.txt /foo/bar/generated/deeper/nested.txt
`
}
execScript += `'`
run := st.Run(
llb.Shlex(execScript),
llb.AddMount("/foo/bar", llb.Scratch()),
)
def, err := run.Root().Marshal(sb.Context())
require.NoError(t, err)
return def
}
func exportCompatibilityImageCase(ctx context.Context, t *testing.T, c *Client, def *llb.Definition, registry string, tc compatibilityCase, version int, expectedSet bool, baseConfig []byte) (compatibilityActual, error) {
t.Helper()
target := fmt.Sprintf("%s/buildkit/compatibility-%s:%s", registry, sanitizeCompatibilityName(tc.Name), compatibilityRunName(version, expectedSet))
attrs := cloneStringMap(tc.Attrs)
attrs["name"] = target
attrs["push"] = "true"
attrs[exptypes.ExporterImageConfigKey] = string(baseConfig)
opt := SolveOpt{
FrontendAttrs: map[string]string{
"attest:provenance": "mode=max,version=v1",
},
Exports: []ExportEntry{{
Type: ExporterImage,
Attrs: attrs,
}},
}
if !expectedSet {
opt.CompatibilityVersion = version
}
_, err := c.Solve(ctx, def, opt, nil)
if err != nil {
return compatibilityActual{}, err
}
desc, provider, err := contentutil.ProviderFromRef(target)
if err != nil {
return compatibilityActual{}, err
}
return readCompatibilityActualFromProvider(ctx, t, provider, desc), nil
}
func exportCompatibilityOCICase(ctx context.Context, t *testing.T, c *Client, def *llb.Definition, tc compatibilityCase, version int, expectedSet bool, baseConfig []byte) (compatibilityActual, error) {
t.Helper()
dir := t.TempDir()
attrs := cloneStringMap(tc.Attrs)
attrs["tar"] = "false"
attrs[exptypes.ExporterImageConfigKey] = string(baseConfig)
opt := SolveOpt{
FrontendAttrs: map[string]string{
"attest:provenance": "mode=max,version=v1",
},
Exports: []ExportEntry{{
Type: ExporterOCI,
OutputDir: dir,
Attrs: attrs,
}},
}
if !expectedSet {
opt.CompatibilityVersion = version
}
_, err := c.Solve(ctx, def, opt, nil)
if err != nil {
return compatibilityActual{}, err
}
indexDT, err := os.ReadFile(filepath.Join(dir, ocispecs.ImageIndexFile))
require.NoError(t, err)
var index ocispecs.Index
require.NoError(t, json.Unmarshal(indexDT, &index))
for _, desc := range index.Manifests {
if desc.Digest == "" {
continue
}
if desc.Platform != nil && desc.Platform.OS == "unknown" && desc.Platform.Architecture == "unknown" {
continue
}
manifestDT, err := os.ReadFile(filepath.Join(dir, ocispecs.ImageBlobsDir, desc.Digest.Algorithm().String(), desc.Digest.Encoded()))
require.NoError(t, err)
var manifest ocispecs.Manifest
require.NoError(t, json.Unmarshal(manifestDT, &manifest))
if manifest.Config.Digest == "" {
continue
}
if isCompatibilityAttestationManifest(manifest) {
continue
}
configDT, err := os.ReadFile(filepath.Join(dir, ocispecs.ImageBlobsDir, manifest.Config.Digest.Algorithm().String(), manifest.Config.Digest.Encoded()))
require.NoError(t, err)
return compatibilityActualFromBytes(manifestDT, configDT, manifest.Layers), nil
}
blobPaths, err := filepath.Glob(filepath.Join(dir, ocispecs.ImageBlobsDir, "*", "*"))
require.NoError(t, err)
sort.Strings(blobPaths)
for _, blobPath := range blobPaths {
manifestDT, err := os.ReadFile(blobPath)
require.NoError(t, err)
var manifest ocispecs.Manifest
if err := json.Unmarshal(manifestDT, &manifest); err != nil {
continue
}
if manifest.Config.Digest == "" {
continue
}
if isCompatibilityAttestationManifest(manifest) {
continue
}
configDT, err := os.ReadFile(filepath.Join(dir, ocispecs.ImageBlobsDir, manifest.Config.Digest.Algorithm().String(), manifest.Config.Digest.Encoded()))
require.NoError(t, err)
return compatibilityActualFromBytes(manifestDT, configDT, manifest.Layers), nil
}
t.Fatalf("missing platform manifest\nindex json:\n%s", normalizeJSON(indexDT))
return compatibilityActual{}, nil
}
func compatibilityActualFromBytes(manifestDT, configDT []byte, layers []ocispecs.Descriptor) compatibilityActual {
out := compatibilityActual{
ManifestDigest: digest.FromBytes(manifestDT).String(),
ConfigDigest: digest.FromBytes(configDT).String(),
ManifestBytes: append([]byte(nil), manifestDT...),
ConfigBytes: append([]byte(nil), configDT...),
ManifestJSON: normalizeJSON(manifestDT),
ConfigJSON: normalizeJSON(configDT),
Layers: make([]compatibilityLayerExpectation, len(layers)),
}
for i, layer := range layers {
out.Layers[i] = compatibilityLayerExpectation{
MediaType: layer.MediaType,
Digest: layer.Digest.String(),
}
}
return out
}
func isCompatibilityAttestationManifest(manifest ocispecs.Manifest) bool {
if len(manifest.Layers) == 0 {
return false
}
for _, layer := range manifest.Layers {
if layer.MediaType != "application/vnd.in-toto+json" {
return false
}
}
return true
}
func readCompatibilityActualFromProvider(ctx context.Context, t *testing.T, provider content.Provider, desc ocispecs.Descriptor) compatibilityActual {
t.Helper()
if images.IsIndexType(desc.MediaType) {
indexDT, err := content.ReadBlob(ctx, provider, desc)
require.NoError(t, err)
var index ocispecs.Index
require.NoError(t, json.Unmarshal(indexDT, &index))
platform := compatibilityPlatform(t)
matcher := platforms.Only(platform)
for _, manifestDesc := range index.Manifests {
if manifestDesc.Platform == nil || !matcher.Match(*manifestDesc.Platform) {
continue
}
return readCompatibilityActualFromProvider(ctx, t, provider, manifestDesc)
}
require.FailNow(t, "missing platform manifest in image index")
}
manifestDT, err := content.ReadBlob(ctx, provider, desc)
require.NoError(t, err)
var manifest ocispecs.Manifest
require.NoError(t, json.Unmarshal(manifestDT, &manifest))
require.True(t, images.IsManifestType(manifest.MediaType), "unexpected manifest media type %s", manifest.MediaType)
configDT, err := content.ReadBlob(ctx, provider, manifest.Config)
require.NoError(t, err)
return compatibilityActualFromBytes(manifestDT, configDT, manifest.Layers)
}
func readImageCompatibilityProvenance(ctx context.Context, ref string) (*provenancetypes.ProvenancePredicateSLSA1, error) {
desc, provider, err := contentutil.ProviderFromRef(ref)
if err != nil {
return nil, err
}
indexDT, err := content.ReadBlob(ctx, provider, desc)
if err != nil {
return nil, err
}
var index ocispecs.Index
if err := json.Unmarshal(indexDT, &index); err != nil {
return nil, err
}
for _, manifestDesc := range index.Manifests {
manifestDT, err := content.ReadBlob(ctx, provider, manifestDesc)
if err != nil {
return nil, err
}
var manifest ocispecs.Manifest
if err := json.Unmarshal(manifestDT, &manifest); err != nil {
return nil, err
}
if !isCompatibilityAttestationManifest(manifest) {
continue
}
for _, layer := range manifest.Layers {
if layer.Annotations["in-toto.io/predicate-type"] != policyimage.SLSAProvenancePredicateType1 {
continue
}
stmtDT, err := content.ReadBlob(ctx, provider, layer)
if err != nil {
return nil, err
}
var stmt struct {
intoto.StatementHeader
Predicate json.RawMessage `json:"predicate"`
}
if err := json.Unmarshal(stmtDT, &stmt); err != nil {
return nil, err
}
var pr provenancetypes.ProvenancePredicateSLSA1
if err := json.Unmarshal(stmt.Predicate, &pr); err != nil {
return nil, err
}
return &pr, nil
}
}
return nil, errors.New("missing SLSA provenance attestation")
}
func assertCompatibilityCase(t *testing.T, exporterType string, tc compatibilityCase, version int, actual compatibilityActual) {
t.Helper()
if os.Getenv(compatibilityUpdateEnv) != "" {
writeCompatibilityGoldens(t, tc.Name, version, actual)
t.Logf("compatibility expectation %s/%s/v%d manifest=%q config=%q layers=%s",
exporterType, tc.Name, version, actual.ManifestDigest, actual.ConfigDigest, formatLayerExpectations(actual.Layers))
return
}
expectedManifestJSON, err := compatibilityGoldens.ReadFile(goldenPath(tc.Name, version, "manifest.json"))
require.NoError(t, err)
expectedConfigJSON, err := compatibilityGoldens.ReadFile(goldenPath(tc.Name, version, "config.json"))
require.NoError(t, err)
exp, err := compatibilityExpectationFromGoldens(expectedManifestJSON, expectedConfigJSON)
require.NoError(t, err)
manifestDiff := ""
if normalizeJSON(expectedManifestJSON) != actual.ManifestJSON {
manifestDiff = unifiedDiff("golden-manifest", normalizeJSON(expectedManifestJSON), actual.ManifestJSON)
}
configDiff := ""
if normalizeJSON(expectedConfigJSON) != actual.ConfigJSON {
configDiff = unifiedDiff("golden-config", normalizeJSON(expectedConfigJSON), actual.ConfigJSON)
}
if exp.ManifestDigest != actual.ManifestDigest ||
exp.ConfigDigest != actual.ConfigDigest ||
!equalLayerExpectations(exp.Layers, actual.Layers) ||
manifestDiff != "" ||
configDiff != "" {
t.Fatalf("%s", formatCompatibilityDebug(exporterType, tc.Name, version, tc.Attrs, exp, actual, manifestDiff, configDiff))
}
}
func assertCompatibilityCaseResult(t *testing.T, exporterType string, tc compatibilityCase, version int, expectedSet bool, actual compatibilityActual, err error) {
t.Helper()
if compatibilityCaseExpectedUnsupported(tc, version, expectedSet) {
require.Error(t, err)
err = grpcerrors.FromGRPC(err)
var unsupported *solvererrdefs.UnsupportedCompatibilityFeatureError
expectedFeature := fmt.Sprintf("%s exporter compression=zstd", exporterType)
if stderrors.As(err, &unsupported) {
require.Equal(t, int64(version), unsupported.Version)
require.Equal(t, expectedFeature, unsupported.Feature)
return
}
require.ErrorContains(t, err, fmt.Sprintf("unsupported compatibility-version %d feature %s", version, expectedFeature))
return
}
require.NoError(t, err)
assertCompatibilityCase(t, exporterType, tc, version, actual)
}
func compatibilityCaseExpectedUnsupported(tc compatibilityCase, version int, expectedSet bool) bool {
return !expectedSet && version == compat.CompatibilityVersion013 && tc.Attrs["compression"] == "zstd"
}
func compatibilityExpectationFromGoldens(manifestDT, configDT []byte) (compatibilityActual, error) {
var manifest ocispecs.Manifest
if err := json.Unmarshal(manifestDT, &manifest); err != nil {
return compatibilityActual{}, err
}
return compatibilityActualFromBytes(manifestDT, configDT, manifest.Layers), nil
}
func writeCompatibilityGoldens(t *testing.T, caseName string, version int, actual compatibilityActual) {
t.Helper()
_, currentFile, _, ok := runtime.Caller(0)
require.True(t, ok)
baseDir := filepath.Dir(currentFile)
manifestPath := filepath.Join(baseDir, "testdata", "compatibility", caseName, fmt.Sprintf("v%d", version), "manifest.json")
configPath := filepath.Join(baseDir, "testdata", "compatibility", caseName, fmt.Sprintf("v%d", version), "config.json")
require.NoError(t, os.MkdirAll(filepath.Dir(manifestPath), 0o755))
require.NoError(t, os.WriteFile(manifestPath, actual.ManifestBytes, 0o644))
require.NoError(t, os.WriteFile(configPath, actual.ConfigBytes, 0o644))
}
func goldenPath(caseName string, version int, file string) string {
return filepath.ToSlash(filepath.Join("testdata", "compatibility", caseName, fmt.Sprintf("v%d", version), file))
}
func formatCompatibilityDebug(exporterType, caseName string, version int, attrs map[string]string, exp compatibilityActual, actual compatibilityActual, manifestDiff, configDiff string) string {
var b strings.Builder
fmt.Fprintf(&b, "compatibility regression mismatch\n")
fmt.Fprintf(&b, "case: %s\n", caseName)
fmt.Fprintf(&b, "exporter: %s\n", exporterType)
fmt.Fprintf(&b, "compatibility-version: %d\n", version)
fmt.Fprintf(&b, "attrs:\n")
keys := make([]string, 0, len(attrs))
for k := range attrs {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Fprintf(&b, " %s=%s\n", k, attrs[k])
}
fmt.Fprintf(&b, "\nexpected:\n")
fmt.Fprintf(&b, " manifest=%s\n", exp.ManifestDigest)
fmt.Fprintf(&b, " config=%s\n", exp.ConfigDigest)
for i, layer := range exp.Layers {
fmt.Fprintf(&b, " layer[%d]=%s %s\n", i, layer.MediaType, layer.Digest)
}
fmt.Fprintf(&b, "\nactual:\n")
fmt.Fprintf(&b, " manifest=%s\n", actual.ManifestDigest)
fmt.Fprintf(&b, " config=%s\n", actual.ConfigDigest)
for i, layer := range actual.Layers {
fmt.Fprintf(&b, " layer[%d]=%s %s\n", i, layer.MediaType, layer.Digest)
}
if manifestDiff != "" {
fmt.Fprintf(&b, "\nmanifest diff:\n%s\n", manifestDiff)
}
if configDiff != "" {
fmt.Fprintf(&b, "\nconfig diff:\n%s\n", configDiff)
}
fmt.Fprintf(&b, "\nmanifest json:\n%s\n", actual.ManifestJSON)
fmt.Fprintf(&b, "\nconfig json:\n%s\n", actual.ConfigJSON)
return b.String()
}
func unifiedDiff(name, expected, actual string) string {
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(expected),
B: difflib.SplitLines(actual),
FromFile: name + ".golden",
ToFile: name + ".actual",
Context: 3,
})
if err != nil {
return fmt.Sprintf("failed to render diff: %v", err)
}
return diff
}
func normalizeJSON(dt []byte) string {
var v any
if err := json.Unmarshal(dt, &v); err != nil {
return string(dt)
}
out, err := json.MarshalIndent(v, "", " ")
if err != nil {
return string(dt)
}
return string(out)
}
func equalLayerExpectations(a, b []compatibilityLayerExpectation) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func formatLayerExpectations(layers []compatibilityLayerExpectation) string {
parts := make([]string, len(layers))
for i, layer := range layers {
parts[i] = fmt.Sprintf("{MediaType:%q,Digest:%q}", layer.MediaType, layer.Digest)
}
return "[" + strings.Join(parts, ", ") + "]"
}
func cloneStringMap(in map[string]string) map[string]string {
if len(in) == 0 {
return map[string]string{}
}
out := make(map[string]string, len(in))
maps.Copy(out, in)
return out
}
func sanitizeCompatibilityName(name string) string {
var b strings.Builder
b.Grow(len(name))
for _, r := range name {
switch {
case r >= 'a' && r <= 'z':
b.WriteRune(r)
case r >= 'A' && r <= 'Z':
b.WriteRune(r + ('a' - 'A'))
case r >= '0' && r <= '9':
b.WriteRune(r)
case r == '.', r == '-', r == '_':
b.WriteRune(r)
default:
b.WriteByte('-')
}
}
return b.String()
}
func compatibilityPlatform(t *testing.T) ocispecs.Platform {
t.Helper()
return platforms.Normalize(platforms.MustParse(compatibilityPlatformString))
}
+2
View File
@@ -36,6 +36,7 @@ import (
type SolveOpt struct {
Exports []ExportEntry
CompatibilityVersion int
EnableSessionExporter bool
LocalDirs map[string]string // Deprecated: use LocalMounts
LocalMounts map[string]fsutil.FS
@@ -311,6 +312,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
Cache: &cacheOpt.options,
Entitlements: slices.Clone(opt.AllowedEntitlements),
Internal: opt.Internal,
CompatibilityVersion: int64(opt.CompatibilityVersion),
SourcePolicy: opt.SourcePolicy,
}
if opt.SourcePolicyProvider != nil {
+5
View File
@@ -0,0 +1,5 @@
Compatibility regression goldens live under:
testdata/compatibility/<case>/v<compat-version>/{manifest,config}.json
Use BUILDKIT_UPDATE_COMPAT_GOLDENS=1 to regenerate them against the current daemon.
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:78260322e6ea253e5f3f675b3140ebe95ae8fc7e0ac2b1b3b88f1c8fc83525ce","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:3626b1d102dacca93b04a986b5c02d362209d18f05234437caa42e05e40db631",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:7a93c5cd268d789b9f3f4395657e8114c377144d0e8532e3082cb310538204e4",
"size": 1916567,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:bddce71fda3dfcd6e87ef6d19383da8d6387ccb6ef9b1469fedd54b1187f5f20",
"size": 151,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:9f55dc33cc1a8a650ec0b977d23e9ae2be65954f129f70fe44d766a52c61885b",
"size": 247,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:f83d05f639a6555fc9c7f62b19ef2a75561e6934f05505d341cb21b6632e129f",
"size": 108,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:9228085939d1444bd500ac8a4f8ee9d39fed1e5905da0968ffed61887ab41042","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:75c490263aada3f00b91cdd6f31d69098f2a27fb6c4a856dc9099a66cc84f96d",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:7a93c5cd268d789b9f3f4395657e8114c377144d0e8532e3082cb310538204e4",
"size": 1916567,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:bddce71fda3dfcd6e87ef6d19383da8d6387ccb6ef9b1469fedd54b1187f5f20",
"size": 151,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:3096f0079befb9bacfab7003f9de4604477e81ef1cd0d4bb9f967db5aba7bebc",
"size": 242,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:f83d05f639a6555fc9c7f62b19ef2a75561e6934f05505d341cb21b6632e129f",
"size": 108,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:78260322e6ea253e5f3f675b3140ebe95ae8fc7e0ac2b1b3b88f1c8fc83525ce","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:3626b1d102dacca93b04a986b5c02d362209d18f05234437caa42e05e40db631",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:9304bdcb5b86d518cf2128a070efb679fff088ce59d27072e9543f9836f78359",
"size": 4272993,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:e67548f577b3eb6e6726c769a9ec3a2a46ad53f009f15d6e2e8bb797ee9e1f3a",
"size": 2588,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:32b77f982cd1e1e83523fc65841d82dd0d925872e3dee6bd508ff81b157864c9",
"size": 6172,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:bf943002b85c24847b4e0b6329ea308c705cffba71423e99d878204e7d2c79fa",
"size": 2076,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:9228085939d1444bd500ac8a4f8ee9d39fed1e5905da0968ffed61887ab41042","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:75c490263aada3f00b91cdd6f31d69098f2a27fb6c4a856dc9099a66cc84f96d",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:9304bdcb5b86d518cf2128a070efb679fff088ce59d27072e9543f9836f78359",
"size": 4272993,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:e67548f577b3eb6e6726c769a9ec3a2a46ad53f009f15d6e2e8bb797ee9e1f3a",
"size": 2588,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:5c3240cc0bcdc46a4ff085359eba4d0ffe7e5e161c5f376e79f2264719041668",
"size": 6172,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:bf943002b85c24847b4e0b6329ea308c705cffba71423e99d878204e7d2c79fa",
"size": 2076,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:78260322e6ea253e5f3f675b3140ebe95ae8fc7e0ac2b1b3b88f1c8fc83525ce","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:3626b1d102dacca93b04a986b5c02d362209d18f05234437caa42e05e40db631",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:a130f54140e9b3f76dc2c2261eaa8500e5be39f942067e7a8ebdd3463795d1bc",
"size": 1913990,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:6c24a150ead6b26912038d3a007cdd5de329ab9a9239ed948efee6381c60af3e",
"size": 143,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:640c55a12b945452255a4cee6c2c084bc3b29f2f5b802eb0b42a4245f489ae23",
"size": 232,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:4b65c30ca18f35f78dac1590302407fd5ae94fb24e5b69231b1e991342a1070c",
"size": 107,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:9228085939d1444bd500ac8a4f8ee9d39fed1e5905da0968ffed61887ab41042","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:75c490263aada3f00b91cdd6f31d69098f2a27fb6c4a856dc9099a66cc84f96d",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:a130f54140e9b3f76dc2c2261eaa8500e5be39f942067e7a8ebdd3463795d1bc",
"size": 1913990,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:6c24a150ead6b26912038d3a007cdd5de329ab9a9239ed948efee6381c60af3e",
"size": 143,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:ba67dd0bb35371d02be3b950c6d617e1f22f55861fc5a7d0d354c8fbb2f77b56",
"size": 228,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:4b65c30ca18f35f78dac1590302407fd5ae94fb24e5b69231b1e991342a1070c",
"size": 107,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:78260322e6ea253e5f3f675b3140ebe95ae8fc7e0ac2b1b3b88f1c8fc83525ce","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,46 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:3626b1d102dacca93b04a986b5c02d362209d18f05234437caa42e05e40db631",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:7a93c5cd268d789b9f3f4395657e8114c377144d0e8532e3082cb310538204e4",
"size": 1916567,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:bddce71fda3dfcd6e87ef6d19383da8d6387ccb6ef9b1469fedd54b1187f5f20",
"size": 151,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:9f55dc33cc1a8a650ec0b977d23e9ae2be65954f129f70fe44d766a52c61885b",
"size": 247,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:f83d05f639a6555fc9c7f62b19ef2a75561e6934f05505d341cb21b6632e129f",
"size": 108,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
],
"annotations": {
"org.opencontainers.image.title": "compatibility-test"
}
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:9228085939d1444bd500ac8a4f8ee9d39fed1e5905da0968ffed61887ab41042","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,46 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:75c490263aada3f00b91cdd6f31d69098f2a27fb6c4a856dc9099a66cc84f96d",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:7a93c5cd268d789b9f3f4395657e8114c377144d0e8532e3082cb310538204e4",
"size": 1916567,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:bddce71fda3dfcd6e87ef6d19383da8d6387ccb6ef9b1469fedd54b1187f5f20",
"size": 151,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:3096f0079befb9bacfab7003f9de4604477e81ef1cd0d4bb9f967db5aba7bebc",
"size": 242,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:f83d05f639a6555fc9c7f62b19ef2a75561e6934f05505d341cb21b6632e129f",
"size": 108,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
],
"annotations": {
"org.opencontainers.image.title": "compatibility-test"
}
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:78260322e6ea253e5f3f675b3140ebe95ae8fc7e0ac2b1b3b88f1c8fc83525ce","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:3626b1d102dacca93b04a986b5c02d362209d18f05234437caa42e05e40db631",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:7a93c5cd268d789b9f3f4395657e8114c377144d0e8532e3082cb310538204e4",
"size": 1916567,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:bddce71fda3dfcd6e87ef6d19383da8d6387ccb6ef9b1469fedd54b1187f5f20",
"size": 151,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:9f55dc33cc1a8a650ec0b977d23e9ae2be65954f129f70fe44d766a52c61885b",
"size": 247,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:f83d05f639a6555fc9c7f62b19ef2a75561e6934f05505d341cb21b6632e129f",
"size": 108,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:9228085939d1444bd500ac8a4f8ee9d39fed1e5905da0968ffed61887ab41042","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:75c490263aada3f00b91cdd6f31d69098f2a27fb6c4a856dc9099a66cc84f96d",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:7a93c5cd268d789b9f3f4395657e8114c377144d0e8532e3082cb310538204e4",
"size": 1916567,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:bddce71fda3dfcd6e87ef6d19383da8d6387ccb6ef9b1469fedd54b1187f5f20",
"size": 151,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:3096f0079befb9bacfab7003f9de4604477e81ef1cd0d4bb9f967db5aba7bebc",
"size": 242,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:f83d05f639a6555fc9c7f62b19ef2a75561e6934f05505d341cb21b6632e129f",
"size": 108,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:78260322e6ea253e5f3f675b3140ebe95ae8fc7e0ac2b1b3b88f1c8fc83525ce","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:3626b1d102dacca93b04a986b5c02d362209d18f05234437caa42e05e40db631",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01",
"size": 4272640,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837",
"size": 2560,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:78260322e6ea253e5f3f675b3140ebe95ae8fc7e0ac2b1b3b88f1c8fc83525ce",
"size": 6144,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245",
"size": 2048,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:9228085939d1444bd500ac8a4f8ee9d39fed1e5905da0968ffed61887ab41042","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:75c490263aada3f00b91cdd6f31d69098f2a27fb6c4a856dc9099a66cc84f96d",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01",
"size": 4272640,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837",
"size": 2560,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:9228085939d1444bd500ac8a4f8ee9d39fed1e5905da0968ffed61887ab41042",
"size": 6144,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245",
"size": 2048,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2015-10-21T07:28:00Z","history":[{"created":"2015-10-21T07:28:00Z","created_by":"created by buildkit","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"created by buildkit","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:78260322e6ea253e5f3f675b3140ebe95ae8fc7e0ac2b1b3b88f1c8fc83525ce","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]}}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:7d82822310244d958c0ba8f7ca7a1a6d05951014a94cddb428db890901a7acd8",
"size": 1261
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
"digest": "sha256:afe006c96b8cc4b555093d5e3c5fed66c350a823f80aee0e832e4b493c990227",
"size": 1792005,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
"digest": "sha256:1f844c50d06d6ce3ae52b9612df9432067c8c7f63d924de3edfaba224aa8067e",
"size": 146,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
"digest": "sha256:d63f0b6201a917c58923b4e58cec8b46a08e9d6eae282e8432e73ccaa848e735",
"size": 237,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
"digest": "sha256:8f28c1c2fe14efe5969fbec33c3d3877cc09bbeeaa0000a1772691e6bca025bc",
"size": 95,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["sh"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"85aa09f514bed8dee7af8bcf0074cf4fe6afecad73abb6c86164cfb7b074c4de","container_config":{"Hostname":"85aa09f514be","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:c3c0c88516c12e4bdff32a3505c8447db711ec0c71682e589b2a5b4e2db9e53f","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2015-10-21T07:28:00Z","docker_version":"20.10.23","history":[{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) ADD file:970f2985156276493000001a07e8417815afcd3a621bf5009ddb87e06eab1514 in / "},{"created":"2015-10-21T07:28:00Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /base, mkfile /base/base.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mkdir /inputs/git, mkfile /inputs/llb.txt, copy /artifact.txt /inputs/http.txt, copy /git.txt /inputs/git/git.txt, copy /sub/nested.txt /inputs/git/nested.txt","comment":"buildkit.exporter.image.v0"},{"created":"2015-10-21T07:28:00Z","created_by":"mount / from exec sh -eux -c \nmkdir -p /foo/bar/generated/deeper\necho exec-fixture \u003e /foo/bar/generated/exec.txt\necho nested-exec \u003e /foo/bar/generated/deeper/nested.txt\n","comment":"buildkit.exporter.image.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a58dbf63860196622d60b97bf39c2cbba3e86039b8a196265389376e95edcf01","sha256:3836403461af46e9486f5f7086a1d6649828a215e357db42b18ed55d55315837","sha256:9228085939d1444bd500ac8a4f8ee9d39fed1e5905da0968ffed61887ab41042","sha256:6463152b9770f67aa00a13fca70749b2eda9cfabdc6fce35ad78d0d73dcc4245"]},"variant":"v8"}
@@ -0,0 +1,43 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:75c490263aada3f00b91cdd6f31d69098f2a27fb6c4a856dc9099a66cc84f96d",
"size": 2309
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
"digest": "sha256:afe006c96b8cc4b555093d5e3c5fed66c350a823f80aee0e832e4b493c990227",
"size": 1792005,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
"digest": "sha256:1f844c50d06d6ce3ae52b9612df9432067c8c7f63d924de3edfaba224aa8067e",
"size": 146,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
"digest": "sha256:cdd742478917b2382493c0c3ccf484ffa94833015e81b7b084c608bb8405affa",
"size": 232,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
"digest": "sha256:8f28c1c2fe14efe5969fbec33c3d3877cc09bbeeaa0000a1772691e6bca025bc",
"size": 95,
"annotations": {
"buildkit/rewritten-timestamp": "1445412480"
}
}
]
}
+10 -1
View File
@@ -34,6 +34,7 @@ import (
"github.com/moby/buildkit/solver/bboltcachestorage"
"github.com/moby/buildkit/solver/llbsolver"
"github.com/moby/buildkit/solver/llbsolver/cdidevices"
"github.com/moby/buildkit/solver/llbsolver/compat"
"github.com/moby/buildkit/solver/llbsolver/history"
"github.com/moby/buildkit/solver/llbsolver/proc"
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
@@ -389,6 +390,14 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*
}
translateLegacySolveRequest(req)
compatibilityVersion := int(req.CompatibilityVersion)
if compatibilityVersion == 0 {
compatibilityVersion = compat.CompatibilityVersionCurrent
}
if err := compat.ValidateCompatibilityVersion(compatibilityVersion); err != nil {
return nil, err
}
defer func() {
time.AfterFunc(time.Second, c.throttledGC)
}()
@@ -538,7 +547,7 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*
FrontendOpt: req.FrontendAttrs,
FrontendInputs: req.FrontendInputs,
CacheImports: cacheImports,
}, llbsolver.ExporterRequest{
}, compatibilityVersion, llbsolver.ExporterRequest{
Exporters: expis,
CacheExporters: cacheExporters,
EnableSessionExporter: req.EnableSessionExporter,
+35
View File
@@ -73,3 +73,38 @@ See [v0.12 documentation](https://github.com/moby/buildkit/blob/v0.12/docs/build
in BuildKit v0.12 and v0.11.
See also the [documentation](/frontend/dockerfile/docs/reference.md#buildkit-built-in-build-args) of the Dockerfile frontend.
## `compatibility-version`
`compatibility-version` pins digest-affecting image assembly behavior for the `image` and `oci` exporters.
BuildKit currently supports these values:
- `10` for the `v0.13.0` and `v0.14.0` historical path
- `20` for the `v0.15.0+` path and current behavior
### `20`
`20` is the current compatibility version.
It matches released BuildKit output from `v0.15.0` and newer for the compatibility test matrix in `client/compatibility_test.go`.
### `10`
`10` represents the `v0.13.0` and `v0.14.0` historical path.
Compared to `20`, `10` differs in two ways:
- in the git-backed copied-content layer, non-executable regular files from the `git` source are stored as mode `0666` instead of `0644`
- `compression=zstd` produces different historical artifacts
The git-backed layer boundary was introduced by commit `6493fd064ceece1d29f3e61aa73a531502f4795c` ("git: ensure exec option is propagated to child git clis").
The old pre-`v0.15.0` zstd artifacts crossed two independent historical changes:
1. `a2a440feb58388e6873082900a5c0b35746b18f3` ("vendor: update klauspost/compress to v1.17.9")
2. `6493fd064ceece1d29f3e61aa73a531502f4795c` ("git: ensure exec option is propagated to child git clis")
The first changed zstd-compressed layer bytes. The second changed the git-backed copied-content layer metadata described above.
Because the currently supported historical backfill for `10` only covers the git-backed artifact difference, BuildKit currently rejects `compression=zstd` with `compatibility-version=10` instead of claiming full reproduction of the old zstd output.
+1 -1
View File
@@ -246,7 +246,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
}
}()
desc, err := e.opt.ImageWriter.Commit(ctx, src, buildInfo.SessionID, buildInfo.InlineCache, &opts)
desc, err := e.opt.ImageWriter.Commit(ctx, src, buildInfo.SessionID, buildInfo.InlineCache, &opts, buildInfo.CompatibilityVersion, e.Type())
if err != nil {
return nil, nil, nil, err
}
+7 -1
View File
@@ -25,6 +25,8 @@ import (
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/solver"
solvererrdefs "github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/llbsolver/compat"
"github.com/moby/buildkit/solver/result"
attestationTypes "github.com/moby/buildkit/util/attestation"
"github.com/moby/buildkit/util/bklog"
@@ -63,10 +65,14 @@ type ImageWriter struct {
opt WriterOpt
}
func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, sessionID string, inlineCache exptypes.InlineCache, opts *ImageCommitOpts) (*ocispecs.Descriptor, error) {
func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, sessionID string, inlineCache exptypes.InlineCache, opts *ImageCommitOpts, compatibilityVersion int, exporterType string) (*ocispecs.Descriptor, error) {
if _, ok := inp.Metadata[exptypes.ExporterPlatformsKey]; len(inp.Refs) > 0 && !ok {
return nil, errors.Errorf("unable to export multiple refs, missing platforms mapping")
}
if compatibilityVersion == compat.CompatibilityVersion013 && opts.RefCfg.Compression.Type == compression.Zstd {
feature := fmt.Sprintf("%s exporter compression=%s", exporterType, opts.RefCfg.Compression.Type.String())
return nil, solvererrdefs.NewUnsupportedCompatibilityFeatureError(compatibilityVersion, feature)
}
isMap := len(inp.Refs) > 0
+4 -3
View File
@@ -50,9 +50,10 @@ type ExporterInstance interface {
}
type ExportBuildInfo struct {
Ref string
InlineCache exptypes.InlineCache
SessionID string
Ref string
InlineCache exptypes.InlineCache
SessionID string
CompatibilityVersion int
}
type DescriptorReference interface {
+1 -1
View File
@@ -159,7 +159,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
}
}()
desc, err := e.opt.ImageWriter.Commit(ctx, src, buildInfo.SessionID, buildInfo.InlineCache, &opts)
desc, err := e.opt.ImageWriter.Commit(ctx, src, buildInfo.SessionID, buildInfo.InlineCache, &opts, buildInfo.CompatibilityVersion, e.Type())
if err != nil {
return nil, nil, nil, err
}
+1 -1
View File
@@ -70,6 +70,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10
github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang v1.23.2
github.com/prometheus/procfs v0.17.0
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b
@@ -203,7 +204,6 @@ require (
github.com/opencontainers/runtime-tools v0.9.1-0.20251114084447-edf4cb3d2116 // indirect
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/otlptranslator v0.0.2 // indirect
+43
View File
@@ -0,0 +1,43 @@
package errdefs
import (
fmt "fmt"
"github.com/containerd/typeurl/v2"
"github.com/moby/buildkit/util/grpcerrors"
)
func init() {
typeurl.Register((*CompatibilityFeature)(nil), "github.com/moby/buildkit", "errdefs.CompatibilityFeature+json")
}
type UnsupportedCompatibilityFeatureError struct {
*CompatibilityFeature
error
}
func (e *UnsupportedCompatibilityFeatureError) Error() string {
msg := fmt.Sprintf("unsupported compatibility-version %d feature %s", e.Version, e.Feature)
if e.error != nil {
msg += ": " + e.error.Error()
}
return msg
}
func (e *UnsupportedCompatibilityFeatureError) Unwrap() error {
return e.error
}
func (e *UnsupportedCompatibilityFeatureError) ToProto() grpcerrors.TypedErrorProto {
return e.CompatibilityFeature
}
func NewUnsupportedCompatibilityFeatureError(version int, feature string) error {
return &UnsupportedCompatibilityFeatureError{
CompatibilityFeature: &CompatibilityFeature{Version: int64(version), Feature: feature},
}
}
func (v *CompatibilityFeature) WrapError(err error) error {
return &UnsupportedCompatibilityFeatureError{error: err, CompatibilityFeature: v}
}
+90 -34
View File
@@ -214,6 +214,58 @@ func (x *FrontendCap) GetName() string {
return ""
}
type CompatibilityFeature struct {
state protoimpl.MessageState `protogen:"open.v1"`
Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
Feature string `protobuf:"bytes,2,opt,name=feature,proto3" json:"feature,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CompatibilityFeature) Reset() {
*x = CompatibilityFeature{}
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CompatibilityFeature) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CompatibilityFeature) ProtoMessage() {}
func (x *CompatibilityFeature) ProtoReflect() protoreflect.Message {
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CompatibilityFeature.ProtoReflect.Descriptor instead.
func (*CompatibilityFeature) Descriptor() ([]byte, []int) {
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{4}
}
func (x *CompatibilityFeature) GetVersion() int64 {
if x != nil {
return x.Version
}
return 0
}
func (x *CompatibilityFeature) GetFeature() string {
if x != nil {
return x.Feature
}
return ""
}
type Subrequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
@@ -223,7 +275,7 @@ type Subrequest struct {
func (x *Subrequest) Reset() {
*x = Subrequest{}
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4]
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -235,7 +287,7 @@ func (x *Subrequest) String() string {
func (*Subrequest) ProtoMessage() {}
func (x *Subrequest) ProtoReflect() protoreflect.Message {
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4]
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -248,7 +300,7 @@ func (x *Subrequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use Subrequest.ProtoReflect.Descriptor instead.
func (*Subrequest) Descriptor() ([]byte, []int) {
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{4}
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{5}
}
func (x *Subrequest) GetName() string {
@@ -275,7 +327,7 @@ type Solve struct {
func (x *Solve) Reset() {
*x = Solve{}
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5]
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -287,7 +339,7 @@ func (x *Solve) String() string {
func (*Solve) ProtoMessage() {}
func (x *Solve) ProtoReflect() protoreflect.Message {
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5]
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -300,7 +352,7 @@ func (x *Solve) ProtoReflect() protoreflect.Message {
// Deprecated: Use Solve.ProtoReflect.Descriptor instead.
func (*Solve) Descriptor() ([]byte, []int) {
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{5}
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{6}
}
func (x *Solve) GetInputIDs() []string {
@@ -382,7 +434,7 @@ type FileAction struct {
func (x *FileAction) Reset() {
*x = FileAction{}
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6]
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -394,7 +446,7 @@ func (x *FileAction) String() string {
func (*FileAction) ProtoMessage() {}
func (x *FileAction) ProtoReflect() protoreflect.Message {
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6]
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -407,7 +459,7 @@ func (x *FileAction) ProtoReflect() protoreflect.Message {
// Deprecated: Use FileAction.ProtoReflect.Descriptor instead.
func (*FileAction) Descriptor() ([]byte, []int) {
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{6}
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{7}
}
func (x *FileAction) GetIndex() int64 {
@@ -427,7 +479,7 @@ type ContentCache struct {
func (x *ContentCache) Reset() {
*x = ContentCache{}
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[7]
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -439,7 +491,7 @@ func (x *ContentCache) String() string {
func (*ContentCache) ProtoMessage() {}
func (x *ContentCache) ProtoReflect() protoreflect.Message {
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[7]
mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -452,7 +504,7 @@ func (x *ContentCache) ProtoReflect() protoreflect.Message {
// Deprecated: Use ContentCache.ProtoReflect.Descriptor instead.
func (*ContentCache) Descriptor() ([]byte, []int) {
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{7}
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{8}
}
func (x *ContentCache) GetIndex() int64 {
@@ -476,7 +528,10 @@ const file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDesc = "" +
"\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" +
"\x06source\x18\x02 \x01(\tR\x06source\"!\n" +
"\vFrontendCap\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\" \n" +
"\x04name\x18\x01 \x01(\tR\x04name\"J\n" +
"\x14CompatibilityFeature\x12\x18\n" +
"\aversion\x18\x01 \x01(\x03R\aversion\x12\x18\n" +
"\afeature\x18\x02 \x01(\tR\afeature\" \n" +
"\n" +
"Subrequest\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\"\xbf\x02\n" +
@@ -509,28 +564,29 @@ func file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP() []
return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescData
}
var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_goTypes = []any{
(*Vertex)(nil), // 0: errdefs.Vertex
(*Source)(nil), // 1: errdefs.Source
(*Frontend)(nil), // 2: errdefs.Frontend
(*FrontendCap)(nil), // 3: errdefs.FrontendCap
(*Subrequest)(nil), // 4: errdefs.Subrequest
(*Solve)(nil), // 5: errdefs.Solve
(*FileAction)(nil), // 6: errdefs.FileAction
(*ContentCache)(nil), // 7: errdefs.ContentCache
nil, // 8: errdefs.Solve.DescriptionEntry
(*pb.SourceInfo)(nil), // 9: pb.SourceInfo
(*pb.Range)(nil), // 10: pb.Range
(*pb.Op)(nil), // 11: pb.Op
(*Vertex)(nil), // 0: errdefs.Vertex
(*Source)(nil), // 1: errdefs.Source
(*Frontend)(nil), // 2: errdefs.Frontend
(*FrontendCap)(nil), // 3: errdefs.FrontendCap
(*CompatibilityFeature)(nil), // 4: errdefs.CompatibilityFeature
(*Subrequest)(nil), // 5: errdefs.Subrequest
(*Solve)(nil), // 6: errdefs.Solve
(*FileAction)(nil), // 7: errdefs.FileAction
(*ContentCache)(nil), // 8: errdefs.ContentCache
nil, // 9: errdefs.Solve.DescriptionEntry
(*pb.SourceInfo)(nil), // 10: pb.SourceInfo
(*pb.Range)(nil), // 11: pb.Range
(*pb.Op)(nil), // 12: pb.Op
}
var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_depIdxs = []int32{
9, // 0: errdefs.Source.info:type_name -> pb.SourceInfo
10, // 1: errdefs.Source.ranges:type_name -> pb.Range
11, // 2: errdefs.Solve.op:type_name -> pb.Op
6, // 3: errdefs.Solve.file:type_name -> errdefs.FileAction
7, // 4: errdefs.Solve.cache:type_name -> errdefs.ContentCache
8, // 5: errdefs.Solve.description:type_name -> errdefs.Solve.DescriptionEntry
10, // 0: errdefs.Source.info:type_name -> pb.SourceInfo
11, // 1: errdefs.Source.ranges:type_name -> pb.Range
12, // 2: errdefs.Solve.op:type_name -> pb.Op
7, // 3: errdefs.Solve.file:type_name -> errdefs.FileAction
8, // 4: errdefs.Solve.cache:type_name -> errdefs.ContentCache
9, // 5: errdefs.Solve.description:type_name -> errdefs.Solve.DescriptionEntry
6, // [6:6] is the sub-list for method output_type
6, // [6:6] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
@@ -543,7 +599,7 @@ func file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_init() {
if File_github_com_moby_buildkit_solver_errdefs_errdefs_proto != nil {
return
}
file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5].OneofWrappers = []any{
file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6].OneofWrappers = []any{
(*Solve_File)(nil),
(*Solve_Cache)(nil),
}
@@ -553,7 +609,7 @@ func file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDesc), len(file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDesc)),
NumEnums: 0,
NumMessages: 9,
NumMessages: 10,
NumExtensions: 0,
NumServices: 0,
},
+5
View File
@@ -24,6 +24,11 @@ message FrontendCap {
string name = 1;
}
message CompatibilityFeature {
int64 version = 1;
string feature = 2;
}
message Subrequest {
string name = 1;
}
+204
View File
@@ -96,6 +96,24 @@ func (m *FrontendCap) CloneMessageVT() proto.Message {
return m.CloneVT()
}
func (m *CompatibilityFeature) CloneVT() *CompatibilityFeature {
if m == nil {
return (*CompatibilityFeature)(nil)
}
r := new(CompatibilityFeature)
r.Version = m.Version
r.Feature = m.Feature
if len(m.unknownFields) > 0 {
r.unknownFields = make([]byte, len(m.unknownFields))
copy(r.unknownFields, m.unknownFields)
}
return r
}
func (m *CompatibilityFeature) CloneMessageVT() proto.Message {
return m.CloneVT()
}
func (m *Subrequest) CloneVT() *Subrequest {
if m == nil {
return (*Subrequest)(nil)
@@ -298,6 +316,28 @@ func (this *FrontendCap) EqualMessageVT(thatMsg proto.Message) bool {
}
return this.EqualVT(that)
}
func (this *CompatibilityFeature) EqualVT(that *CompatibilityFeature) bool {
if this == that {
return true
} else if this == nil || that == nil {
return false
}
if this.Version != that.Version {
return false
}
if this.Feature != that.Feature {
return false
}
return string(this.unknownFields) == string(that.unknownFields)
}
func (this *CompatibilityFeature) EqualMessageVT(thatMsg proto.Message) bool {
that, ok := thatMsg.(*CompatibilityFeature)
if !ok {
return false
}
return this.EqualVT(that)
}
func (this *Subrequest) EqualVT(that *Subrequest) bool {
if this == that {
return true
@@ -646,6 +686,51 @@ func (m *FrontendCap) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *CompatibilityFeature) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *CompatibilityFeature) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *CompatibilityFeature) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.Feature) > 0 {
i -= len(m.Feature)
copy(dAtA[i:], m.Feature)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Feature)))
i--
dAtA[i] = 0x12
}
if m.Version != 0 {
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Version))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *Subrequest) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@@ -963,6 +1048,23 @@ func (m *FrontendCap) SizeVT() (n int) {
return n
}
func (m *CompatibilityFeature) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Version != 0 {
n += 1 + protohelpers.SizeOfVarint(uint64(m.Version))
}
l = len(m.Feature)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
func (m *Subrequest) SizeVT() (n int) {
if m == nil {
return 0
@@ -1470,6 +1572,108 @@ func (m *FrontendCap) UnmarshalVT(dAtA []byte) error {
}
return nil
}
func (m *CompatibilityFeature) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: CompatibilityFeature: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: CompatibilityFeature: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
}
m.Version = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Version |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Feature", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Feature = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Subrequest) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
+44
View File
@@ -11,6 +11,7 @@ import (
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/llbsolver/compat"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/bkmaps"
"github.com/moby/buildkit/util/flightcontrol"
@@ -90,6 +91,34 @@ func (s *state) ResolverCache() ResolverCache {
return s
}
func (s *state) CompatibilityVersion() (int, error) {
s.mu.Lock()
jobs := make([]*Job, 0, len(s.jobs))
for j := range s.jobs {
jobs = append(jobs, j)
}
s.mu.Unlock()
version := 0
for _, j := range jobs {
v, err := j.CompatibilityVersion()
if err != nil {
return 0, err
}
if version == 0 {
version = v
continue
}
if version != v {
return 0, errors.Errorf("conflicting compatibility versions in shared solve state: %d != %d", version, v)
}
}
if version == 0 {
return compat.CompatibilityVersionCurrent, nil
}
return version, nil
}
func (s *state) Lock(key any) (values []any, release func(any) error, err error) {
var rcs []ResolverCache
s.mu.Lock()
@@ -865,6 +894,21 @@ func (j *Job) UniqueID() string {
return j.uniqueID
}
func (j *Job) CompatibilityVersion() (int, error) {
version := compat.CompatibilityVersionCurrent
if err := j.EachValue(context.TODO(), compat.JobValueKey, func(v any) error {
parsed, ok := v.(int)
if !ok {
return errors.Errorf("invalid compatibility version %T", v)
}
version = parsed
return nil
}); err != nil {
return 0, err
}
return version, nil
}
func (j *Job) InContext(ctx context.Context, f func(context.Context, JobContext) error) error {
return f(progress.WithProgress(ctx, j.pw), j)
}
+36
View File
@@ -0,0 +1,36 @@
package compat
import (
"slices"
"github.com/pkg/errors"
)
const (
CompatibilityVersion013 = 10
CompatibilityVersion015 = 20
CompatibilityVersionCurrent = CompatibilityVersion015
)
// JobValueKey is the key used to store the compatibility version on a solver
// job via Job.SetValue/EachValue.
const JobValueKey = "llb.compatibilityversion"
var supportedCompatibilityVersions = []int{
CompatibilityVersion013,
CompatibilityVersion015,
}
func SupportedCompatibilityVersions() []int {
return slices.Clone(supportedCompatibilityVersions)
}
func ValidateCompatibilityVersion(version int) error {
if slices.Contains(supportedCompatibilityVersions, version) {
return nil
}
if version > CompatibilityVersionCurrent {
return errors.Errorf("unsupported compatibility-version %d: upgrade buildkit (max supported: %d)", version, CompatibilityVersionCurrent)
}
return errors.Errorf("unsupported compatibility-version %d (supported: %v)", version, supportedCompatibilityVersions)
}
+19
View File
@@ -0,0 +1,19 @@
package compat
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestSupportedCompatibilityVersions(t *testing.T) {
require.Equal(t, []int{10, 20}, SupportedCompatibilityVersions())
require.Equal(t, 20, CompatibilityVersionCurrent)
}
func TestValidateCompatibilityVersion(t *testing.T) {
require.NoError(t, ValidateCompatibilityVersion(CompatibilityVersion013))
require.NoError(t, ValidateCompatibilityVersion(CompatibilityVersionCurrent))
require.ErrorContains(t, ValidateCompatibilityVersion(11), "unsupported compatibility-version 11")
require.ErrorContains(t, ValidateCompatibilityVersion(30), "upgrade buildkit")
}
+8 -3
View File
@@ -188,11 +188,16 @@ func (s *Solver) runExporters(ctx context.Context, ref string, exporters []expor
defer inlineCacheMu.Unlock()
return runInlineCacheExporter(ctx, exp, inlineCacheExporter, job, cached)
})
compatibilityVersion, err := job.CompatibilityVersion()
if err != nil {
return err
}
resp, finalize, desc, expErr := exp.Export(ctx, inp, exporter.ExportBuildInfo{
Ref: ref,
SessionID: job.SessionID,
InlineCache: inlineCache,
Ref: ref,
SessionID: job.SessionID,
InlineCache: inlineCache,
CompatibilityVersion: compatibilityVersion,
})
resps[i], finalizeFuncs[i], descs[i] = resp, finalize, desc
if expErr != nil {
+4
View File
@@ -311,3 +311,7 @@ func (j *jobCtx) Cleanup(f func() error) error {
func (j *jobCtx) ResolverCache() solver.ResolverCache {
return nil
}
func (j *jobCtx) CompatibilityVersion() (int, error) {
return 0, nil
}
-1
View File
@@ -71,7 +71,6 @@ func ProvenanceProcessor(slsaVersion provenancetypes.ProvenanceSLSA, attrs map[s
if err != nil {
return nil, err
}
return json.MarshalIndent(pr, "", " ")
},
})
+6
View File
@@ -506,6 +506,12 @@ func (p *ProvenanceCreator) Predicate(ctx context.Context) (any, error) {
p.pr.RunDetails.Metadata.BuildKitMetadata.SysUsage = sysSamples
}
compatibilityVersion, err := p.j.CompatibilityVersion()
if err != nil {
return nil, err
}
p.pr.BuildDefinition.ExternalParameters.Request.CompatibilityVersion = compatibilityVersion
if p.slsaVersion == provenancetypes.ProvenanceSLSA02 {
return p.pr.ConvertToSLSA02(), nil
}
+6 -6
View File
@@ -172,12 +172,12 @@ type ProvenanceMetadataSLSA1 struct {
}
type Parameters struct {
Frontend string `json:"frontend,omitempty"`
Args map[string]string `json:"args,omitempty"`
Secrets []*Secret `json:"secrets,omitempty"`
SSH []*SSH `json:"ssh,omitempty"`
Locals []*LocalSource `json:"locals,omitempty"`
// TODO: select export attributes
Frontend string `json:"frontend,omitempty"`
Args map[string]string `json:"args,omitempty"`
Secrets []*Secret `json:"secrets,omitempty"`
SSH []*SSH `json:"ssh,omitempty"`
Locals []*LocalSource `json:"locals,omitempty"`
CompatibilityVersion int `json:"compatibilityVersion,omitempty"`
// TODO: frontend inputs
}
+6 -1
View File
@@ -20,6 +20,7 @@ import (
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/solver/llbsolver/compat"
"github.com/moby/buildkit/solver/llbsolver/history"
"github.com/moby/buildkit/solver/result"
spb "github.com/moby/buildkit/sourcepolicy/pb"
@@ -153,7 +154,7 @@ func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge {
return s.bridge(b)
}
func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req frontend.SolveRequest, exp ExporterRequest, ent []entitlements.Entitlement, post []Processor, internal bool, srcPol *spb.Policy, policySession string) (_ *client.SolveResponse, err error) {
func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req frontend.SolveRequest, compatibilityVersion int, exp ExporterRequest, ent []entitlements.Entitlement, post []Processor, internal bool, srcPol *spb.Policy, policySession string) (_ *client.SolveResponse, err error) {
hasNamedDockerfileContext := false
for k := range req.FrontendOpt {
if k == "context:dockerfile.v0" || strings.HasPrefix(k, "context:dockerfile.v0::") {
@@ -216,6 +217,10 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro
if policySession != "" {
j.SetValue(keySourcePolicySession, policySession)
}
if compatibilityVersion == 0 {
compatibilityVersion = compat.CompatibilityVersionCurrent
}
j.SetValue(compat.JobValueKey, compatibilityVersion)
j.SessionID = sessionID
+2
View File
@@ -184,6 +184,8 @@ type JobContext interface {
// ResolverCache returns object for memorizing/synchronizing remote resolving decisions during the job.
// Steps from same build job will share the same resolver cache.
ResolverCache() ResolverCache
// CompatibilityVersion returns the solve-wide compatibility version for the current job.
CompatibilityVersion() (int, error)
}
type ResolverCache interface {
+44 -2
View File
@@ -24,6 +24,7 @@ import (
"github.com/moby/buildkit/session/sshforward"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/solver/llbsolver/compat"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/source"
srctypes "github.com/moby/buildkit/source/types"
@@ -789,6 +790,14 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context, jobCtx solver.JobConte
g = jobCtx.Session()
}
gs.getAuthToken(ctx, g)
compatibilityVersion := 0
if jobCtx != nil {
var err error
compatibilityVersion, err = jobCtx.CompatibilityVersion()
if err != nil {
return nil, err
}
}
snapshotKey := cacheKey + ":" + gs.src.Subdir
gs.locker.Lock(snapshotKey)
@@ -808,7 +817,7 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context, jobCtx solver.JobConte
}
defer repo.Release()
ref, err := gs.checkout(ctx, repo, g)
ref, err := gs.checkout(ctx, repo, g, compatibilityVersion)
if err != nil {
return nil, err
}
@@ -975,7 +984,7 @@ func (gs *gitSourceHandler) tryRemoteFetch(ctx context.Context, g session.Group,
return repo, nil
}
func (gs *gitSourceHandler) checkout(ctx context.Context, repo *gitRepo, g session.Group) (_ cache.ImmutableRef, retErr error) {
func (gs *gitSourceHandler) checkout(ctx context.Context, repo *gitRepo, g session.Group, compatibilityVersion int) (_ cache.ImmutableRef, retErr error) {
ref := gs.src.Ref
checkoutRef, err := gs.cache.New(ctx, nil, g, cache.WithRecordType(client.UsageRecordTypeGitCheckout), cache.WithDescription(fmt.Sprintf("git snapshot for %s#%s", urlutil.RedactCredentials(gs.src.Remote), ref)))
if err != nil {
@@ -1138,6 +1147,12 @@ func (gs *gitSourceHandler) checkout(ctx context.Context, repo *gitRepo, g sessi
}
}
if compatibilityVersion == compat.CompatibilityVersion013 {
if err := resetCompatibility014FileModes(checkoutDir); err != nil {
return nil, errors.Wrapf(err, "failed to normalize compatibility file modes for %s", urlutil.RedactCredentials(gs.src.Remote))
}
}
if gs.src.MTime == "commit" {
commitTime, err := getCommitTime(ctx, git, ref)
if err != nil {
@@ -1220,6 +1235,33 @@ func resetSnapshotMtimes(dir string, t time.Time) error {
return nil
}
// resetCompatibility014FileModes restores the pre-v0.15 git checkout file
// mode for non-executable regular files, which were stored with group/other
// write bits set before the exec-option propagation fix. Executable files are
// left untouched: their pre-v0.15 behavior is not covered by the current
// compatibility matrix, and blindly adding write bits to 0o755 would be a
// guess.
func resetCompatibility014FileModes(dir string) error {
return filepath.WalkDir(dir, func(p string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || d.Type()&os.ModeSymlink != 0 {
return nil
}
info, err := d.Info()
if err != nil {
return err
}
mode := info.Mode()
if !mode.IsRegular() || mode&0o111 != 0 {
return nil
}
return os.Chmod(p, mode|0o222)
})
}
type wouldClobberExistingTagError struct {
error
}
+70
View File
@@ -27,8 +27,11 @@ import (
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/cache/metadata"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/snapshot"
containerdsnapshot "github.com/moby/buildkit/snapshot/containerd"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/solver/llbsolver/compat"
"github.com/moby/buildkit/util/gitutil"
"github.com/moby/buildkit/util/gitutil/gitobject"
"github.com/moby/buildkit/util/gitutil/gitsign"
@@ -2188,6 +2191,54 @@ func setupGitSource(t *testing.T, tmpdir string) *Source {
return gs
}
type testJobContext struct {
compatibilityVersion int
}
func (j testJobContext) Session() session.Group {
return nil
}
func (j testJobContext) Cleanup(func() error) error {
return nil
}
func (j testJobContext) ResolverCache() solver.ResolverCache {
return nil
}
func (j testJobContext) CompatibilityVersion() (int, error) {
return j.compatibilityVersion, nil
}
func gitSnapshotMode(ctx context.Context, t *testing.T, gs *Source, id *GitIdentifier, jobCtx solver.JobContext) os.FileMode {
t.Helper()
g, err := gs.Resolve(ctx, id, nil, nil)
require.NoError(t, err)
_, _, _, done, err := g.CacheKey(ctx, jobCtx, 0)
require.NoError(t, err)
require.True(t, done)
ref, err := g.Snapshot(ctx, jobCtx)
require.NoError(t, err)
defer ref.Release(context.TODO())
mount, err := ref.Mount(ctx, true, nil)
require.NoError(t, err)
lm := snapshot.LocalMounter(mount)
dir, err := lm.Mount()
require.NoError(t, err)
defer lm.Unmount()
st, err := os.Lstat(filepath.Join(dir, "def"))
require.NoError(t, err)
return st.Mode() & os.ModePerm
}
type gitRepoFixture struct {
mainPath, subPath string // Filesystem paths to the respective repos
mainURL, subURL string // HTTP URLs for the respective repos
@@ -2497,6 +2548,25 @@ func TestCommitTimeMtimesKeepGitDirSHA256(t *testing.T) {
testCommitTimeMtimes(t, "sha256", true)
}
func TestCompatibility014FileModes(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Depends on unimplemented containerd bind-mount support on Windows")
}
t.Parallel()
ctx := logProgressStreams(context.Background(), t)
gs := setupGitSource(t, t.TempDir())
repo := setupGitRepo(t, "sha1")
id := &GitIdentifier{Remote: repo.mainURL, Ref: "master"}
currentMode := gitSnapshotMode(ctx, t, gs, id, testJobContext{compatibilityVersion: compat.CompatibilityVersionCurrent})
compat013Mode := gitSnapshotMode(ctx, t, gs, id, testJobContext{compatibilityVersion: compat.CompatibilityVersion013})
require.Equal(t, os.FileMode(0o644), currentMode)
require.Equal(t, os.FileMode(0o666), compat013Mode)
}
func testCommitTimeMtimes(t *testing.T, format string, keepGitDir bool) {
if runtime.GOOS == "windows" {
t.Skip("Depends on unimplemented containerd bind-mount support on Windows")
+4
View File
@@ -599,6 +599,10 @@ func (s *simpleJobContext) ResolverCache() solver.ResolverCache {
return nil
}
func (s *simpleJobContext) CompatibilityVersion() (int, error) {
return 0, nil
}
func readSignFixture(t *testing.T, fixturesPath, name string) []byte {
t.Helper()
p := filepath.Join(fixturesPath, name)