Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59d89421af | ||
|
|
ec072a1cb2 | ||
|
|
8b290b8d86 | ||
|
|
35cfe2422e |
34
README.md
34
README.md
@@ -48,11 +48,11 @@ the inputs you provide:
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
| Mode | When Used | Description |
|
||||
| -------------- | ------------------------------------------------------ | ------------------------------------------------ |
|
||||
| **Provenance** | No `sbom-path` or predicate inputs | Auto-generates [SLSA build provenance][10] |
|
||||
| **SBOM** | `sbom-path` is provided | Creates attestation from SPDX or CycloneDX SBOM |
|
||||
| **Custom** | `predicate-type`/`predicate`/`predicate-path` provided | User-supplied predicate |
|
||||
| Mode | When Used | Description |
|
||||
| -------------- | ------------------------------------------------------ | ----------------------------------------------- |
|
||||
| **Provenance** | No `sbom-path` or predicate inputs | Auto-generates [SLSA build provenance][10] |
|
||||
| **SBOM** | `sbom-path` is provided | Creates attestation from SPDX or CycloneDX SBOM |
|
||||
| **Custom** | `predicate-type`/`predicate`/`predicate-path` provided | User-supplied predicate |
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
||||
@@ -159,7 +159,7 @@ 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` |
|
||||
@@ -320,9 +320,25 @@ 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`.
|
||||
#### Artifact Metadata Storage Records
|
||||
|
||||
When generating a build provenance attestation, if the `push-to-registry` option
|
||||
is set to true, the Action will also emit an
|
||||
[Artifact Metadata Storage Record](https://docs.github.com/en/rest/orgs/artifact-metadata?apiVersion=2022-11-28#create-artifact-metadata-storage-record).
|
||||
Storage records enrich artifact metadata by capturing storage related details,
|
||||
such as which registry an image is hosted on and whether it's marked as active.
|
||||
|
||||
If you do not want to emit a storage record, set `create-storage-record` to
|
||||
`false`.
|
||||
|
||||
> **NOTE**: Storage records can only be created for artifacts built from
|
||||
> [organization-owned](https://docs.github.com/en/organizations/collaborating-with-groups-in-organizations/about-organizations)
|
||||
> repositories.
|
||||
|
||||
Artifacts associated with a storage record can be viewed by navigating to the
|
||||
`Linked Artifacts` page in your organization:
|
||||
`https://github.com/orgs/YOUR_ORG/artifacts` (replace `YOUR_ORG` with your
|
||||
organization name).
|
||||
|
||||
> **NOTE**: When pushing to Docker Hub, please use "docker.io" as the registry
|
||||
> portion of the image name.
|
||||
|
||||
@@ -30,6 +30,7 @@ describe('index', () => {
|
||||
'subject-name': 'my-artifact',
|
||||
'subject-digest': '',
|
||||
'subject-checksums': '',
|
||||
'subject-version': '',
|
||||
'predicate-type': 'https://example.com/predicate',
|
||||
predicate: '{}',
|
||||
'predicate-path': '',
|
||||
@@ -57,6 +58,7 @@ describe('index', () => {
|
||||
subjectName: 'my-artifact',
|
||||
subjectDigest: '',
|
||||
subjectChecksums: '',
|
||||
subjectVersion: '',
|
||||
predicateType: 'https://example.com/predicate',
|
||||
predicate: '{}',
|
||||
predicatePath: '',
|
||||
|
||||
@@ -145,7 +145,8 @@ describe('createAttestation', () => {
|
||||
const storageOpts = {
|
||||
...defaultOpts,
|
||||
pushToRegistry: true,
|
||||
createStorageRecord: true
|
||||
createStorageRecord: true,
|
||||
subjectVersion: '1.2.3'
|
||||
}
|
||||
|
||||
it('should create storage record when enabled and owner is org', async () => {
|
||||
@@ -157,10 +158,27 @@ describe('createAttestation', () => {
|
||||
storageOpts
|
||||
)
|
||||
|
||||
expect(mockCreateStorageRecord).toHaveBeenCalled()
|
||||
expect(mockCreateStorageRecord).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ version: '1.2.3' }),
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
)
|
||||
expect(result.storageRecordIds).toEqual([12345])
|
||||
})
|
||||
|
||||
it('should omit version from storage record when subjectVersion is empty', async () => {
|
||||
const subjects = [TEST_SUBJECT_WITH_REGISTRY]
|
||||
const opts = { ...storageOpts, subjectVersion: '' }
|
||||
|
||||
await createAttestation(subjects, TEST_PREDICATE, opts)
|
||||
|
||||
expect(mockCreateStorageRecord).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ version: undefined }),
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('should skip storage record when owner is User', async () => {
|
||||
mockGetOctokit.mockReturnValue(createOctokitMock('User'))
|
||||
const subjects = [TEST_SUBJECT_WITH_REGISTRY]
|
||||
|
||||
@@ -101,6 +101,7 @@ const defaultInputs: RunInputs = {
|
||||
subjectChecksums: '',
|
||||
pushToRegistry: false,
|
||||
createStorageRecord: false,
|
||||
subjectVersion: '',
|
||||
showSummary: false,
|
||||
githubToken: 'test-token',
|
||||
privateSigning: false
|
||||
|
||||
@@ -30,6 +30,11 @@ inputs:
|
||||
attestation. Must specify exactly one of "subject-path", "subject-digest",
|
||||
or "subject-checksums".
|
||||
required: false
|
||||
subject-version:
|
||||
description: >
|
||||
Version of the subject for the attestation. Only used when
|
||||
"push-to-registry" and "create-storage-record" are both set to true.
|
||||
required: false
|
||||
sbom-path:
|
||||
description: >
|
||||
Path to the JSON-formatted SBOM file (SPDX or CycloneDX) to attest.
|
||||
|
||||
50
dist/index.js
generated
vendored
50
dist/index.js
generated
vendored
@@ -108507,6 +108507,27 @@ exports.LRUCache = LRUCache;
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 86705:
|
||||
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
||||
|
||||
// This file exists as a CommonJS module to read the version from package.json.
|
||||
// In an ESM package, using `require()` directly in .ts files requires disabling
|
||||
// ESLint rules and doesn't work reliably across all Node.js versions.
|
||||
// By keeping this as a .cjs file, we can use require() naturally and export
|
||||
// the version for the ESM modules to import.
|
||||
const packageJson = __nccwpck_require__(47849)
|
||||
module.exports = {version: packageJson.version}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 47849:
|
||||
/***/ ((module) => {
|
||||
|
||||
module.exports = /*#__PURE__*/JSON.parse('{"name":"@actions/attest","version":"3.2.0","description":"Actions attestation lib","keywords":["github","actions","attestation"],"homepage":"https://github.com/actions/toolkit/tree/main/packages/attest","license":"MIT","type":"module","main":"lib/index.js","types":"lib/index.d.ts","exports":{".":{"types":"./lib/index.d.ts","import":"./lib/index.js"}},"directories":{"lib":"lib","test":"__tests__"},"files":["lib"],"publishConfig":{"access":"public","provenance":true},"repository":{"type":"git","url":"git+https://github.com/actions/toolkit.git","directory":"packages/attest"},"scripts":{"test":"echo \\"Error: run tests from root\\" && exit 1","tsc":"tsc && cp src/internal/package-version.cjs lib/internal/"},"bugs":{"url":"https://github.com/actions/toolkit/issues"},"devDependencies":{"@sigstore/mock":"^0.10.0","@sigstore/rekor-types":"^3.0.0","@types/jsonwebtoken":"^9.0.6","nock":"^13.5.1","undici":"^6.23.0"},"dependencies":{"@actions/core":"^3.0.0","@actions/github":"^9.0.0","@actions/http-client":"^4.0.0","@octokit/plugin-retry":"^8.0.3","@sigstore/bundle":"^3.1.0","@sigstore/sign":"^3.1.0","jose":"^5.10.0"}}');
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 4592:
|
||||
/***/ ((module) => {
|
||||
|
||||
@@ -115926,6 +115947,22 @@ function retry(octokit, octokitOptions) {
|
||||
retry.VERSION = plugin_retry_dist_bundle_VERSION;
|
||||
|
||||
|
||||
// EXTERNAL MODULE: ./node_modules/@actions/attest/lib/internal/package-version.cjs
|
||||
var package_version = __nccwpck_require__(86705);
|
||||
;// CONCATENATED MODULE: ./node_modules/@actions/attest/lib/internal/utils.js
|
||||
|
||||
const utils_getUserAgent = () => {
|
||||
const baseUserAgent = `@actions/attest-${package_version.version}`;
|
||||
const orchId = process.env['ACTIONS_ORCHESTRATION_ID'];
|
||||
if (orchId) {
|
||||
// Sanitize the orchestration ID to ensure it contains only valid characters
|
||||
// Valid characters: 0-9, a-z, _, -, .
|
||||
const sanitizedId = orchId.replace(/[^a-z0-9_.-]/gi, '_');
|
||||
return `${baseUserAgent} actions_orchestration_id/${sanitizedId}`;
|
||||
}
|
||||
return baseUserAgent;
|
||||
};
|
||||
//# sourceMappingURL=utils.js.map
|
||||
;// CONCATENATED MODULE: ./node_modules/@actions/attest/lib/artifactMetadata.js
|
||||
var artifactMetadata_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
@@ -115949,6 +115986,7 @@ var __rest = (undefined && undefined.__rest) || function (s, e) {
|
||||
};
|
||||
|
||||
|
||||
|
||||
const CREATE_STORAGE_RECORD_REQUEST = 'POST /orgs/{owner}/artifacts/metadata/storage-record';
|
||||
const DEFAULT_RETRY_COUNT = 5;
|
||||
/**
|
||||
@@ -115966,8 +116004,9 @@ function createStorageRecord(artifactOptions, packageRegistryOptions, token, ret
|
||||
return artifactMetadata_awaiter(this, void 0, void 0, function* () {
|
||||
const retries = retryAttempts !== null && retryAttempts !== void 0 ? retryAttempts : DEFAULT_RETRY_COUNT;
|
||||
const octokit = getOctokit(token, { retry: { retries } }, retry);
|
||||
const headersWithUserAgent = Object.assign({ 'User-Agent': utils_getUserAgent() }, headers);
|
||||
try {
|
||||
const response = yield octokit.request(CREATE_STORAGE_RECORD_REQUEST, Object.assign({ owner: github_context.repo.owner, headers }, buildRequestParams(artifactOptions, packageRegistryOptions)));
|
||||
const response = yield octokit.request(CREATE_STORAGE_RECORD_REQUEST, Object.assign({ owner: github_context.repo.owner, headers: headersWithUserAgent }, buildRequestParams(artifactOptions, packageRegistryOptions)));
|
||||
const data = typeof response.data == 'string'
|
||||
? JSON.parse(response.data)
|
||||
: response.data;
|
||||
@@ -116121,6 +116160,7 @@ var store_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
|
||||
};
|
||||
|
||||
|
||||
|
||||
const CREATE_ATTESTATION_REQUEST = 'POST /repos/{owner}/{repo}/attestations';
|
||||
const store_DEFAULT_RETRY_COUNT = 5;
|
||||
/**
|
||||
@@ -116134,11 +116174,12 @@ const writeAttestation = (attestation_1, token_1, ...args_1) => store_awaiter(vo
|
||||
var _a;
|
||||
const retries = (_a = options.retry) !== null && _a !== void 0 ? _a : store_DEFAULT_RETRY_COUNT;
|
||||
const octokit = getOctokit(token, { retry: { retries } }, retry);
|
||||
const headers = Object.assign({ 'User-Agent': utils_getUserAgent() }, options.headers);
|
||||
try {
|
||||
const response = yield octokit.request(CREATE_ATTESTATION_REQUEST, {
|
||||
owner: github_context.repo.owner,
|
||||
repo: github_context.repo.repo,
|
||||
headers: options.headers,
|
||||
headers,
|
||||
bundle: attestation
|
||||
});
|
||||
const data = typeof response.data == 'string'
|
||||
@@ -120858,7 +120899,8 @@ const createAttestation = async (subjects, predicate, opts) => {
|
||||
const registryUrl = getRegistryURL(subject.name);
|
||||
const artifactOpts = {
|
||||
name: subject.name,
|
||||
digest: subjectDigest
|
||||
digest: subjectDigest,
|
||||
version: opts.subjectVersion || undefined
|
||||
};
|
||||
const packageRegistryOpts = {
|
||||
registryUrl
|
||||
@@ -121116,6 +121158,7 @@ async function run(inputs) {
|
||||
sigstoreInstance,
|
||||
pushToRegistry: inputs.pushToRegistry,
|
||||
createStorageRecord: inputs.createStorageRecord,
|
||||
subjectVersion: inputs.subjectVersion,
|
||||
githubToken: inputs.githubToken
|
||||
});
|
||||
logAttestation(subjects, att, sigstoreInstance);
|
||||
@@ -121257,6 +121300,7 @@ const inputs = {
|
||||
predicatePath: getInput('predicate-path'),
|
||||
pushToRegistry: getBooleanInput('push-to-registry'),
|
||||
createStorageRecord: getBooleanInput('create-storage-record'),
|
||||
subjectVersion: getInput('subject-version'),
|
||||
showSummary: getBooleanInput('show-summary'),
|
||||
githubToken: getInput('github-token'),
|
||||
// undocumented -- not part of public interface
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "actions/attest",
|
||||
"version": "4.0.0",
|
||||
"version": "4.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "actions/attest",
|
||||
"version": "4.0.0",
|
||||
"version": "4.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/attest": "^3.0.0",
|
||||
"@actions/attest": "^3.2.0",
|
||||
"@actions/core": "^3.0.0",
|
||||
"@actions/github": "^9.0.0",
|
||||
"@actions/glob": "^0.6.1",
|
||||
@@ -42,9 +42,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/attest": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/attest/-/attest-3.0.0.tgz",
|
||||
"integrity": "sha512-XrGmxFA3rZO4ACtVEUHFUI318lMycHQjHep3SX/AqU8IwR0y9afw8URsGrQZhGqwMDTYxYFST9PaNQCksIyE8A==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/attest/-/attest-3.2.0.tgz",
|
||||
"integrity": "sha512-Mdpqfyfp4dp7VZt9lVBmQTlnpK0PBrIXSblzeseP4w6Gn4Bbl5bpScJ+8zgwOMfTz1049wPzSUda5XtTYIZloQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^3.0.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "actions/attest",
|
||||
"description": "Generate signed attestations for workflow artifacts",
|
||||
"version": "4.0.0",
|
||||
"version": "4.1.0",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -78,7 +78,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/attest": "^3.0.0",
|
||||
"@actions/attest": "^3.2.0",
|
||||
"@actions/core": "^3.0.0",
|
||||
"@actions/github": "^9.0.0",
|
||||
"@actions/glob": "^0.6.1",
|
||||
|
||||
@@ -26,6 +26,7 @@ export const createAttestation = async (
|
||||
sigstoreInstance: SigstoreInstance
|
||||
pushToRegistry: boolean
|
||||
createStorageRecord: boolean
|
||||
subjectVersion?: string
|
||||
githubToken: string
|
||||
}
|
||||
): Promise<AttestResult> => {
|
||||
@@ -77,7 +78,8 @@ export const createAttestation = async (
|
||||
const registryUrl = getRegistryURL(subject.name)
|
||||
const artifactOpts = {
|
||||
name: subject.name,
|
||||
digest: subjectDigest
|
||||
digest: subjectDigest,
|
||||
version: opts.subjectVersion || undefined
|
||||
}
|
||||
const packageRegistryOpts = {
|
||||
registryUrl
|
||||
|
||||
@@ -15,6 +15,7 @@ const inputs: RunInputs = {
|
||||
predicatePath: core.getInput('predicate-path'),
|
||||
pushToRegistry: core.getBooleanInput('push-to-registry'),
|
||||
createStorageRecord: core.getBooleanInput('create-storage-record'),
|
||||
subjectVersion: core.getInput('subject-version'),
|
||||
showSummary: core.getBooleanInput('show-summary'),
|
||||
githubToken: core.getInput('github-token'),
|
||||
// undocumented -- not part of public interface
|
||||
|
||||
@@ -35,6 +35,7 @@ export type RunInputs = SubjectInputs &
|
||||
SBOMInputs & {
|
||||
pushToRegistry: boolean
|
||||
createStorageRecord: boolean
|
||||
subjectVersion: string
|
||||
githubToken: string
|
||||
showSummary: boolean
|
||||
privateSigning: boolean
|
||||
@@ -97,6 +98,7 @@ export async function run(inputs: RunInputs): Promise<void> {
|
||||
sigstoreInstance,
|
||||
pushToRegistry: inputs.pushToRegistry,
|
||||
createStorageRecord: inputs.createStorageRecord,
|
||||
subjectVersion: inputs.subjectVersion,
|
||||
githubToken: inputs.githubToken
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user