Compare commits

..

2 Commits

Author SHA1 Message Date
Brian DeHamer 539e4021af wip
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2025-12-09 11:08:20 -08:00
Brian DeHamer ee65b3addf testing
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2025-12-09 10:56:50 -08:00
13 changed files with 116466 additions and 91920 deletions
+3 -3
View File
@@ -28,11 +28,11 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v6.0.1
uses: actions/checkout@v6.0.0
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v6.1.0
uses: actions/setup-node@v6.0.0
with:
node-version-file: .node-version
cache: npm
@@ -60,7 +60,7 @@ jobs:
- if: ${{ failure() && steps.diff.outcome == 'failure' }}
name: Upload Artifact
id: upload
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: dist
path: dist/
+3 -3
View File
@@ -21,11 +21,11 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.1
uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 # v5.0.1
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version-file: .node-version
cache: npm
@@ -58,7 +58,7 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.1
uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 # v5.0.1
- name: Calculate subject digest
id: subject
env:
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v6.0.1
uses: actions/checkout@v6.0.0
- name: Initialize CodeQL
id: initialize
+6 -20
View File
@@ -52,13 +52,11 @@ attest:
permissions:
id-token: write
attestations: write
artifact-metadata: write
```
The `id-token` permission gives the action the ability to mint the OIDC token
necessary to request a Sigstore signing certificate. The `attestations`
permission is necessary to persist the attestation. The `artifact-metadata`
permission is necessary to create the artifact storage record.
permission is necessary to persist the attestation.
1. Add the following to your workflow after your artifact has been built:
@@ -120,12 +118,6 @@ See [action.yml](action.yml)
# the "subject-digest" parameter be specified. Defaults to false.
push-to-registry:
# Whether to create a storage record for the artifact.
# Requires that push-to-registry is set to true.
# Requires that the "subject-name" parameter specify the fully-qualified
# image name. Defaults to true.
create-storage-record:
# Whether to attach a list of generated attestations to the workflow run
# summary page. Defaults to true.
show-summary:
@@ -139,12 +131,11 @@ See [action.yml](action.yml)
<!-- markdownlint-disable MD013 -->
| Name | Description | Example |
| ------------------- | -------------------------------------------------------------- | ------------------------------------------------ |
| `attestation-id` | GitHub ID for the attestation | `123456` |
| `attestation-url` | URL for the attestation summary | `https://github.com/foo/bar/attestations/123456` |
| `bundle-path` | Absolute path to the file containing the generated attestation | `/tmp/attestation.json` |
| `storage-record-ids` | GitHub IDs for the storage records | `987654` |
| Name | Description | Example |
| ----------------- | -------------------------------------------------------------- | ------------------------------------------------ |
| `attestation-id` | GitHub ID for the attestation | `123456` |
| `attestation-url` | URL for the attestation summary | `https://github.com/foo/bar/attestations/123456` |
| `bundle-path` | Absolute path to the file containing the generated attestation | `/tmp/attestation.json` |
<!-- markdownlint-enable MD013 -->
@@ -278,10 +269,6 @@ fully-qualified image name (e.g. "ghcr.io/user/app" or
"acme.azurecr.io/user/app"). Do NOT include a tag as part of the image name --
the specific image being attested is identified by the supplied digest.
If the `push-to-registry` option is set to true, the Action will also
emit an Artifact Metadata Storage Record. If you do not want to emit a
storage record, set `create-storage-record` to `false`.
> **NOTE**: When pushing to Docker Hub, please use "docker.io" as the registry
> portion of the image name.
@@ -300,7 +287,6 @@ jobs:
packages: write
contents: read
attestations: write
artifact-metadata: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
+11 -96
View File
@@ -9,8 +9,6 @@ import * as core from '@actions/core'
import * as github from '@actions/github'
import { mockFulcio, mockRekor, mockTSA } from '@sigstore/mock'
import * as oci from '@sigstore/oci'
import * as attest from '@actions/attest'
import * as localAttest from '../src/attest'
import fs from 'fs/promises'
import nock from 'nock'
import os from 'os'
@@ -21,7 +19,6 @@ import * as main from '../src/main'
// Mock the GitHub Actions core library
const infoMock = jest.spyOn(core, 'info')
const warningMock = jest.spyOn(core, 'warning')
const startGroupMock = jest.spyOn(core, 'startGroup')
const setOutputMock = jest.spyOn(core, 'setOutput')
const setFailedMock = jest.spyOn(core, 'setFailed')
@@ -30,7 +27,7 @@ const setFailedMock = jest.spyOn(core, 'setFailed')
setFailedMock.mockImplementation(() => {})
const summaryWriteMock = jest.spyOn(core.summary, 'write')
summaryWriteMock.mockResolvedValue(core.summary)
summaryWriteMock.mockImplementation(async () => Promise.resolve(core.summary))
// Mock the action's main function
const runMock = jest.spyOn(main, 'run')
@@ -48,7 +45,6 @@ const defaultInputs: main.RunInputs = {
subjectPath: '',
subjectChecksums: '',
pushToRegistry: false,
createStorageRecord: true,
showSummary: true,
githubToken: '',
privateSigning: false
@@ -70,14 +66,13 @@ describe('action', () => {
'base64'
)}.}`
const subjectName = 'ghcr.io/registry/foo/bar'
const subjectName = 'registry/foo/bar'
const subjectDigest =
'sha256:7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
const predicate = '{}'
const predicateType = 'https://in-toto.io/attestation/release/v0.1'
const attestationID = '1234567890'
const storageRecordID = 987654321
beforeEach(() => {
jest.clearAllMocks()
@@ -87,21 +82,14 @@ describe('action', () => {
.query({ audience: 'sigstore' })
.reply(200, { value: oidcToken })
const pool = mockAgent.get('https://api.github.com')
pool
mockAgent
.get('https://api.github.com')
.intercept({
path: /^\/repos\/.*\/.*\/attestations$/,
method: 'post'
})
.reply(201, { id: attestationID })
pool
.intercept({
path: /^\/orgs\/.*\/artifacts\/metadata\/storage-record$/,
method: 'post'
})
.reply(200, { storage_records: [{ id: storageRecordID }] })
process.env = {
...originalEnv,
ACTIONS_ID_TOKEN_REQUEST_URL: tokenURL,
@@ -231,9 +219,6 @@ describe('action', () => {
describe('when the repository is public', () => {
const getRegCredsSpy = jest.spyOn(oci, 'getRegistryCredentials')
const attachArtifactSpy = jest.spyOn(oci, 'attachArtifactToImage')
const repoOwnerIsOrgSpy = jest.spyOn(localAttest, 'repoOwnerIsOrg')
const createStorageRecordSpy = jest.spyOn(attest, 'createStorageRecord')
const createAttestationSpy = jest.spyOn(localAttest, 'createAttestation')
const inputs: main.RunInputs = {
...defaultInputs,
@@ -262,12 +247,13 @@ describe('action', () => {
username: 'username',
password: 'password'
}))
attachArtifactSpy.mockResolvedValue({
digest: 'sha256:123456',
mediaType: 'application/vnd.cncf.notary.v2',
size: 123456
})
repoOwnerIsOrgSpy.mockResolvedValue(true)
attachArtifactSpy.mockImplementation(async () =>
Promise.resolve({
digest: 'sha256:123456',
mediaType: 'application/vnd.cncf.notary.v2',
size: 123456
})
)
})
it('invokes the action w/o error', async () => {
@@ -277,10 +263,6 @@ describe('action', () => {
expect(setFailedMock).not.toHaveBeenCalled()
expect(getRegCredsSpy).toHaveBeenCalledWith(subjectName)
expect(attachArtifactSpy).toHaveBeenCalled()
expect(createAttestationSpy).toHaveBeenCalled()
expect(repoOwnerIsOrgSpy).toHaveBeenCalled()
expect(createStorageRecordSpy).toHaveBeenCalled()
expect(warningMock).not.toHaveBeenCalled()
expect(infoMock).toHaveBeenNthCalledWith(
1,
expect.stringMatching(
@@ -311,14 +293,6 @@ describe('action', () => {
6,
expect.stringMatching(attestationID)
)
expect(infoMock).toHaveBeenNthCalledWith(
9,
expect.stringMatching('Storage record created')
)
expect(infoMock).toHaveBeenNthCalledWith(
10,
expect.stringMatching('Storage record IDs: 987654321')
)
expect(setOutputMock).toHaveBeenNthCalledWith(
1,
'bundle-path',
@@ -334,65 +308,6 @@ describe('action', () => {
'attestation-url',
expect.stringContaining(`foo/bar/attestations/${attestationID}`)
)
expect(setOutputMock).toHaveBeenNthCalledWith(
4,
'storage-record-ids',
expect.stringMatching(storageRecordID.toString())
)
expect(setFailedMock).not.toHaveBeenCalled()
})
it('catches error when storage record creation fails and continues', async () => {
// Mock the createStorageRecord function and throw an error
createStorageRecordSpy.mockRejectedValueOnce(
new Error('Failed to persist storage record: Not Found')
)
await main.run(inputs)
expect(runMock).toHaveReturned()
expect(createAttestationSpy).toHaveBeenCalled()
expect(repoOwnerIsOrgSpy).toHaveBeenCalled()
expect(createStorageRecordSpy).toHaveBeenCalled()
expect(setFailedMock).not.toHaveBeenCalled()
expect(warningMock).toHaveBeenNthCalledWith(
1,
expect.stringMatching('Failed to create storage record')
)
})
it('does not create a storage record when the repo is owned by a user', async () => {
repoOwnerIsOrgSpy.mockResolvedValueOnce(false)
await main.run(inputs)
expect(runMock).toHaveReturned()
expect(setFailedMock).not.toHaveBeenCalled()
expect(getRegCredsSpy).toHaveBeenCalledWith(subjectName)
expect(attachArtifactSpy).toHaveBeenCalled()
expect(createAttestationSpy).toHaveBeenCalled()
expect(repoOwnerIsOrgSpy).toHaveBeenCalled()
expect(createStorageRecordSpy).not.toHaveBeenCalled()
expect(warningMock).not.toHaveBeenCalled()
expect(infoMock).toHaveBeenCalledWith(
expect.stringMatching(
`Attestation created for ${subjectName}@${subjectDigest}`
)
)
expect(infoMock).not.toHaveBeenCalledWith(
expect.stringMatching('Storage record created')
)
expect(infoMock).not.toHaveBeenCalledWith(
expect.stringMatching('Storage record IDs: 987654321')
)
expect(setOutputMock).toHaveBeenCalledWith(
'attestation-id',
expect.stringMatching(attestationID)
)
expect(setOutputMock).not.toHaveBeenCalledWith(
'storage-record-ids',
expect.stringMatching(storageRecordID.toString())
)
expect(setFailedMock).not.toHaveBeenCalled()
})
})
-8
View File
@@ -53,12 +53,6 @@ inputs:
the "subject-digest" parameter be specified. Defaults to false.
default: false
required: false
create-storage-record:
description: >
Whether to create a storage record for the artifact.
Requires that push-to-registry is set to true. Defaults to true.
default: true
required: false
show-summary:
description: >
Whether to attach a list of generated attestations to the workflow run
@@ -77,8 +71,6 @@ outputs:
description: 'The ID of the attestation.'
attestation-url:
description: 'The URL for the attestation summary.'
storage-record-ids:
description: 'The IDs of the storage records created for the artifact.'
runs:
using: node24
Generated Vendored
+299
View File
@@ -0,0 +1,299 @@
"use strict";
exports.id = 717;
exports.ids = [717];
exports.modules = {
/***/ 4717:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (/* binding */ pMap)
/* harmony export */ });
/* unused harmony exports pMapIterable, pMapSkip */
async function pMap(
iterable,
mapper,
{
concurrency = Number.POSITIVE_INFINITY,
stopOnError = true,
signal,
} = {},
) {
return new Promise((resolve_, reject_) => {
if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`);
}
if (typeof mapper !== 'function') {
throw new TypeError('Mapper function is required');
}
if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`);
}
const result = [];
const errors = [];
const skippedIndexesMap = new Map();
let isRejected = false;
let isResolved = false;
let isIterableDone = false;
let resolvingCount = 0;
let currentIndex = 0;
const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]();
const signalListener = () => {
reject(signal.reason);
};
const cleanup = () => {
signal?.removeEventListener('abort', signalListener);
};
const resolve = value => {
resolve_(value);
cleanup();
};
const reject = reason => {
isRejected = true;
isResolved = true;
reject_(reason);
cleanup();
};
if (signal) {
if (signal.aborted) {
reject(signal.reason);
}
signal.addEventListener('abort', signalListener, {once: true});
}
const next = async () => {
if (isResolved) {
return;
}
const nextItem = await iterator.next();
const index = currentIndex;
currentIndex++;
// Note: `iterator.next()` can be called many times in parallel.
// This can cause multiple calls to this `next()` function to
// receive a `nextItem` with `done === true`.
// The shutdown logic that rejects/resolves must be protected
// so it runs only one time as the `skippedIndex` logic is
// non-idempotent.
if (nextItem.done) {
isIterableDone = true;
if (resolvingCount === 0 && !isResolved) {
if (!stopOnError && errors.length > 0) {
reject(new AggregateError(errors)); // eslint-disable-line unicorn/error-message
return;
}
isResolved = true;
if (skippedIndexesMap.size === 0) {
resolve(result);
return;
}
const pureResult = [];
// Support multiple `pMapSkip`'s.
for (const [index, value] of result.entries()) {
if (skippedIndexesMap.get(index) === pMapSkip) {
continue;
}
pureResult.push(value);
}
resolve(pureResult);
}
return;
}
resolvingCount++;
// Intentionally detached
(async () => {
try {
const element = await nextItem.value;
if (isResolved) {
return;
}
const value = await mapper(element, index);
// Use Map to stage the index of the element.
if (value === pMapSkip) {
skippedIndexesMap.set(index, value);
}
result[index] = value;
resolvingCount--;
await next();
} catch (error) {
if (stopOnError) {
reject(error);
} else {
errors.push(error);
resolvingCount--;
// In that case we can't really continue regardless of `stopOnError` state
// since an iterable is likely to continue throwing after it throws once.
// If we continue calling `next()` indefinitely we will likely end up
// in an infinite loop of failed iteration.
try {
await next();
} catch (error) {
reject(error);
}
}
}
})();
};
// Create the concurrent runners in a detached (non-awaited)
// promise. We need this so we can await the `next()` calls
// to stop creating runners before hitting the concurrency limit
// if the iterable has already been marked as done.
// NOTE: We *must* do this for async iterators otherwise we'll spin up
// infinite `next()` calls by default and never start the event loop.
(async () => {
for (let index = 0; index < concurrency; index++) {
try {
// eslint-disable-next-line no-await-in-loop
await next();
} catch (error) {
reject(error);
break;
}
if (isIterableDone || isRejected) {
break;
}
}
})();
});
}
function pMapIterable(
iterable,
mapper,
{
concurrency = Number.POSITIVE_INFINITY,
backpressure = concurrency,
} = {},
) {
if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`);
}
if (typeof mapper !== 'function') {
throw new TypeError('Mapper function is required');
}
if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`);
}
if (!((Number.isSafeInteger(backpressure) && backpressure >= concurrency) || backpressure === Number.POSITIVE_INFINITY)) {
throw new TypeError(`Expected \`backpressure\` to be an integer from \`concurrency\` (${concurrency}) and up or \`Infinity\`, got \`${backpressure}\` (${typeof backpressure})`);
}
return {
async * [Symbol.asyncIterator]() {
const iterator = iterable[Symbol.asyncIterator] === undefined ? iterable[Symbol.iterator]() : iterable[Symbol.asyncIterator]();
const promises = [];
let runningMappersCount = 0;
let isDone = false;
let index = 0;
function trySpawn() {
if (isDone || !(runningMappersCount < concurrency && promises.length < backpressure)) {
return;
}
const promise = (async () => {
const {done, value} = await iterator.next();
if (done) {
return {done: true};
}
runningMappersCount++;
// Spawn if still below concurrency and backpressure limit
trySpawn();
try {
const returnValue = await mapper(await value, index++);
runningMappersCount--;
if (returnValue === pMapSkip) {
const index = promises.indexOf(promise);
if (index > 0) {
promises.splice(index, 1);
}
}
// Spawn if still below backpressure limit and just dropped below concurrency limit
trySpawn();
return {done: false, value: returnValue};
} catch (error) {
isDone = true;
return {error};
}
})();
promises.push(promise);
}
trySpawn();
while (promises.length > 0) {
const {error, done, value} = await promises[0]; // eslint-disable-line no-await-in-loop
promises.shift();
if (error) {
throw error;
}
if (done) {
return;
}
// Spawn if just dropped below backpressure limit and below the concurrency limit
trySpawn();
if (value === pMapSkip) {
continue;
}
yield value;
}
},
};
}
const pMapSkip = Symbol('skip');
/***/ })
};
;
Generated Vendored
+115855 -90990
View File
File diff suppressed because one or more lines are too long
+264 -700
View File
File diff suppressed because it is too large Load Diff
+11 -11
View File
@@ -1,7 +1,7 @@
{
"name": "actions/attest",
"description": "Generate signed attestations for workflow artifacts",
"version": "3.2.0",
"version": "3.0.0",
"author": "",
"private": true,
"homepage": "https://github.com/actions/attest",
@@ -69,31 +69,31 @@
]
},
"dependencies": {
"@actions/attest": "^2.1.0",
"@actions/core": "^2.0.1",
"@actions/attest": "file:/Users/bdehamer/dev/actions/toolkit/packages/attest",
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.1",
"@actions/glob": "^0.5.0",
"@sigstore/oci": "^0.6.0",
"csv-parse": "^5.6.0"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@eslint/js": "^9.39.1",
"@sigstore/mock": "^0.11.0",
"@types/jest": "^30.0.0",
"@types/make-fetch-happen": "^10.0.4",
"@types/node": "^25.0.3",
"@types/node": "^24.10.1",
"@vercel/ncc": "^0.38.4",
"eslint": "^9.39.2",
"eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.9.0",
"eslint-plugin-jest": "^29.2.1",
"jest": "^30.2.0",
"js-yaml": "^4.1.1",
"markdownlint-cli": "^0.47.0",
"markdownlint-cli": "^0.46.0",
"nock": "^13.5.6",
"prettier": "^3.7.4",
"prettier": "^3.7.3",
"ts-jest": "^29.4.6",
"typescript": "^5.9.3",
"typescript-eslint": "^8.50.1",
"undici": "^7.18.2"
"typescript-eslint": "^8.48.0",
"undici": "^5.29.0"
}
}
+13 -77
View File
@@ -7,8 +7,6 @@ import {
} from '@actions/attest'
import { attachArtifactToImage, getRegistryCredentials } from '@sigstore/oci'
import { formatSubjectDigest } from './subject'
import * as core from '@actions/core'
import * as github from '@actions/github'
const OCI_TIMEOUT = 30000
const OCI_RETRY = 3
@@ -16,7 +14,6 @@ const OCI_RETRY = 3
export type SigstoreInstance = 'public-good' | 'github'
export type AttestResult = Attestation & {
attestationDigest?: string
storageRecordIds?: number[]
}
export const createAttestation = async (
@@ -25,7 +22,6 @@ export const createAttestation = async (
opts: {
sigstoreInstance: SigstoreInstance
pushToRegistry: boolean
createStorageRecord: boolean
githubToken: string
}
): Promise<AttestResult> => {
@@ -42,12 +38,23 @@ export const createAttestation = async (
if (subjects.length === 1 && opts.pushToRegistry) {
const subject = subjects[0]
const record = await createStorageRecord({
artifactOptions: {
name: subject.name,
digest: formatSubjectDigest(subject)
},
packageRegistryOptions: {
registryUrl: 'https://ghcr.io'
},
token: opts.githubToken,
writeOptions: {}
})
const credentials = getRegistryCredentials(subject.name)
const subjectDigest = formatSubjectDigest(subject)
const artifact = await attachArtifactToImage({
credentials,
imageName: subject.name,
imageDigest: subjectDigest,
imageDigest: formatSubjectDigest(subject),
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
mediaType: attestation.bundle.mediaType,
annotations: {
@@ -59,78 +66,7 @@ export const createAttestation = async (
// Add the attestation's digest to the result
result.attestationDigest = artifact.digest
// Because creating a storage record requires the 'artifact-metadata:write'
// permission, we wrap this in a try/catch to avoid failing the entire
// attestation process if the token does not have the correct permissions.
if (opts.createStorageRecord) {
try {
const token = opts.githubToken
const isOrg = await repoOwnerIsOrg(token)
if (!isOrg) {
// The Artifact Metadata Storage Record API is only available to
// organizations. So if the repo owner is not an organization,
// storage record creation should not be attempted.
return result
}
const registryUrl = getRegistryURL(subject.name)
const artifactOpts = {
name: subject.name,
digest: subjectDigest
}
const packageRegistryOpts = {
registryUrl
}
const records = await createStorageRecord(
artifactOpts,
packageRegistryOpts,
token
)
if (!records || records.length === 0) {
core.warning('No storage records were created.')
}
result.storageRecordIds = records
} catch (error) {
core.warning(`Failed to create storage record: ${error}`)
core.warning(
'Please check that the "artifact-metadata:write" permission has been included'
)
}
}
}
return result
}
// Call the GET /repos/{owner}/{repo} endpoint to determine if the repo
// owner is an organization. This is used to determine if storage
// record creation should be attempted.
export const repoOwnerIsOrg = async (githubToken: string): Promise<boolean> => {
const octokit = github.getOctokit(githubToken)
const { data: repo } = await octokit.rest.repos.get({
owner: github.context.repo.owner,
repo: github.context.repo.repo
})
return repo.owner?.type === 'Organization'
}
function getRegistryURL(subjectName: string): string {
let url: URL
try {
url = new URL(subjectName)
} catch {
url = new URL(`https://${subjectName}`)
}
if (url.protocol !== 'https:') {
throw new Error(
`Unsupported protocol ${url.protocol} in subject name ${subjectName}`
)
}
return url.origin
}
-1
View File
@@ -13,7 +13,6 @@ const inputs: RunInputs = {
predicate: core.getInput('predicate'),
predicatePath: core.getInput('predicate-path'),
pushToRegistry: core.getBooleanInput('push-to-registry'),
createStorageRecord: core.getBooleanInput('create-storage-record'),
showSummary: core.getBooleanInput('show-summary'),
githubToken: core.getInput('github-token'),
// undocumented -- not part of public interface
-10
View File
@@ -21,7 +21,6 @@ const ATTESTATION_PATHS_FILE_NAME = 'created_attestation_paths.txt'
export type RunInputs = SubjectInputs &
PredicateInputs & {
pushToRegistry: boolean
createStorageRecord: boolean
githubToken: string
showSummary: boolean
privateSigning: boolean
@@ -70,7 +69,6 @@ export async function run(inputs: RunInputs): Promise<void> {
const att = await createAttestation(subjects, predicate, {
sigstoreInstance,
pushToRegistry: inputs.pushToRegistry,
createStorageRecord: inputs.createStorageRecord,
githubToken: inputs.githubToken
})
@@ -102,9 +100,6 @@ export async function run(inputs: RunInputs): Promise<void> {
core.setOutput('attestation-id', att.attestationID)
core.setOutput('attestation-url', attestationURL(att.attestationID))
}
if (att.storageRecordIds) {
core.setOutput('storage-record-ids', att.storageRecordIds.join(','))
}
/* istanbul ignore else */
if (inputs.showSummary) {
@@ -174,11 +169,6 @@ const logAttestation = (
core.info(style.highlight('Attestation uploaded to registry'))
core.info(`${subjects[0].name}@${attestation.attestationDigest}`)
}
if (attestation.storageRecordIds && attestation.storageRecordIds.length > 0) {
core.info(style.highlight('Storage record created'))
core.info(`Storage record IDs: ${attestation.storageRecordIds.join(',')}`)
}
}
// Attach summary information to the GitHub Actions run