add new OCI manifests for attestations

This commit is contained in:
Conor Sloan
2024-08-22 18:13:15 +01:00
parent c11354f432
commit e44432d3e5
6 changed files with 377 additions and 106 deletions
+5 -3
View File
@@ -182,7 +182,7 @@ function configureFetchMock(
)
}
const testManifest: ociContainer.Manifest = {
const testManifest: ociContainer.OCIImageManifest = {
schemaVersion: 2,
mediaType: 'application/vnd.oci.image.manifest.v1+json',
artifactType: 'application/vnd.oci.image.manifest.v1+json',
@@ -526,8 +526,10 @@ function validateRequestConfig(url: string, config: any): void {
}
}
function cloneLayers(layers: ociContainer.Layer[]): ociContainer.Layer[] {
const result: ociContainer.Layer[] = []
function cloneLayers(
layers: ociContainer.Descriptor[]
): ociContainer.Descriptor[] {
const result: ociContainer.Descriptor[] = []
for (const layer of layers) {
result.push({ ...layer }) // this is _NOT_ a deep clone
}
+158 -68
View File
@@ -1,36 +1,19 @@
import { createActionPackageManifest, sha256Digest } from '../src/oci-container'
import {
createActionPackageManifest,
sha256Digest,
sizeInBytes,
OCIImageManifest,
createSigstoreAttestationManifest,
OCIIndexManifest,
createReferrerTagManifest
} from '../src/oci-container'
import { FileMetadata } from '../src/fs-helper'
const createdTimestamp = '2021-01-01T00:00:00.000Z'
describe('sha256Digest', () => {
it('calculates the SHA256 digest of the provided manifest', () => {
const date = new Date('2021-01-01T00:00:00Z')
const repo = 'test-org/test-repo'
const version = '1.2.3'
const repoId = '123'
const ownerId = '456'
const sourceCommit = 'abc'
const tarFile: FileMetadata = {
path: '/test/test/test.tar.gz',
sha256: 'tarSha',
size: 123
}
const zipFile: FileMetadata = {
path: '/test/test/test.zip',
sha256: 'zipSha',
size: 456
}
const manifest = createActionPackageManifest(
tarFile,
zipFile,
repo,
repoId,
ownerId,
sourceCommit,
version,
date
)
const { manifest } = testActionPackageManifest()
const digest = sha256Digest(manifest)
const expectedDigest =
'sha256:dd8537ef913cf87e25064a074973ed2c62699f1dbd74d0dd78e85d394a5758b5'
@@ -39,25 +22,17 @@ describe('sha256Digest', () => {
})
})
describe('size', () => {
it('returns the total size of the provided manifest', () => {
const { manifest } = testActionPackageManifest()
const size = sizeInBytes(manifest)
expect(size).toBe(1133)
})
})
describe('createActionPackageManifest', () => {
it('creates a manifest containing the provided information', () => {
const date = new Date()
const repo = 'test-org/test-repo'
const sanitizedRepo = 'test-org-test-repo'
const version = '1.2.3'
const repoId = '123'
const ownerId = '456'
const sourceCommit = 'abc'
const tarFile: FileMetadata = {
path: '/test/test/test.tar.gz',
sha256: 'tarSha',
size: 123
}
const zipFile: FileMetadata = {
path: '/test/test/test.zip',
sha256: 'zipSha',
size: 456
}
const { manifest, zipFile, tarFile } = testActionPackageManifest()
const expectedJSON = `{
"schemaVersion": 2,
@@ -79,7 +54,7 @@ describe('createActionPackageManifest', () => {
"size":${tarFile.size},
"digest":"${tarFile.sha256}",
"annotations":{
"org.opencontainers.image.title":"${sanitizedRepo}_${version}.tar.gz"
"org.opencontainers.image.title":"test-org-test-repo_1.2.3.tar.gz"
}
},
{
@@ -87,12 +62,12 @@ describe('createActionPackageManifest', () => {
"size":${zipFile.size},
"digest":"${zipFile.sha256}",
"annotations":{
"org.opencontainers.image.title":"${sanitizedRepo}_${version}.zip"
"org.opencontainers.image.title":"test-org-test-repo_1.2.3.zip"
}
}
],
"annotations":{
"org.opencontainers.image.created":"${date.toISOString()}",
"org.opencontainers.image.created":"${createdTimestamp}",
"action.tar.gz.digest":"${tarFile.sha256}",
"action.zip.digest":"${zipFile.sha256}",
"com.github.package.type":"actions_oci_pkg",
@@ -103,26 +78,141 @@ describe('createActionPackageManifest', () => {
}
}`
const manifest = createActionPackageManifest(
{
path: 'test.tar.gz',
size: tarFile.size,
sha256: tarFile.sha256
},
{
path: 'test.zip',
size: zipFile.size,
sha256: zipFile.sha256
},
repo,
repoId,
ownerId,
sourceCommit,
version,
date
)
const manifestJSON = JSON.stringify(manifest)
expect(manifestJSON).toEqual(expectedJSON.replace(/\s/g, ''))
})
})
describe('createSigstoreAttestationManifest', () => {
it('creates a manifest containing the provided information', () => {
const manifest = testAttestationManifest()
const expectedJSON = `{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"artifactType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"config": {
"mediaType": "application/vnd.oci.empty.v1+json",
"size": 2,
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
},
"layers": [
{
"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"size": 10,
"digest": "bundleDigest"
}
],
"subject": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 100,
"digest": "subjectDigest"
},
"annotations": {
"dev.sigstore.bundle.content": "dsse-envelope",
"dev.sigstore.bundle.predicateType": "https://slsa.dev/provenance/v1",
"com.github.package.type": "actions_oci_pkg_attestation",
"org.opencontainers.image.created": "2021-01-01T00:00:00.000Z"
}
}
`
const manifestJSON = JSON.stringify(manifest)
expect(manifestJSON).toEqual(expectedJSON.replace(/\s/g, ''))
})
})
describe('createReferrerIndexManifest', () => {
it('creates a manifest containing the provided information', () => {
const manifest = testReferrerIndexManifest()
const expectedJSON = `
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"artifactType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"size": 100,
"digest": "attDigest",
"annotations": {
"com.github.package.type": "actions_oci_pkg_attestation",
"org.opencontainers.image.created": "2021-01-01T00:00:00.000Z",
"dev.sigstore.bundle.content": "dsse-envelope",
"dev.sigstore.bundle.predicateType": "https://slsa.dev/provenance/v1"
}
}
],
"annotations": {
"com.github.package.type": "actions_oci_pkg_referrer_tag",
"org.opencontainers.image.created": "2021-01-01T00:00:00.000Z"
}
}
`
const manifestJSON = JSON.stringify(manifest)
expect(manifestJSON).toEqual(expectedJSON.replace(/\s/g, ''))
})
})
function testActionPackageManifest(): {
manifest: OCIImageManifest
tarFile: FileMetadata
zipFile: FileMetadata
} {
const date = new Date('2021-01-01T00:00:00Z')
const repo = 'test-org/test-repo'
const version = '1.2.3'
const repoId = '123'
const ownerId = '456'
const sourceCommit = 'abc'
const tarFile: FileMetadata = {
path: '/test/test/test.tar.gz',
sha256: 'tarSha',
size: 123
}
const zipFile: FileMetadata = {
path: '/test/test/test.zip',
sha256: 'zipSha',
size: 456
}
const manifest = createActionPackageManifest(
tarFile,
zipFile,
repo,
repoId,
ownerId,
sourceCommit,
version,
date
)
return {
manifest,
tarFile,
zipFile
}
}
function testAttestationManifest(): OCIImageManifest {
return createSigstoreAttestationManifest(
10,
'bundleDigest',
100,
'subjectDigest',
new Date(createdTimestamp)
)
}
function testReferrerIndexManifest(): OCIIndexManifest {
return createReferrerTagManifest(
'attDigest',
100,
new Date(createdTimestamp),
new Date(createdTimestamp)
)
}
+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="116" height="20" role="img" aria-label="Coverage: 97.17%"><title>Coverage: 97.17%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#4c1"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">97.17%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">97.17%</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="116" height="20" role="img" aria-label="Coverage: 97.39%"><title>Coverage: 97.39%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#4c1"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">97.39%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">97.39%</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Generated Vendored
+81 -9
View File
@@ -107754,25 +107754,40 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.createActionPackageManifest = createActionPackageManifest;
exports.createSigstoreAttestationManifest = createSigstoreAttestationManifest;
exports.createReferrerTagManifest = createReferrerTagManifest;
exports.sha256Digest = sha256Digest;
exports.sizeInBytes = sizeInBytes;
const crypto = __importStar(__nccwpck_require__(6113));
const imageIndexMediaType = 'application/vnd.oci.image.index.v1+json';
const imageManifestMediaType = 'application/vnd.oci.image.manifest.v1+json';
const actionsPackageMediaType = 'application/vnd.github.actions.package.v1+json';
const actionsPackageTarLayerMediaType = 'application/vnd.github.actions.package.layer.v1.tar+gzip';
const actionsPackageZipLayerMediaType = 'application/vnd.github.actions.package.layer.v1.zip';
const sigstoreBundleMediaType = 'application/vnd.dev.sigstore.bundle.v0.3+json';
const ociEmptyMediaType = 'application/vnd.oci.empty.v1+json';
const actionPackageAnnotationValue = 'actions_oci_pkg';
const actionPackageAttestationAnnotationValue = 'actions_oci_pkg_attestation';
const actionPackageReferrerTagAnnotationValue = 'actions_oci_pkg_referrer_tag';
const emptyConfigSize = 2;
const emptyConfigSha = 'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a';
// Given a name and archive metadata, creates a manifest in the format expected by GHCR for an Actions Package.
function createActionPackageManifest(tarFile, zipFile, repository, repoId, ownerId, sourceCommit, version, created) {
function createActionPackageManifest(tarFile, zipFile, repository, repoId, ownerId, sourceCommit, version, created = new Date()) {
const configLayer = createConfigLayer();
const sanitizedRepo = sanitizeRepository(repository);
const tarLayer = createTarLayer(tarFile, sanitizedRepo, version);
const zipLayer = createZipLayer(zipFile, sanitizedRepo, version);
const manifest = {
schemaVersion: 2,
mediaType: 'application/vnd.oci.image.manifest.v1+json',
artifactType: 'application/vnd.github.actions.package.v1+json',
mediaType: imageManifestMediaType,
artifactType: actionsPackageMediaType,
config: configLayer,
layers: [configLayer, tarLayer, zipLayer],
annotations: {
'org.opencontainers.image.created': created.toISOString(),
'action.tar.gz.digest': tarFile.sha256,
'action.zip.digest': zipFile.sha256,
'com.github.package.type': 'actions_oci_pkg',
'com.github.package.type': actionPackageAnnotationValue,
'com.github.package.version': version,
'com.github.source.repo.id': repoId,
'com.github.source.repo.owner.id': ownerId,
@@ -107781,6 +107796,59 @@ function createActionPackageManifest(tarFile, zipFile, repository, repoId, owner
};
return manifest;
}
function createSigstoreAttestationManifest(bundleSize, bundleDigest, subjectSize, subjectDigest, created = new Date()) {
const configLayer = createConfigLayer();
const sigstoreAttestationLayer = {
mediaType: sigstoreBundleMediaType,
size: bundleSize,
digest: bundleDigest
};
const subject = {
mediaType: imageManifestMediaType,
size: subjectSize,
digest: subjectDigest
};
const manifest = {
schemaVersion: 2,
mediaType: imageManifestMediaType,
artifactType: sigstoreBundleMediaType,
config: configLayer,
layers: [sigstoreAttestationLayer],
subject,
annotations: {
'dev.sigstore.bundle.content': 'dsse-envelope',
'dev.sigstore.bundle.predicateType': 'https://slsa.dev/provenance/v1',
'com.github.package.type': actionPackageAttestationAnnotationValue,
'org.opencontainers.image.created': created.toISOString()
}
};
return manifest;
}
function createReferrerTagManifest(attestationDigest, attestationSize, attestationCreated, created = new Date()) {
const manifest = {
schemaVersion: 2,
mediaType: imageIndexMediaType,
manifests: [
{
mediaType: imageManifestMediaType,
artifactType: sigstoreBundleMediaType,
size: attestationSize,
digest: attestationDigest,
annotations: {
'com.github.package.type': actionPackageAttestationAnnotationValue,
'org.opencontainers.image.created': attestationCreated.toISOString(),
'dev.sigstore.bundle.content': 'dsse-envelope',
'dev.sigstore.bundle.predicateType': 'https://slsa.dev/provenance/v1'
}
}
],
annotations: {
'com.github.package.type': actionPackageReferrerTagAnnotationValue,
'org.opencontainers.image.created': created.toISOString()
}
};
return manifest;
}
// Calculate the SHA256 digest of a given manifest.
// This should match the digest which the GitHub container registry calculates for this manifest.
function sha256Digest(manifest) {
@@ -107791,17 +107859,21 @@ function sha256Digest(manifest) {
const hexHash = hash.digest('hex');
return `sha256:${hexHash}`;
}
function sizeInBytes(manifest) {
const data = JSON.stringify(manifest);
return Buffer.byteLength(data, 'utf8');
}
function createConfigLayer() {
const configLayer = {
mediaType: 'application/vnd.oci.empty.v1+json',
size: 2,
digest: 'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a'
mediaType: ociEmptyMediaType,
size: emptyConfigSize,
digest: emptyConfigSha
};
return configLayer;
}
function createZipLayer(zipFile, repository, version) {
const zipLayer = {
mediaType: 'application/vnd.github.actions.package.layer.v1.zip',
mediaType: actionsPackageZipLayerMediaType,
size: zipFile.size,
digest: zipFile.sha256,
annotations: {
@@ -107812,7 +107884,7 @@ function createZipLayer(zipFile, repository, version) {
}
function createTarLayer(tarFile, repository, version) {
const tarLayer = {
mediaType: 'application/vnd.github.actions.package.layer.v1.tar+gzip',
mediaType: actionsPackageTarLayerMediaType,
size: tarFile.size,
digest: tarFile.sha256,
annotations: {
+2 -2
View File
@@ -11,7 +11,7 @@ export async function publishOCIArtifact(
semver: string,
zipFile: FileMetadata,
tarFile: FileMetadata,
manifest: ociContainer.Manifest
manifest: ociContainer.OCIImageManifest
): Promise<{ packageURL: URL; publishedDigest: string }> {
const b64Token = Buffer.from(token).toString('base64')
@@ -81,7 +81,7 @@ export async function publishOCIArtifact(
}
async function uploadLayer(
layer: ociContainer.Layer,
layer: ociContainer.Descriptor,
file: FileMetadata,
registryURL: URL,
checkBlobEndpoint: string,
+130 -23
View File
@@ -1,19 +1,46 @@
import { FileMetadata } from './fs-helper'
import * as crypto from 'crypto'
export interface Manifest {
const imageIndexMediaType = 'application/vnd.oci.image.index.v1+json'
const imageManifestMediaType = 'application/vnd.oci.image.manifest.v1+json'
const actionsPackageMediaType = 'application/vnd.github.actions.package.v1+json'
const actionsPackageTarLayerMediaType =
'application/vnd.github.actions.package.layer.v1.tar+gzip'
const actionsPackageZipLayerMediaType =
'application/vnd.github.actions.package.layer.v1.zip'
const sigstoreBundleMediaType = 'application/vnd.dev.sigstore.bundle.v0.3+json'
const ociEmptyMediaType = 'application/vnd.oci.empty.v1+json'
const actionPackageAnnotationValue = 'actions_oci_pkg'
const actionPackageAttestationAnnotationValue = 'actions_oci_pkg_attestation'
const actionPackageReferrerTagAnnotationValue = 'actions_oci_pkg_referrer_tag'
const emptyConfigSize = 2
const emptyConfigSha =
'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a'
export interface OCIImageManifest {
schemaVersion: number
mediaType: string
artifactType: string
config: Layer
layers: Layer[]
config: Descriptor
layers: Descriptor[]
subject?: Descriptor
annotations: { [key: string]: string }
}
export interface Layer {
export interface OCIIndexManifest {
schemaVersion: number
mediaType: string
manifests: Descriptor[]
annotations: { [key: string]: string }
}
export interface Descriptor {
mediaType: string
size: number
digest: string
artifactType?: string
annotations?: { [key: string]: string }
}
@@ -26,24 +53,24 @@ export function createActionPackageManifest(
ownerId: string,
sourceCommit: string,
version: string,
created: Date
): Manifest {
created: Date = new Date()
): OCIImageManifest {
const configLayer = createConfigLayer()
const sanitizedRepo = sanitizeRepository(repository)
const tarLayer = createTarLayer(tarFile, sanitizedRepo, version)
const zipLayer = createZipLayer(zipFile, sanitizedRepo, version)
const manifest: Manifest = {
const manifest: OCIImageManifest = {
schemaVersion: 2,
mediaType: 'application/vnd.oci.image.manifest.v1+json',
artifactType: 'application/vnd.github.actions.package.v1+json',
mediaType: imageManifestMediaType,
artifactType: actionsPackageMediaType,
config: configLayer,
layers: [configLayer, tarLayer, zipLayer],
annotations: {
'org.opencontainers.image.created': created.toISOString(),
'action.tar.gz.digest': tarFile.sha256,
'action.zip.digest': zipFile.sha256,
'com.github.package.type': 'actions_oci_pkg',
'com.github.package.type': actionPackageAnnotationValue,
'com.github.package.version': version,
'com.github.source.repo.id': repoId,
'com.github.source.repo.owner.id': ownerId,
@@ -54,9 +81,83 @@ export function createActionPackageManifest(
return manifest
}
export function createSigstoreAttestationManifest(
bundleSize: number,
bundleDigest: string,
subjectSize: number,
subjectDigest: string,
created: Date = new Date()
): OCIImageManifest {
const configLayer = createConfigLayer()
const sigstoreAttestationLayer: Descriptor = {
mediaType: sigstoreBundleMediaType,
size: bundleSize,
digest: bundleDigest
}
const subject: Descriptor = {
mediaType: imageManifestMediaType,
size: subjectSize,
digest: subjectDigest
}
const manifest: OCIImageManifest = {
schemaVersion: 2,
mediaType: imageManifestMediaType,
artifactType: sigstoreBundleMediaType,
config: configLayer,
layers: [sigstoreAttestationLayer],
subject,
annotations: {
'dev.sigstore.bundle.content': 'dsse-envelope',
'dev.sigstore.bundle.predicateType': 'https://slsa.dev/provenance/v1',
'com.github.package.type': actionPackageAttestationAnnotationValue,
'org.opencontainers.image.created': created.toISOString()
}
}
return manifest
}
export function createReferrerTagManifest(
attestationDigest: string,
attestationSize: number,
attestationCreated: Date,
created: Date = new Date()
): OCIIndexManifest {
const manifest: OCIIndexManifest = {
schemaVersion: 2,
mediaType: imageIndexMediaType,
manifests: [
{
mediaType: imageManifestMediaType,
artifactType: sigstoreBundleMediaType,
size: attestationSize,
digest: attestationDigest,
annotations: {
'com.github.package.type': actionPackageAttestationAnnotationValue,
'org.opencontainers.image.created': attestationCreated.toISOString(),
'dev.sigstore.bundle.content': 'dsse-envelope',
'dev.sigstore.bundle.predicateType': 'https://slsa.dev/provenance/v1'
}
}
],
annotations: {
'com.github.package.type': actionPackageReferrerTagAnnotationValue,
'org.opencontainers.image.created': created.toISOString()
}
}
return manifest
}
// Calculate the SHA256 digest of a given manifest.
// This should match the digest which the GitHub container registry calculates for this manifest.
export function sha256Digest(manifest: Manifest): string {
export function sha256Digest(
manifest: OCIImageManifest | OCIIndexManifest
): string {
const data = JSON.stringify(manifest)
const buffer = Buffer.from(data, 'utf8')
const hash = crypto.createHash('sha256')
@@ -65,12 +166,18 @@ export function sha256Digest(manifest: Manifest): string {
return `sha256:${hexHash}`
}
function createConfigLayer(): Layer {
const configLayer: Layer = {
mediaType: 'application/vnd.oci.empty.v1+json',
size: 2,
digest:
'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a'
export function sizeInBytes(
manifest: OCIImageManifest | OCIIndexManifest
): number {
const data = JSON.stringify(manifest)
return Buffer.byteLength(data, 'utf8')
}
function createConfigLayer(): Descriptor {
const configLayer: Descriptor = {
mediaType: ociEmptyMediaType,
size: emptyConfigSize,
digest: emptyConfigSha
}
return configLayer
@@ -80,9 +187,9 @@ function createZipLayer(
zipFile: FileMetadata,
repository: string,
version: string
): Layer {
const zipLayer: Layer = {
mediaType: 'application/vnd.github.actions.package.layer.v1.zip',
): Descriptor {
const zipLayer: Descriptor = {
mediaType: actionsPackageZipLayerMediaType,
size: zipFile.size,
digest: zipFile.sha256,
annotations: {
@@ -97,9 +204,9 @@ function createTarLayer(
tarFile: FileMetadata,
repository: string,
version: string
): Layer {
const tarLayer: Layer = {
mediaType: 'application/vnd.github.actions.package.layer.v1.tar+gzip',
): Descriptor {
const tarLayer: Descriptor = {
mediaType: actionsPackageTarLayerMediaType,
size: tarFile.size,
digest: tarFile.sha256,
annotations: {