diff --git a/packages/attest/README.md b/packages/attest/README.md index b157d5a1..6331d86a 100644 --- a/packages/attest/README.md +++ b/packages/attest/README.md @@ -175,10 +175,71 @@ specification](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigst ### createStorageRecord -The `createStorageRecord` function accepts parameters defining artifact -and package registry details and creates a storage record on behalf of the artifact. -The storage record contains metadata about where the artifact is stored on a given -package registry. +The `createStorageRecord` function creates an +[artifact metadata storage record](https://docs.github.com/en/rest/orgs/artifact-metadata?apiVersion=2022-11-28#create-artifact-metadata-storage-record) +on behalf of an attested artifact. It accepts parameters defining artifact +and package registry details. The storage record contains metadata about where the artifact is stored on a given package registry. + +```js +const { createStorageRecord } = require('@actions/attest'); +const core = require('@actions/core'); + +async function run() { + // In order to persist attestations to the repo, this should be a token with + // repository write permissions. + const ghToken = core.getInput('gh-token'); + + const record = await createStorageRecord({ + name: 'my-artifact-name', + digest: { 'sha256': '36ab4667...'}, + version: "v1.0.0", + registry_url: "https://my-fave-pkg-registry.com", + token: ghToken + }); + + console.log(record); +} + +run(); +``` + +The `createStorageRecord` function supports the following options: + +```typescript +export type StorageRecordOptions = { + // Includes details about the attested artifact + artifactOptions: { + // The name of the artifact + name: string + // The digest of the artifact + digest: string + // The version of the artifact + version?: string + // The status of the artifact + status?: string + }, + // Includes details about the package registry the artifact was published to + packageRegistryOptions: { + // The URL of the package registry + registryUrl: string + // The URL of the artifact in the package registry + artifactUrl?: string + // The package registry repository the artifact was published to. + repo?: string + // The path of the artifact in the package registry repository. + path?: string + }, + // GitHub token for writing attestations. + token: string + // Optional parameters for the write operation. + writeOptions: { + // The number of times to retry the request. + retry?: number + // HTTP headers to include in request to Artifact Metadata API. + headers?: RequestHeaders + } +} +``` ## Sigstore Instance diff --git a/packages/attest/__tests__/artifactMetadata.test.ts b/packages/attest/__tests__/artifactMetadata.test.ts index 192978af..ab63c3f1 100644 --- a/packages/attest/__tests__/artifactMetadata.test.ts +++ b/packages/attest/__tests__/artifactMetadata.test.ts @@ -15,7 +15,6 @@ describe('createStorageRecord', () => { registryUrl: 'https://my-registry.org', } - const mockAgent = new MockAgent() setGlobalDispatcher(mockAgent) @@ -50,7 +49,12 @@ describe('createStorageRecord', () => { it('persists the storage record', async () => { await expect( - createStorageRecord(artifactParams, registryParams, token, {headers}) + createStorageRecord({ + artifactOptions: artifactParams, + packageRegistryOptions: registryParams, + token, + writeOptions: {headers}, + }) ).resolves.toEqual(['123', '456']) }) }) @@ -75,7 +79,12 @@ describe('createStorageRecord', () => { it('throws an error', async () => { await expect( - createStorageRecord(artifactParams, registryParams, token, {retry: 0}) + createStorageRecord({ + artifactOptions: artifactParams, + packageRegistryOptions: registryParams, + token, + writeOptions: {retry: 0}, + }) ).rejects.toThrow(/oops/) }) }) @@ -106,7 +115,12 @@ describe('createStorageRecord', () => { }) it('persists the attestation', async () => { - await expect(createStorageRecord(artifactParams, registryParams, token)).resolves.toEqual(['123', '456']) + await expect(createStorageRecord({ + artifactOptions: artifactParams, + packageRegistryOptions: registryParams, + token, + writeOptions: {}, + })).resolves.toEqual(['123', '456']) }) }) }) diff --git a/packages/attest/src/artifactMetadata.ts b/packages/attest/src/artifactMetadata.ts index bbe267b0..5e4bf10f 100644 --- a/packages/attest/src/artifactMetadata.ts +++ b/packages/attest/src/artifactMetadata.ts @@ -5,23 +5,41 @@ import {RequestHeaders} from '@octokit/types' const CREATE_STORAGE_RECORD_REQUEST = 'POST /orgs/{owner}/artifacts/metadata/storage-record' const DEFAULT_RETRY_COUNT = 5 -export type ArtifactParams = { - name: string - digest: string - version?: string - status?: string -} - -export type PackageRegistryParams = { - registryUrl: string - artifactUrl?: string - repo?: string - path?: string -} - -export type WriteOptions = { - retry?: number - headers?: RequestHeaders +/** + * Options for creating a storage record for an attested artifact. + */ +export type StorageRecordOptions = { + // Includes details about the attested artifact + artifactOptions: { + // The name of the artifact + name: string + // The digest of the artifact + digest: string + // The version of the artifact + version?: string + // The status of the artifact + status?: string + }, + // Includes details about the package registry the artifact was published to + packageRegistryOptions: { + // The URL of the package registry + registryUrl: string + // The URL of the artifact in the package registry + artifactUrl?: string + // The package registry repository the artifact was published to. + repo?: string + // The path of the artifact in the package registry repository. + path?: string + }, + // GitHub token for writing attestations. + token: string + // Optional parameters for the write operation. + writeOptions: { + // The number of times to retry the request. + retry?: number + // HTTP headers to include in request to Artifact Metadata API. + headers?: RequestHeaders + } } /** @@ -33,20 +51,15 @@ export type WriteOptions = { * @returns The ID of the storage record. * @throws Error if the storage record fails to persist. */ -export async function createStorageRecord( - artifactParams: ArtifactParams, - packageRegistryParams: PackageRegistryParams, - token: string, - options: WriteOptions = {} -): Promise> { - const retries = options.retry ?? DEFAULT_RETRY_COUNT - const octokit = github.getOctokit(token, {retry: {retries}}, retry) +export async function createStorageRecord(options: StorageRecordOptions): Promise> { + const retries = options.writeOptions.retry ?? DEFAULT_RETRY_COUNT + const octokit = github.getOctokit(options.token, {retry: {retries}}, retry) try { const response = await octokit.request(CREATE_STORAGE_RECORD_REQUEST, { owner: github.context.repo.owner, - headers: options.headers, - ...buildRequestParams(artifactParams, packageRegistryParams), + headers: options.writeOptions.headers, + ...buildRequestParams(options.artifactOptions, options.packageRegistryOptions), }) const data =