Merge pull request #823 from crazy-max/buildx-attestations-digest

buildx(imagetools): return attestations digests
This commit is contained in:
CrazyMax
2025-10-30 15:52:12 +01:00
committed by GitHub
6 changed files with 329 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:dccc69dd895968c4f21aa9e43e715f25f0cedfce4b17f1014c88c307928e22fc",
"size": 1599
}

View File

@@ -0,0 +1,141 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:79cc6476ab1a3371c9afd8b44e7c55610057c43e18d9b39b68e2b0c2475cc1b6",
"size": 4654,
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:dccc69dd895968c4f21aa9e43e715f25f0cedfce4b17f1014c88c307928e22fc",
"size": 1599,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:1b6bce668653f08e2d0f9f7c9b646675b2cbce94ce8abdf4eb0eabaef4353045",
"size": 1599,
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:8f251fda6057e9dffc54f7874b249920f15f1813e9b1406a0cebeca5e4ab1ad9",
"size": 1599,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:d306cbc2d506547f136c8e0ea040b929743f298fb2813d9030efdb9d9eee4d51",
"size": 1599,
"platform": {
"architecture": "s390x",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:9d195ff2dc9ef347bb52ebb1c2a6e6587d4bd87019d2ea11df3e7046a3d19708",
"size": 1599,
"platform": {
"architecture": "ppc64le",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:72410c2c4529fca9339ebbcc8db2a1d5cb4d72d72c669f50b6d45d8a0f79fc22",
"size": 1599,
"platform": {
"architecture": "riscv64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:2ba4ad6eae1efcafee73a971953093c7c32b6938f2f9fd4998c8bf4d0fbe76f2",
"size": 1113,
"annotations": {
"vnd.docker.reference.digest": "sha256:dccc69dd895968c4f21aa9e43e715f25f0cedfce4b17f1014c88c307928e22fc",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:0709528fae1747ce17638ad2978ee7936b38a294136eaadaf692e415f64b1e03",
"size": 1113,
"annotations": {
"vnd.docker.reference.digest": "sha256:1b6bce668653f08e2d0f9f7c9b646675b2cbce94ce8abdf4eb0eabaef4353045",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:241b7159129d53923c89708bcc052b3398086a826519896be2f025545916e43e",
"size": 1113,
"annotations": {
"vnd.docker.reference.digest": "sha256:8f251fda6057e9dffc54f7874b249920f15f1813e9b1406a0cebeca5e4ab1ad9",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:97f4a222a7992dba6dc1a43991d0cca1fcffdc25593033c6a3a7ff14c8651cbf",
"size": 1113,
"annotations": {
"vnd.docker.reference.digest": "sha256:d306cbc2d506547f136c8e0ea040b929743f298fb2813d9030efdb9d9eee4d51",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:aa933713d8094b2708120e889acb6f7153dee4e0f3298ccd3e37a584cd0c260d",
"size": 1113,
"annotations": {
"vnd.docker.reference.digest": "sha256:9d195ff2dc9ef347bb52ebb1c2a6e6587d4bd87019d2ea11df3e7046a3d19708",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:d95ca72d4f2a6bc416d4b2f3003b2af9d5f4dea99acec6ad3ab0c2082000a98c",
"size": 1113,
"annotations": {
"vnd.docker.reference.digest": "sha256:72410c2c4529fca9339ebbcc8db2a1d5cb4d72d72c669f50b6d45d8a0f79fc22",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}
]
}

View File

@@ -0,0 +1,80 @@
[
{
"mediaType":"application/vnd.oci.image.manifest.v1+json",
"digest":"sha256:2ba4ad6eae1efcafee73a971953093c7c32b6938f2f9fd4998c8bf4d0fbe76f2",
"size":1113,
"annotations":{
"vnd.docker.reference.digest":"sha256:dccc69dd895968c4f21aa9e43e715f25f0cedfce4b17f1014c88c307928e22fc",
"vnd.docker.reference.type":"attestation-manifest"
},
"platform":{
"architecture":"unknown",
"os":"unknown"
}
},
{
"mediaType":"application/vnd.oci.image.manifest.v1+json",
"digest":"sha256:0709528fae1747ce17638ad2978ee7936b38a294136eaadaf692e415f64b1e03",
"size":1113,
"annotations":{
"vnd.docker.reference.digest":"sha256:1b6bce668653f08e2d0f9f7c9b646675b2cbce94ce8abdf4eb0eabaef4353045",
"vnd.docker.reference.type":"attestation-manifest"
},
"platform":{
"architecture":"unknown",
"os":"unknown"
}
},
{
"mediaType":"application/vnd.oci.image.manifest.v1+json",
"digest":"sha256:241b7159129d53923c89708bcc052b3398086a826519896be2f025545916e43e",
"size":1113,
"annotations":{
"vnd.docker.reference.digest":"sha256:8f251fda6057e9dffc54f7874b249920f15f1813e9b1406a0cebeca5e4ab1ad9",
"vnd.docker.reference.type":"attestation-manifest"
},
"platform":{
"architecture":"unknown",
"os":"unknown"
}
},
{
"mediaType":"application/vnd.oci.image.manifest.v1+json",
"digest":"sha256:97f4a222a7992dba6dc1a43991d0cca1fcffdc25593033c6a3a7ff14c8651cbf",
"size":1113,
"annotations":{
"vnd.docker.reference.digest":"sha256:d306cbc2d506547f136c8e0ea040b929743f298fb2813d9030efdb9d9eee4d51",
"vnd.docker.reference.type":"attestation-manifest"
},
"platform":{
"architecture":"unknown",
"os":"unknown"
}
},
{
"mediaType":"application/vnd.oci.image.manifest.v1+json",
"digest":"sha256:aa933713d8094b2708120e889acb6f7153dee4e0f3298ccd3e37a584cd0c260d",
"size":1113,
"annotations":{
"vnd.docker.reference.digest":"sha256:9d195ff2dc9ef347bb52ebb1c2a6e6587d4bd87019d2ea11df3e7046a3d19708",
"vnd.docker.reference.type":"attestation-manifest"
},
"platform":{
"architecture":"unknown",
"os":"unknown"
}
},
{
"mediaType":"application/vnd.oci.image.manifest.v1+json",
"digest":"sha256:d95ca72d4f2a6bc416d4b2f3003b2af9d5f4dea99acec6ad3ab0c2082000a98c",
"size":1113,
"annotations":{
"vnd.docker.reference.digest":"sha256:72410c2c4529fca9339ebbcc8db2a1d5cb4d72d72c669f50b6d45d8a0f79fc22",
"vnd.docker.reference.type":"attestation-manifest"
},
"platform":{
"architecture":"unknown",
"os":"unknown"
}
}
]

View File

@@ -19,7 +19,10 @@ import * as fs from 'fs';
import * as path from 'path';
import {ImageTools} from '../../src/buildx/imagetools';
import {Manifest as ImageToolsManifest} from '../../src/types/buildx/imagetools';
import {Image} from '../../src/types/oci/config';
import {Descriptor} from '../../src/types/oci/descriptor';
const fixturesDir = path.join(__dirname, '..', '.fixtures');
@@ -37,3 +40,39 @@ maybe('inspectImage', () => {
expect(image).toEqual(expectedImage);
});
});
maybe('inspectManifest', () => {
it('inspect descriptor', async () => {
const manifest = await new ImageTools().inspectManifest('moby/buildkit:latest@sha256:dccc69dd895968c4f21aa9e43e715f25f0cedfce4b17f1014c88c307928e22fc');
const expectedManifest = <Descriptor>JSON.parse(fs.readFileSync(path.join(fixturesDir, 'imagetools-03.json'), {encoding: 'utf-8'}).trim());
expect(manifest).toEqual(expectedManifest);
});
it('inspect index', async () => {
const manifest = await new ImageTools().inspectManifest('moby/buildkit:latest@sha256:79cc6476ab1a3371c9afd8b44e7c55610057c43e18d9b39b68e2b0c2475cc1b6');
const expectedManifest = <ImageToolsManifest>JSON.parse(fs.readFileSync(path.join(fixturesDir, 'imagetools-04.json'), {encoding: 'utf-8'}).trim());
expect(manifest).toEqual(expectedManifest);
});
});
maybe('attestationDescriptors', () => {
it('returns buildkit attestations descriptors', async () => {
const attestations = await new ImageTools().attestationDescriptors('moby/buildkit:latest@sha256:79cc6476ab1a3371c9afd8b44e7c55610057c43e18d9b39b68e2b0c2475cc1b6');
const expectedAttestations = <Array<Descriptor>>JSON.parse(fs.readFileSync(path.join(fixturesDir, 'imagetools-05.json'), {encoding: 'utf-8'}).trim());
expect(attestations).toEqual(expectedAttestations);
});
});
maybe('attestationDigests', () => {
it('returns buildkit attestations digests', async () => {
const digests = await new ImageTools().attestationDigests('moby/buildkit:latest@sha256:79cc6476ab1a3371c9afd8b44e7c55610057c43e18d9b39b68e2b0c2475cc1b6');
// prettier-ignore
expect(digests).toEqual([
'sha256:2ba4ad6eae1efcafee73a971953093c7c32b6938f2f9fd4998c8bf4d0fbe76f2',
'sha256:0709528fae1747ce17638ad2978ee7936b38a294136eaadaf692e415f64b1e03',
'sha256:241b7159129d53923c89708bcc052b3398086a826519896be2f025545916e43e',
'sha256:97f4a222a7992dba6dc1a43991d0cca1fcffdc25593033c6a3a7ff14c8651cbf',
'sha256:aa933713d8094b2708120e889acb6f7153dee4e0f3298ccd3e37a584cd0c260d',
'sha256:d95ca72d4f2a6bc416d4b2f3003b2af9d5f4dea99acec6ad3ab0c2082000a98c'
]);
});
});

View File

@@ -17,7 +17,10 @@
import {Buildx} from './buildx';
import {Exec} from '../exec';
import {Manifest as ImageToolsManifest} from '../types/buildx/imagetools';
import {Image} from '../types/oci/config';
import {Descriptor} from '../types/oci/descriptor';
import {Digest} from '../types/oci/digest';
export interface ImageToolsOpts {
buildx?: Buildx;
@@ -58,4 +61,37 @@ export class ImageTools {
throw new Error('Unexpected output format');
});
}
public async inspectManifest(name: string): Promise<ImageToolsManifest | Descriptor> {
const cmd = await this.getInspectCommand([name, '--format', '{{json .Manifest}}']);
return await Exec.getExecOutput(cmd.command, cmd.args, {
ignoreReturnCode: true,
silent: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim());
}
const parsedOutput = JSON.parse(res.stdout);
if (typeof parsedOutput === 'object' && !Array.isArray(parsedOutput) && parsedOutput !== null) {
if (Object.prototype.hasOwnProperty.call(parsedOutput, 'manifests')) {
return <ImageToolsManifest>parsedOutput;
} else {
return <Descriptor>parsedOutput;
}
}
throw new Error('Unexpected output format');
});
}
public async attestationDescriptors(name: string): Promise<Array<Descriptor>> {
const manifest = await this.inspectManifest(name);
if (typeof manifest === 'object' && manifest !== null && 'manifests' in manifest && Array.isArray(manifest.manifests)) {
return manifest.manifests.filter(m => m.annotations && m.annotations['vnd.docker.reference.type'] === 'attestation-manifest');
}
throw new Error(`No attestation descriptors found for ${name}`);
}
public async attestationDigests(name: string): Promise<Array<Digest>> {
return (await this.attestationDescriptors(name)).map(attestation => attestation.digest);
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright 2025 actions-toolkit authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Versioned} from '../oci/versioned';
import {Descriptor} from '../oci/descriptor';
import {Digest} from '../oci/digest';
// https://github.com/docker/buildx/blob/62857022a08552bee5cad0c3044a9a3b185f0b32/util/imagetools/printers.go#L109-L123
export interface Manifest extends Versioned {
mediaType?: string;
digest: Digest;
size: number;
manifests?: Descriptor[];
annotations?: Record<string, string>;
}