Merge pull request #2196 from actions/update-attest-storage-record-params

Refactor attest package createStorageRecord function params
This commit is contained in:
Meredith Lancaster
2025-12-09 11:49:14 -08:00
committed by GitHub
3 changed files with 103 additions and 99 deletions
+30 -34
View File
@@ -189,7 +189,7 @@ async function run() {
// repository write permissions. // repository write permissions.
const ghToken = core.getInput('gh-token'); const ghToken = core.getInput('gh-token');
const record = await createStorageRecord({ const record = await createStorageRecord(
artifactOptions: { artifactOptions: {
name: 'my-artifact-name', name: 'my-artifact-name',
digest: { 'sha256': '36ab4667...'}, digest: { 'sha256': '36ab4667...'},
@@ -199,7 +199,7 @@ async function run() {
registryUrl: "https://my-fave-pkg-registry.com" registryUrl: "https://my-fave-pkg-registry.com"
}, },
token: ghToken token: ghToken
}); );
console.log(record); console.log(record);
} }
@@ -210,39 +210,35 @@ run();
The `createStorageRecord` function supports the following options: The `createStorageRecord` function supports the following options:
```typescript ```typescript
export type StorageRecordOptions = { // Artifact details to associate the record with
// Includes details about the attested artifact export type ArtifactOptions = {
artifactOptions: { // The name of the artifact
// The name of the artifact name: string
name: string // The digest of the artifact
// The digest of the artifact digest: string
digest: string // The version of the artifact
// The version of the artifact version?: string
version?: string // The status of the artifact
// The status of the artifact status?: string
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
}
} }
// Includes details about the package registry the artifact was published to
export type 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.
// The number of times to retry the request.
retryAttempts?: number
// HTTP headers to include in request to Artifact Metadata API.
headers?: RequestHeaders
``` ```
## Sigstore Instance ## Sigstore Instance
@@ -6,17 +6,13 @@ describe('createStorageRecord', () => {
const token = 'token' const token = 'token'
const headers = {'X-GitHub-Foo': 'true'} const headers = {'X-GitHub-Foo': 'true'}
const options = { const artifactOptions = {
artifactOptions: { name: 'my-lib',
name: 'my-lib', version: '1.0.0',
version: '1.0.0', digest: `sha256:${'a'.repeat(64)}`
digest: `sha256:${'a'.repeat(64)}` }
}, const packageRegistryOptions = {
packageRegistryOptions: { registryUrl: 'https://my-registry.org'
registryUrl: 'https://my-registry.org'
},
token,
writeOptions: {headers}
} }
const mockAgent = new MockAgent() const mockAgent = new MockAgent()
@@ -52,7 +48,15 @@ describe('createStorageRecord', () => {
}) })
it('persists the storage record', async () => { it('persists the storage record', async () => {
await expect(createStorageRecord(options)).resolves.toEqual([123, 456]) await expect(
createStorageRecord(
artifactOptions,
packageRegistryOptions,
token,
undefined,
headers
)
).resolves.toEqual([123, 456])
}) })
}) })
@@ -76,10 +80,13 @@ describe('createStorageRecord', () => {
it('throws an error', async () => { it('throws an error', async () => {
await expect( await expect(
createStorageRecord({ createStorageRecord(
...options, artifactOptions,
writeOptions: {retry: 0} packageRegistryOptions,
}) token,
0,
headers
)
).rejects.toThrow(/oops/) ).rejects.toThrow(/oops/)
}) })
}) })
@@ -94,8 +101,8 @@ describe('createStorageRecord', () => {
method: 'POST', method: 'POST',
headers: {authorization: `token ${token}`}, headers: {authorization: `token ${token}`},
body: JSON.stringify({ body: JSON.stringify({
...options.artifactOptions, ...artifactOptions,
registry_url: options.packageRegistryOptions.registryUrl registry_url: packageRegistryOptions.registryUrl
}) })
}) })
.reply(500, 'oops') .reply(500, 'oops')
@@ -107,8 +114,8 @@ describe('createStorageRecord', () => {
method: 'POST', method: 'POST',
headers: {authorization: `token ${token}`}, headers: {authorization: `token ${token}`},
body: JSON.stringify({ body: JSON.stringify({
...options.artifactOptions, ...artifactOptions,
registry_url: options.packageRegistryOptions.registryUrl registry_url: packageRegistryOptions.registryUrl
}) })
}) })
.reply(200, {storage_records: [{id: 123}, {id: 456}]}) .reply(200, {storage_records: [{id: 123}, {id: 456}]})
@@ -117,10 +124,13 @@ describe('createStorageRecord', () => {
it('persists the storage record', async () => { it('persists the storage record', async () => {
await expect( await expect(
createStorageRecord({ createStorageRecord(
...options, artifactOptions,
writeOptions: {} packageRegistryOptions,
}) token,
undefined,
headers
)
).resolves.toEqual([123, 456]) ).resolves.toEqual([123, 456])
}) })
}) })
+39 -41
View File
@@ -9,57 +9,54 @@ const DEFAULT_RETRY_COUNT = 5
/** /**
* Options for creating a storage record for an attested artifact. * Options for creating a storage record for an attested artifact.
*/ */
export type StorageRecordOptions = { export type ArtifactOptions = {
// Includes details about the attested artifact // Includes details about the attested artifact
artifactOptions: { // The name of the artifact
// The name of the artifact name: string
name: string // The digest of the artifact
// The digest of the artifact digest: string
digest: string // The version of the artifact
// The version of the artifact version?: string
version?: string // The status of the artifact
// The status of the artifact status?: string
status?: string }
} // Includes details about the package registry the artifact was published to
// Includes details about the package registry the artifact was published to export type PackageRegistryOptions = {
packageRegistryOptions: { // The URL of the package registry
// The URL of the package registry registryUrl: string
registryUrl: string // The URL of the artifact in the package registry
// The URL of the artifact in the package registry artifactUrl?: string
artifactUrl?: string // The package registry repository the artifact was published to.
// The package registry repository the artifact was published to. repo?: string
repo?: string // The path of the artifact in the package registry repository.
// The path of the artifact in the package registry repository. path?: string
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
}
} }
/** /**
* Writes a storage record on behalf of an artifact that has been attested * Writes a storage record on behalf of an artifact that has been attested
* @param StorageRecordOptions - parameters for the storage record API request. * @param artifactOptions - parameters for the storage record API request.
* @param packageRegistryOptions - parameters for the package registry API request.
* @param token - GitHub token used to authenticate the request.
* @param retryAttempts - The number of retries to attempt if the request fails.
* @param headers - Additional headers to include in the request.
*
* @returns The ID of the storage record. * @returns The ID of the storage record.
* @throws Error if the storage record fails to persist. * @throws Error if the storage record fails to persist.
*/ */
export async function createStorageRecord( export async function createStorageRecord(
options: StorageRecordOptions artifactOptions: ArtifactOptions,
packageRegistryOptions: PackageRegistryOptions,
token: string,
retryAttempts?: number,
headers?: RequestHeaders
): Promise<number[]> { ): Promise<number[]> {
const retries = options.writeOptions.retry ?? DEFAULT_RETRY_COUNT const retries = retryAttempts ?? DEFAULT_RETRY_COUNT
const octokit = github.getOctokit(options.token, {retry: {retries}}, retry) const octokit = github.getOctokit(token, {retry: {retries}}, retry)
try { try {
const response = await octokit.request(CREATE_STORAGE_RECORD_REQUEST, { const response = await octokit.request(CREATE_STORAGE_RECORD_REQUEST, {
owner: github.context.repo.owner, owner: github.context.repo.owner,
headers: options.writeOptions.headers, headers,
...buildRequestParams(options) ...buildRequestParams(artifactOptions, packageRegistryOptions)
}) })
const data = const data =
@@ -75,11 +72,12 @@ export async function createStorageRecord(
} }
function buildRequestParams( function buildRequestParams(
options: StorageRecordOptions artifactOptions: ArtifactOptions,
packageRegistryOptions: PackageRegistryOptions
): Record<string, unknown> { ): Record<string, unknown> {
const {registryUrl, artifactUrl, ...rest} = options.packageRegistryOptions const {registryUrl, artifactUrl, ...rest} = packageRegistryOptions
return { return {
...options.artifactOptions, ...artifactOptions,
registry_url: registryUrl, registry_url: registryUrl,
artifact_url: artifactUrl, artifact_url: artifactUrl,
...rest ...rest