Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 12c083815e | |||
| 38ff958ab6 | |||
| 60d0be1445 | |||
| 32f49af665 | |||
| 3f67a24e31 | |||
| e259ee2285 | |||
| 58fa41a101 | |||
| b0d8b47eb7 | |||
| 9b22bf5c9f | |||
| 9cbbc78ff9 |
@@ -175,7 +175,7 @@ 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.
|
||||
|
||||
> **NOTE**: When pushing to Docker Hub, please use "index.docker.io" as the
|
||||
> **NOTE**: When pushing to Docker Hub, please use "docker.io" as the
|
||||
> registry portion of the image name.
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -11,6 +11,14 @@ Follow the steps below to tag a new release for the `actions/attest` action.
|
||||
gh release create vX.X.X
|
||||
```
|
||||
|
||||
1. Move (or create) the major version tag to point to the same commit tagged
|
||||
above:
|
||||
|
||||
```shell
|
||||
git tag -fa vX -m "vX"
|
||||
git push origin vX --force
|
||||
```
|
||||
|
||||
1. As appropriate, update any actions like
|
||||
[`actions/attest-build-provenance`](https://github.com/actions/attest-build-provenance)
|
||||
and [`actions/attest-sbom`](https://github.com/actions/attest-sbom) which
|
||||
|
||||
@@ -116,7 +116,9 @@ describe('action', () => {
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/missing "id-token" permission/)
|
||||
new Error(
|
||||
'missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.'
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -131,9 +133,7 @@ describe('action', () => {
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
expect.stringMatching(
|
||||
/one of subject-path or subject-digest must be provided/i
|
||||
)
|
||||
new Error('One of subject-path or subject-digest must be provided')
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -330,7 +330,9 @@ describe('action', () => {
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
'Too many subjects specified. The maximum number of subjects is 64.'
|
||||
new Error(
|
||||
'Too many subjects specified. The maximum number of subjects is 64.'
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,10 @@ import path from 'path'
|
||||
import { subjectFromInputs } from '../src/subject'
|
||||
|
||||
describe('subjectFromInputs', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PUSH-TO-REGISTRY'] = 'false'
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = ''
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = ''
|
||||
@@ -45,12 +49,12 @@ describe('subjectFromInputs', () => {
|
||||
})
|
||||
|
||||
describe('when specifying a subject digest', () => {
|
||||
const name = 'subject'
|
||||
const name = 'Subject'
|
||||
|
||||
describe('when the digest is malformed', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
|
||||
process.env['INPUT_SUBJECT-NAME'] = 'subject'
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
@@ -63,7 +67,7 @@ describe('subjectFromInputs', () => {
|
||||
describe('when the alogrithm is not supported', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'md5:deadbeef'
|
||||
process.env['INPUT_SUBJECT-NAME'] = 'subject'
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
@@ -76,7 +80,7 @@ describe('subjectFromInputs', () => {
|
||||
describe('when the sha256 digest is malformed', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'sha256:deadbeef'
|
||||
process.env['INPUT_SUBJECT-NAME'] = 'subject'
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
@@ -105,6 +109,28 @@ describe('subjectFromInputs', () => {
|
||||
expect(subject[0].digest).toEqual({ [alg]: digest })
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the push-to-registry is true', () => {
|
||||
const imageName = 'ghcr.io/FOO/bar'
|
||||
const alg = 'sha256'
|
||||
const digest =
|
||||
'7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = `${alg}:${digest}`
|
||||
process.env['INPUT_SUBJECT-NAME'] = imageName
|
||||
process.env['INPUT_PUSH-TO-REGISTRY'] = 'true'
|
||||
})
|
||||
|
||||
it('returns the subject (with name downcased)', async () => {
|
||||
const subject = await subjectFromInputs()
|
||||
|
||||
expect(subject).toBeDefined()
|
||||
expect(subject).toHaveLength(1)
|
||||
expect(subject[0].name).toEqual(imageName.toLowerCase())
|
||||
expect(subject[0].digest).toEqual({ [alg]: digest })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a subject path', () => {
|
||||
|
||||
+490
-153
@@ -11433,7 +11433,7 @@ exports.SignedCertificateTimestamp = SignedCertificateTimestamp;
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.HEADER_OCI_SUBJECT = exports.HEADER_LOCATION = exports.HEADER_IF_MATCH = exports.HEADER_ETAG = exports.HEADER_DIGEST = exports.HEADER_CONTENT_TYPE = exports.HEADER_CONTENT_LENGTH = exports.HEADER_AUTHORIZATION = exports.HEADER_AUTHENTICATE = exports.HEADER_API_VERSION = exports.HEADER_ACCEPT = exports.CONTENT_TYPE_EMPTY_DESCRIPTOR = exports.CONTENT_TYPE_OCTET_STREAM = exports.CONTENT_TYPE_OCI_MANIFEST = exports.CONTENT_TYPE_OCI_INDEX = void 0;
|
||||
exports.HEADER_OCI_SUBJECT = exports.HEADER_LOCATION = exports.HEADER_IF_MATCH = exports.HEADER_ETAG = exports.HEADER_DIGEST = exports.HEADER_CONTENT_TYPE = exports.HEADER_CONTENT_LENGTH = exports.HEADER_AUTHORIZATION = exports.HEADER_AUTHENTICATE = exports.HEADER_API_VERSION = exports.HEADER_ACCEPT = exports.CONTENT_TYPE_EMPTY_DESCRIPTOR = exports.CONTENT_TYPE_OCTET_STREAM = exports.CONTENT_TYPE_DOCKER_MANIFEST_LIST = exports.CONTENT_TYPE_DOCKER_MANIFEST = exports.CONTENT_TYPE_OCI_MANIFEST = exports.CONTENT_TYPE_OCI_INDEX = void 0;
|
||||
/*
|
||||
Copyright 2023 The Sigstore Authors.
|
||||
|
||||
@@ -11451,6 +11451,8 @@ limitations under the License.
|
||||
*/
|
||||
exports.CONTENT_TYPE_OCI_INDEX = 'application/vnd.oci.image.index.v1+json';
|
||||
exports.CONTENT_TYPE_OCI_MANIFEST = 'application/vnd.oci.image.manifest.v1+json';
|
||||
exports.CONTENT_TYPE_DOCKER_MANIFEST = 'application/vnd.docker.distribution.manifest.v2+json';
|
||||
exports.CONTENT_TYPE_DOCKER_MANIFEST_LIST = 'application/vnd.docker.distribution.manifest.list.v2+json';
|
||||
exports.CONTENT_TYPE_OCTET_STREAM = 'application/octet-stream';
|
||||
exports.CONTENT_TYPE_EMPTY_DESCRIPTOR = 'application/vnd.oci.empty.v1+json';
|
||||
exports.HEADER_ACCEPT = 'Accept';
|
||||
@@ -11575,6 +11577,96 @@ class OCIError extends Error {
|
||||
exports.OCIError = OCIError;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 437:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
/*
|
||||
Copyright 2024 The Sigstore Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
const http2_1 = __nccwpck_require__(85158);
|
||||
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
|
||||
const proc_log_1 = __nccwpck_require__(56528);
|
||||
const promise_retry_1 = __importDefault(__nccwpck_require__(54742));
|
||||
const { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_TOO_MANY_REQUESTS, HTTP_STATUS_REQUEST_TIMEOUT, } = http2_1.constants;
|
||||
const fetchWithRetry = async (url, options = {}) => {
|
||||
return (0, promise_retry_1.default)(async (retry, attemptNum) => {
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const logRetry = (reason) => {
|
||||
proc_log_1.log.http('fetch', `${options.method} ${url} attempt ${attemptNum} failed with ${reason}`);
|
||||
};
|
||||
const response = await (0, make_fetch_happen_1.default)(url, {
|
||||
...options,
|
||||
retry: false, // We're handling retries ourselves
|
||||
}).catch((reason) => {
|
||||
logRetry(reason);
|
||||
return retry(reason);
|
||||
});
|
||||
if (retryable(response.status)) {
|
||||
logRetry(response.status);
|
||||
return retry(response);
|
||||
}
|
||||
return response;
|
||||
}, retryOpts(options.retry)).catch((err) => {
|
||||
// If we got an actual error, throw it
|
||||
if (err instanceof Error) {
|
||||
throw err;
|
||||
}
|
||||
// Otherwise, return the response (this is simply a retry-able response for
|
||||
// which we exceeded the retry limit)
|
||||
return err;
|
||||
});
|
||||
};
|
||||
// Returns a wrapped fetch function with default options
|
||||
fetchWithRetry.defaults = (defaultOptions = {}, wrappedFetch = fetchWithRetry) => {
|
||||
const defaultedFetch = (url, options = {}) => {
|
||||
const finalOptions = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
headers: { ...defaultOptions.headers, ...options.headers },
|
||||
};
|
||||
return wrappedFetch(url, finalOptions);
|
||||
};
|
||||
defaultedFetch.defaults = (newDefaults = {}) => fetchWithRetry.defaults(newDefaults, defaultedFetch);
|
||||
return defaultedFetch;
|
||||
};
|
||||
// Determine if a status code is retryable. This includes 5xx errors, 408, and
|
||||
// 429.
|
||||
const retryable = (status) => [HTTP_STATUS_REQUEST_TIMEOUT, HTTP_STATUS_TOO_MANY_REQUESTS].includes(status) || status >= HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||||
// Normalize the retry options to the format expected by promise-retry
|
||||
const retryOpts = (retry) => {
|
||||
if (typeof retry === 'boolean') {
|
||||
return { retries: retry ? 1 : 0 };
|
||||
}
|
||||
else if (typeof retry === 'number') {
|
||||
return { retries: retry };
|
||||
}
|
||||
else {
|
||||
return { retries: 0, ...retry };
|
||||
}
|
||||
};
|
||||
exports["default"] = fetchWithRetry;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 79539:
|
||||
@@ -11614,13 +11706,14 @@ limitations under the License.
|
||||
const constants_1 = __nccwpck_require__(61319);
|
||||
const error_1 = __nccwpck_require__(60064);
|
||||
const registry_1 = __nccwpck_require__(27464);
|
||||
const DOCKER_DEFAULT_REGISTRY = 'registry-1.docker.io';
|
||||
const EMPTY_BLOB = Buffer.from('{}');
|
||||
class OCIImage {
|
||||
constructor(image, creds, opts) {
|
||||
_OCIImage_instances.add(this);
|
||||
_OCIImage_client.set(this, void 0);
|
||||
_OCIImage_credentials.set(this, void 0);
|
||||
__classPrivateFieldSet(this, _OCIImage_client, new registry_1.RegistryClient(image.registry, image.path, opts), "f");
|
||||
__classPrivateFieldSet(this, _OCIImage_client, new registry_1.RegistryClient(canonicalizeRegistryName(image.registry), image.path, opts), "f");
|
||||
__classPrivateFieldSet(this, _OCIImage_credentials, creds, "f");
|
||||
}
|
||||
async addArtifact(opts) {
|
||||
@@ -11750,6 +11843,13 @@ const newIndex = () => ({
|
||||
const digestToTag = (digest) => {
|
||||
return digest.replace(':', '-');
|
||||
};
|
||||
// Canonicalize the registry name to match the format used by the registry
|
||||
// client. This is used primarily to handle the special case of the Docker Hub
|
||||
// registry.
|
||||
// https://github.com/moby/moby/blob/v24.0.2/registry/config.go#L25-L48
|
||||
const canonicalizeRegistryName = (registry) => {
|
||||
return registry.endsWith('docker.io') ? DOCKER_DEFAULT_REGISTRY : registry;
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -11869,11 +11969,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
|
||||
const node_crypto_1 = __importDefault(__nccwpck_require__(6005));
|
||||
const constants_1 = __nccwpck_require__(61319);
|
||||
const credentials_1 = __nccwpck_require__(95475);
|
||||
const error_1 = __nccwpck_require__(60064);
|
||||
const fetch_1 = __importDefault(__nccwpck_require__(437));
|
||||
const ALL_MANIFEST_MEDIA_TYPES = [
|
||||
constants_1.CONTENT_TYPE_OCI_INDEX,
|
||||
constants_1.CONTENT_TYPE_OCI_MANIFEST,
|
||||
constants_1.CONTENT_TYPE_DOCKER_MANIFEST,
|
||||
constants_1.CONTENT_TYPE_DOCKER_MANIFEST_LIST,
|
||||
].join(',');
|
||||
class RegistryClient {
|
||||
constructor(registry, repository, opts) {
|
||||
_RegistryClient_instances.add(this);
|
||||
@@ -11881,7 +11987,7 @@ class RegistryClient {
|
||||
_RegistryClient_repository.set(this, void 0);
|
||||
_RegistryClient_fetch.set(this, void 0);
|
||||
__classPrivateFieldSet(this, _RegistryClient_repository, repository, "f");
|
||||
__classPrivateFieldSet(this, _RegistryClient_fetch, make_fetch_happen_1.default.defaults(opts), "f");
|
||||
__classPrivateFieldSet(this, _RegistryClient_fetch, fetch_1.default.defaults(opts), "f");
|
||||
// Use http for localhost registries, https otherwise
|
||||
const hostname = new URL(`http://${registry}`).hostname;
|
||||
/* istanbul ignore next */
|
||||
@@ -11968,9 +12074,7 @@ class RegistryClient {
|
||||
async checkManifest(reference) {
|
||||
const response = await __classPrivateFieldGet(this, _RegistryClient_fetch, "f").call(this, `${__classPrivateFieldGet(this, _RegistryClient_baseURL, "f")}/v2/${__classPrivateFieldGet(this, _RegistryClient_repository, "f")}/manifests/${reference}`, {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
[constants_1.HEADER_ACCEPT]: `${constants_1.CONTENT_TYPE_OCI_MANIFEST},${constants_1.CONTENT_TYPE_OCI_INDEX}`,
|
||||
},
|
||||
headers: { [constants_1.HEADER_ACCEPT]: ALL_MANIFEST_MEDIA_TYPES },
|
||||
}).then((0, error_1.ensureStatus)(200));
|
||||
const mediaType = response.headers.get(constants_1.HEADER_CONTENT_TYPE) ||
|
||||
/* istanbul ignore next */ '';
|
||||
@@ -11982,9 +12086,7 @@ class RegistryClient {
|
||||
// Retrieves a manifest by reference
|
||||
async getManifest(reference) {
|
||||
const response = await __classPrivateFieldGet(this, _RegistryClient_fetch, "f").call(this, `${__classPrivateFieldGet(this, _RegistryClient_baseURL, "f")}/v2/${__classPrivateFieldGet(this, _RegistryClient_repository, "f")}/manifests/${reference}`, {
|
||||
headers: {
|
||||
[constants_1.HEADER_ACCEPT]: `${constants_1.CONTENT_TYPE_OCI_MANIFEST},${constants_1.CONTENT_TYPE_OCI_INDEX}`,
|
||||
},
|
||||
headers: { [constants_1.HEADER_ACCEPT]: ALL_MANIFEST_MEDIA_TYPES },
|
||||
}).then((0, error_1.ensureStatus)(200));
|
||||
const body = await response.json();
|
||||
const mediaType = response.headers.get(constants_1.HEADER_CONTENT_TYPE) ||
|
||||
@@ -13923,8 +14025,23 @@ exports.internalError = internalError;
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Copyright 2023 The Sigstore Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.checkStatus = exports.HTTPError = void 0;
|
||||
exports.HTTPError = void 0;
|
||||
class HTTPError extends Error {
|
||||
constructor({ status, message, location, }) {
|
||||
super(`(${status}) ${message}`);
|
||||
@@ -13933,38 +14050,11 @@ class HTTPError extends Error {
|
||||
}
|
||||
}
|
||||
exports.HTTPError = HTTPError;
|
||||
const checkStatus = async (response) => {
|
||||
if (response.ok) {
|
||||
return response;
|
||||
}
|
||||
else {
|
||||
let message = response.statusText;
|
||||
const location = response.headers?.get('Location') || undefined;
|
||||
const contentType = response.headers?.get('Content-Type');
|
||||
// If response type is JSON, try to parse the body for a message
|
||||
if (contentType?.includes('application/json')) {
|
||||
try {
|
||||
await response.json().then((body) => {
|
||||
message = body.message;
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
throw new HTTPError({
|
||||
status: response.status,
|
||||
message: message,
|
||||
location: location,
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.checkStatus = checkStatus;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 62960:
|
||||
/***/ 78509:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
|
||||
"use strict";
|
||||
@@ -13972,6 +14062,110 @@ exports.checkStatus = checkStatus;
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.fetchWithRetry = void 0;
|
||||
/*
|
||||
Copyright 2023 The Sigstore Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
const http2_1 = __nccwpck_require__(85158);
|
||||
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
|
||||
const proc_log_1 = __nccwpck_require__(56528);
|
||||
const promise_retry_1 = __importDefault(__nccwpck_require__(54742));
|
||||
const util_1 = __nccwpck_require__(90724);
|
||||
const error_1 = __nccwpck_require__(11294);
|
||||
const { HTTP2_HEADER_LOCATION, HTTP2_HEADER_CONTENT_TYPE, HTTP2_HEADER_USER_AGENT, HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_TOO_MANY_REQUESTS, HTTP_STATUS_REQUEST_TIMEOUT, } = http2_1.constants;
|
||||
async function fetchWithRetry(url, options) {
|
||||
return (0, promise_retry_1.default)(async (retry, attemptNum) => {
|
||||
const method = options.method || 'POST';
|
||||
const headers = {
|
||||
[HTTP2_HEADER_USER_AGENT]: util_1.ua.getUserAgent(),
|
||||
...options.headers,
|
||||
};
|
||||
const response = await (0, make_fetch_happen_1.default)(url, {
|
||||
method,
|
||||
headers,
|
||||
body: options.body,
|
||||
timeout: options.timeout,
|
||||
retry: false, // We're handling retries ourselves
|
||||
}).catch((reason) => {
|
||||
proc_log_1.log.http('fetch', `${method} ${url} attempt ${attemptNum} failed with ${reason}`);
|
||||
return retry(reason);
|
||||
});
|
||||
if (response.ok) {
|
||||
return response;
|
||||
}
|
||||
else {
|
||||
const error = await errorFromResponse(response);
|
||||
proc_log_1.log.http('fetch', `${method} ${url} attempt ${attemptNum} failed with ${response.status}`);
|
||||
if (retryable(response.status)) {
|
||||
return retry(error);
|
||||
}
|
||||
else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}, retryOpts(options.retry));
|
||||
}
|
||||
exports.fetchWithRetry = fetchWithRetry;
|
||||
// Translate a Response into an HTTPError instance. This will attempt to parse
|
||||
// the response body for a message, but will default to the statusText if none
|
||||
// is found.
|
||||
const errorFromResponse = async (response) => {
|
||||
let message = response.statusText;
|
||||
const location = response.headers?.get(HTTP2_HEADER_LOCATION) || undefined;
|
||||
const contentType = response.headers?.get(HTTP2_HEADER_CONTENT_TYPE);
|
||||
// If response type is JSON, try to parse the body for a message
|
||||
if (contentType?.includes('application/json')) {
|
||||
try {
|
||||
const body = await response.json();
|
||||
message = body.message || message;
|
||||
}
|
||||
catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return new error_1.HTTPError({
|
||||
status: response.status,
|
||||
message: message,
|
||||
location: location,
|
||||
});
|
||||
};
|
||||
// Determine if a status code is retryable. This includes 5xx errors, 408, and
|
||||
// 429.
|
||||
const retryable = (status) => [HTTP_STATUS_REQUEST_TIMEOUT, HTTP_STATUS_TOO_MANY_REQUESTS].includes(status) || status >= HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||||
// Normalize the retry options to the format expected by promise-retry
|
||||
const retryOpts = (retry) => {
|
||||
if (typeof retry === 'boolean') {
|
||||
return { retries: retry ? 1 : 0 };
|
||||
}
|
||||
else if (typeof retry === 'number') {
|
||||
return { retries: retry };
|
||||
}
|
||||
else {
|
||||
return { retries: 0, ...retry };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 62960:
|
||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.Fulcio = void 0;
|
||||
/*
|
||||
@@ -13989,33 +14183,26 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
|
||||
const util_1 = __nccwpck_require__(90724);
|
||||
const error_1 = __nccwpck_require__(11294);
|
||||
const fetch_1 = __nccwpck_require__(78509);
|
||||
/**
|
||||
* Fulcio API client.
|
||||
*/
|
||||
class Fulcio {
|
||||
constructor(options) {
|
||||
this.fetch = make_fetch_happen_1.default.defaults({
|
||||
retry: options.retry,
|
||||
timeout: options.timeout,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': util_1.ua.getUserAgent(),
|
||||
},
|
||||
});
|
||||
this.baseUrl = options.baseURL;
|
||||
this.options = options;
|
||||
}
|
||||
async createSigningCertificate(request) {
|
||||
const url = `${this.baseUrl}/api/v2/signingCert`;
|
||||
const response = await this.fetch(url, {
|
||||
method: 'POST',
|
||||
const { baseURL, retry, timeout } = this.options;
|
||||
const url = `${baseURL}/api/v2/signingCert`;
|
||||
const response = await (0, fetch_1.fetchWithRetry)(url, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
timeout,
|
||||
retry,
|
||||
});
|
||||
await (0, error_1.checkStatus)(response);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
exports.Fulcio = Fulcio;
|
||||
@@ -14024,13 +14211,10 @@ exports.Fulcio = Fulcio;
|
||||
/***/ }),
|
||||
|
||||
/***/ 56205:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.Rekor = void 0;
|
||||
/*
|
||||
@@ -14048,23 +14232,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
|
||||
const util_1 = __nccwpck_require__(90724);
|
||||
const error_1 = __nccwpck_require__(11294);
|
||||
const fetch_1 = __nccwpck_require__(78509);
|
||||
/**
|
||||
* Rekor API client.
|
||||
*/
|
||||
class Rekor {
|
||||
constructor(options) {
|
||||
this.fetch = make_fetch_happen_1.default.defaults({
|
||||
retry: options.retry,
|
||||
timeout: options.timeout,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'User-Agent': util_1.ua.getUserAgent(),
|
||||
},
|
||||
});
|
||||
this.baseUrl = options.baseURL;
|
||||
this.options = options;
|
||||
}
|
||||
/**
|
||||
* Create a new entry in the Rekor log.
|
||||
@@ -14072,13 +14246,17 @@ class Rekor {
|
||||
* @returns {Promise<Entry>} The created entry
|
||||
*/
|
||||
async createEntry(propsedEntry) {
|
||||
const url = `${this.baseUrl}/api/v1/log/entries`;
|
||||
const response = await this.fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
const { baseURL, timeout, retry } = this.options;
|
||||
const url = `${baseURL}/api/v1/log/entries`;
|
||||
const response = await (0, fetch_1.fetchWithRetry)(url, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
body: JSON.stringify(propsedEntry),
|
||||
timeout,
|
||||
retry,
|
||||
});
|
||||
await (0, error_1.checkStatus)(response);
|
||||
const data = await response.json();
|
||||
return entryFromResponse(data);
|
||||
}
|
||||
@@ -14088,45 +14266,19 @@ class Rekor {
|
||||
* @returns {Promise<Entry>} The retrieved entry
|
||||
*/
|
||||
async getEntry(uuid) {
|
||||
const url = `${this.baseUrl}/api/v1/log/entries/${uuid}`;
|
||||
const response = await this.fetch(url);
|
||||
await (0, error_1.checkStatus)(response);
|
||||
const { baseURL, timeout, retry } = this.options;
|
||||
const url = `${baseURL}/api/v1/log/entries/${uuid}`;
|
||||
const response = await (0, fetch_1.fetchWithRetry)(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout,
|
||||
retry,
|
||||
});
|
||||
const data = await response.json();
|
||||
return entryFromResponse(data);
|
||||
}
|
||||
/**
|
||||
* Search the Rekor log index for entries matching the given query.
|
||||
* @param opts {SearchIndex} Options to search the Rekor log
|
||||
* @returns {Promise<string[]>} UUIDs of matching entries
|
||||
*/
|
||||
async searchIndex(opts) {
|
||||
const url = `${this.baseUrl}/api/v1/index/retrieve`;
|
||||
const response = await this.fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(opts),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
await (0, error_1.checkStatus)(response);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Search the Rekor logs for matching the given query.
|
||||
* @param opts {SearchLogQuery} Query to search the Rekor log
|
||||
* @returns {Promise<Entry[]>} List of matching entries
|
||||
*/
|
||||
async searchLog(opts) {
|
||||
const url = `${this.baseUrl}/api/v1/log/entries/retrieve`;
|
||||
const response = await this.fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(opts),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
await (0, error_1.checkStatus)(response);
|
||||
const rawData = await response.json();
|
||||
const data = rawData.map((d) => entryFromResponse(d));
|
||||
return data;
|
||||
}
|
||||
}
|
||||
exports.Rekor = Rekor;
|
||||
// Unpack the response from the Rekor API into a more convenient format.
|
||||
@@ -14147,13 +14299,10 @@ function entryFromResponse(data) {
|
||||
/***/ }),
|
||||
|
||||
/***/ 82759:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.TimestampAuthority = void 0;
|
||||
/*
|
||||
@@ -14171,28 +14320,22 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
|
||||
const util_1 = __nccwpck_require__(90724);
|
||||
const error_1 = __nccwpck_require__(11294);
|
||||
const fetch_1 = __nccwpck_require__(78509);
|
||||
class TimestampAuthority {
|
||||
constructor(options) {
|
||||
this.fetch = make_fetch_happen_1.default.defaults({
|
||||
retry: options.retry,
|
||||
timeout: options.timeout,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': util_1.ua.getUserAgent(),
|
||||
},
|
||||
});
|
||||
this.baseUrl = options.baseURL;
|
||||
this.options = options;
|
||||
}
|
||||
async createTimestamp(request) {
|
||||
const url = `${this.baseUrl}/api/v1/timestamp`;
|
||||
const response = await this.fetch(url, {
|
||||
method: 'POST',
|
||||
const { baseURL, timeout, retry } = this.options;
|
||||
const url = `${baseURL}/api/v1/timestamp`;
|
||||
const response = await (0, fetch_1.fetchWithRetry)(url, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
timeout,
|
||||
retry,
|
||||
});
|
||||
await (0, error_1.checkStatus)(response);
|
||||
return response.buffer();
|
||||
}
|
||||
}
|
||||
@@ -40919,6 +41062,8 @@ class CacheEntry {
|
||||
const cacheWritePromise = new Promise((resolve, reject) => {
|
||||
cacheWriteResolve = resolve
|
||||
cacheWriteReject = reject
|
||||
}).catch((err) => {
|
||||
body.emit('error', err)
|
||||
})
|
||||
|
||||
body = new CachingMinipassPipeline({ events: ['integrity', 'size'] }, new MinipassFlush({
|
||||
@@ -41673,6 +41818,7 @@ const { Minipass } = __nccwpck_require__(14968)
|
||||
const fetch = __nccwpck_require__(68998)
|
||||
const promiseRetry = __nccwpck_require__(54742)
|
||||
const ssri = __nccwpck_require__(4406)
|
||||
const { log } = __nccwpck_require__(56528)
|
||||
|
||||
const CachingMinipassPipeline = __nccwpck_require__(61064)
|
||||
const { getAgent } = __nccwpck_require__(79907)
|
||||
@@ -41760,6 +41906,8 @@ const remoteFetch = (request, options) => {
|
||||
options.onRetry(res)
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line max-len */
|
||||
log.http('fetch', `${req.method} ${req.url} attempt ${attemptNum} failed with ${res.status}`)
|
||||
return retryHandler(res)
|
||||
}
|
||||
|
||||
@@ -41783,6 +41931,7 @@ const remoteFetch = (request, options) => {
|
||||
options.onRetry(err)
|
||||
}
|
||||
|
||||
log.http('fetch', `${req.method} ${req.url} attempt ${attemptNum} failed with ${err.code}`)
|
||||
return retryHandler(err)
|
||||
}
|
||||
}, options.retry).catch((err) => {
|
||||
@@ -49022,6 +49171,166 @@ module.exports = async (
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 56528:
|
||||
/***/ ((module) => {
|
||||
|
||||
const META = Symbol('proc-log.meta')
|
||||
module.exports = {
|
||||
META: META,
|
||||
output: {
|
||||
LEVELS: [
|
||||
'standard',
|
||||
'error',
|
||||
'buffer',
|
||||
'flush',
|
||||
],
|
||||
KEYS: {
|
||||
standard: 'standard',
|
||||
error: 'error',
|
||||
buffer: 'buffer',
|
||||
flush: 'flush',
|
||||
},
|
||||
standard: function (...args) {
|
||||
return process.emit('output', 'standard', ...args)
|
||||
},
|
||||
error: function (...args) {
|
||||
return process.emit('output', 'error', ...args)
|
||||
},
|
||||
buffer: function (...args) {
|
||||
return process.emit('output', 'buffer', ...args)
|
||||
},
|
||||
flush: function (...args) {
|
||||
return process.emit('output', 'flush', ...args)
|
||||
},
|
||||
},
|
||||
log: {
|
||||
LEVELS: [
|
||||
'notice',
|
||||
'error',
|
||||
'warn',
|
||||
'info',
|
||||
'verbose',
|
||||
'http',
|
||||
'silly',
|
||||
'timing',
|
||||
'pause',
|
||||
'resume',
|
||||
],
|
||||
KEYS: {
|
||||
notice: 'notice',
|
||||
error: 'error',
|
||||
warn: 'warn',
|
||||
info: 'info',
|
||||
verbose: 'verbose',
|
||||
http: 'http',
|
||||
silly: 'silly',
|
||||
timing: 'timing',
|
||||
pause: 'pause',
|
||||
resume: 'resume',
|
||||
},
|
||||
error: function (...args) {
|
||||
return process.emit('log', 'error', ...args)
|
||||
},
|
||||
notice: function (...args) {
|
||||
return process.emit('log', 'notice', ...args)
|
||||
},
|
||||
warn: function (...args) {
|
||||
return process.emit('log', 'warn', ...args)
|
||||
},
|
||||
info: function (...args) {
|
||||
return process.emit('log', 'info', ...args)
|
||||
},
|
||||
verbose: function (...args) {
|
||||
return process.emit('log', 'verbose', ...args)
|
||||
},
|
||||
http: function (...args) {
|
||||
return process.emit('log', 'http', ...args)
|
||||
},
|
||||
silly: function (...args) {
|
||||
return process.emit('log', 'silly', ...args)
|
||||
},
|
||||
timing: function (...args) {
|
||||
return process.emit('log', 'timing', ...args)
|
||||
},
|
||||
pause: function () {
|
||||
return process.emit('log', 'pause')
|
||||
},
|
||||
resume: function () {
|
||||
return process.emit('log', 'resume')
|
||||
},
|
||||
},
|
||||
time: {
|
||||
LEVELS: [
|
||||
'start',
|
||||
'end',
|
||||
],
|
||||
KEYS: {
|
||||
start: 'start',
|
||||
end: 'end',
|
||||
},
|
||||
start: function (name, fn) {
|
||||
process.emit('time', 'start', name)
|
||||
function end () {
|
||||
return process.emit('time', 'end', name)
|
||||
}
|
||||
if (typeof fn === 'function') {
|
||||
const res = fn()
|
||||
if (res && res.finally) {
|
||||
return res.finally(end)
|
||||
}
|
||||
end()
|
||||
return res
|
||||
}
|
||||
return end
|
||||
},
|
||||
end: function (name) {
|
||||
return process.emit('time', 'end', name)
|
||||
},
|
||||
},
|
||||
input: {
|
||||
LEVELS: [
|
||||
'start',
|
||||
'end',
|
||||
'read',
|
||||
],
|
||||
KEYS: {
|
||||
start: 'start',
|
||||
end: 'end',
|
||||
read: 'read',
|
||||
},
|
||||
start: function (fn) {
|
||||
process.emit('input', 'start')
|
||||
function end () {
|
||||
return process.emit('input', 'end')
|
||||
}
|
||||
if (typeof fn === 'function') {
|
||||
const res = fn()
|
||||
if (res && res.finally) {
|
||||
return res.finally(end)
|
||||
}
|
||||
end()
|
||||
return res
|
||||
}
|
||||
return end
|
||||
},
|
||||
end: function () {
|
||||
return process.emit('input', 'end')
|
||||
},
|
||||
read: function (...args) {
|
||||
let resolve, reject
|
||||
const promise = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve
|
||||
reject = _reject
|
||||
})
|
||||
process.emit('input', 'read', resolve, reject, ...args)
|
||||
return promise
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 54742:
|
||||
@@ -79642,14 +79951,25 @@ const endpoints_1 = __nccwpck_require__(69112);
|
||||
const predicate_1 = __nccwpck_require__(72103);
|
||||
const subject_1 = __nccwpck_require__(95206);
|
||||
const COLOR_CYAN = '\x1B[36m';
|
||||
const COLOR_GRAY = '\x1B[38;5;244m';
|
||||
const COLOR_DEFAULT = '\x1B[39m';
|
||||
const ATTESTATION_FILE_NAME = 'attestation.jsonl';
|
||||
const MAX_SUBJECT_COUNT = 64;
|
||||
const OCI_TIMEOUT = 2000;
|
||||
const OCI_RETRY = 3;
|
||||
/* istanbul ignore next */
|
||||
const logHandler = (level, ...args) => {
|
||||
// Send any HTTP-related log events to the GitHub Actions debug log
|
||||
if (level === 'http') {
|
||||
core.debug(args.join(' '));
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The main function for the action.
|
||||
* @returns {Promise<void>} Resolves when the action is complete.
|
||||
*/
|
||||
async function run() {
|
||||
process.on('log', logHandler);
|
||||
// Provenance visibility will be public ONLY if we can confirm that the
|
||||
// repository is public AND the undocumented "private-signing" arg is NOT set.
|
||||
// Otherwise, it will be private.
|
||||
@@ -79694,13 +80014,17 @@ async function run() {
|
||||
}
|
||||
catch (err) {
|
||||
// Fail the workflow run if an error occurs
|
||||
core.setFailed(err instanceof Error ? err.message : /* istanbul ignore next */ `${err}`);
|
||||
core.setFailed(err instanceof Error ? err : /* istanbul ignore next */ `${err}`);
|
||||
// Log the cause of the error if one is available
|
||||
/* istanbul ignore if */
|
||||
if (err instanceof Error && 'cause' in err) {
|
||||
const innerErr = err.cause;
|
||||
core.debug(innerErr instanceof Error ? innerErr.message : `${innerErr}}`);
|
||||
core.info(mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
process.removeListener('log', logHandler);
|
||||
}
|
||||
}
|
||||
exports.run = run;
|
||||
const createAttestation = async (subject, predicate, sigstoreInstance) => {
|
||||
@@ -79737,14 +80061,19 @@ const createAttestation = async (subject, predicate, sigstoreInstance) => {
|
||||
annotations: {
|
||||
'dev.sigstore.bundle.content': 'dsse-envelope',
|
||||
'dev.sigstore.bundle.predicateType': core.getInput('predicate-type')
|
||||
}
|
||||
},
|
||||
fetchOpts: { timeout: OCI_TIMEOUT, retry: OCI_RETRY }
|
||||
});
|
||||
core.info(highlight('Attestation uploaded to registry'));
|
||||
core.info(`${subject.name}@${artifact.digest}`);
|
||||
}
|
||||
return attestation;
|
||||
};
|
||||
// Emphasis string using ANSI color codes
|
||||
const highlight = (str) => `${COLOR_CYAN}${str}${COLOR_DEFAULT}`;
|
||||
// De-emphasize string using ANSI color codes
|
||||
/* istanbul ignore next */
|
||||
const mute = (str) => `${COLOR_GRAY}${str}${COLOR_DEFAULT}`;
|
||||
const tempDir = () => {
|
||||
const basePath = process.env['RUNNER_TEMP'];
|
||||
/* istanbul ignore if */
|
||||
@@ -79869,6 +80198,9 @@ const subjectFromInputs = async () => {
|
||||
const subjectPath = core.getInput('subject-path', { required: false });
|
||||
const subjectDigest = core.getInput('subject-digest', { required: false });
|
||||
const subjectName = core.getInput('subject-name', { required: false });
|
||||
const pushToRegistry = core.getBooleanInput('push-to-registry', {
|
||||
required: false
|
||||
});
|
||||
if (!subjectPath && !subjectDigest) {
|
||||
throw new Error('One of subject-path or subject-digest must be provided');
|
||||
}
|
||||
@@ -79878,11 +80210,14 @@ const subjectFromInputs = async () => {
|
||||
if (subjectDigest && !subjectName) {
|
||||
throw new Error('subject-name must be provided when using subject-digest');
|
||||
}
|
||||
// If push-to-registry is enabled, ensure the subject name is lowercase
|
||||
// to conform to OCI image naming conventions
|
||||
const name = pushToRegistry ? subjectName.toLowerCase() : subjectName;
|
||||
if (subjectPath) {
|
||||
return await getSubjectFromPath(subjectPath, subjectName);
|
||||
return await getSubjectFromPath(subjectPath, name);
|
||||
}
|
||||
else {
|
||||
return [getSubjectFromDigest(subjectDigest, subjectName)];
|
||||
return [getSubjectFromDigest(subjectDigest, name)];
|
||||
}
|
||||
};
|
||||
exports.subjectFromInputs = subjectFromInputs;
|
||||
@@ -83326,7 +83661,7 @@ exports.LRUCache = LRUCache;
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.Glob = void 0;
|
||||
const minimatch_1 = __nccwpck_require__(7111);
|
||||
const path_scurry_1 = __nccwpck_require__(69569);
|
||||
const path_scurry_1 = __nccwpck_require__(51081);
|
||||
const url_1 = __nccwpck_require__(57310);
|
||||
const pattern_js_1 = __nccwpck_require__(92895);
|
||||
const walker_js_1 = __nccwpck_require__(45548);
|
||||
@@ -89034,7 +89369,7 @@ exports.Minipass = Minipass;
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 69569:
|
||||
/***/ 51081:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
|
||||
"use strict";
|
||||
@@ -89115,21 +89450,21 @@ const IFMT = 0b1111;
|
||||
// mask to unset low 4 bits
|
||||
const IFMT_UNKNOWN = ~IFMT;
|
||||
// set after successfully calling readdir() and getting entries.
|
||||
const READDIR_CALLED = 16;
|
||||
const READDIR_CALLED = 0b0000_0001_0000;
|
||||
// set after a successful lstat()
|
||||
const LSTAT_CALLED = 32;
|
||||
const LSTAT_CALLED = 0b0000_0010_0000;
|
||||
// set if an entry (or one of its parents) is definitely not a dir
|
||||
const ENOTDIR = 64;
|
||||
const ENOTDIR = 0b0000_0100_0000;
|
||||
// set if an entry (or one of its parents) does not exist
|
||||
// (can also be set on lstat errors like EACCES or ENAMETOOLONG)
|
||||
const ENOENT = 128;
|
||||
const ENOENT = 0b0000_1000_0000;
|
||||
// cannot have child entries -- also verify &IFMT is either IFDIR or IFLNK
|
||||
// set if we fail to readlink
|
||||
const ENOREADLINK = 256;
|
||||
const ENOREADLINK = 0b0001_0000_0000;
|
||||
// set if we know realpath() will fail
|
||||
const ENOREALPATH = 512;
|
||||
const ENOREALPATH = 0b0010_0000_0000;
|
||||
const ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
|
||||
const TYPEMASK = 1023;
|
||||
const TYPEMASK = 0b0011_1111_1111;
|
||||
const entToType = (s) => s.isFile()
|
||||
? IFREG
|
||||
: s.isDirectory()
|
||||
@@ -89743,7 +90078,7 @@ class PathBase {
|
||||
/* c8 ignore stop */
|
||||
try {
|
||||
const read = await this.#fs.promises.readlink(this.fullpath());
|
||||
const linkTarget = this.parent.resolve(read);
|
||||
const linkTarget = (await this.parent.realpath())?.resolve(read);
|
||||
if (linkTarget) {
|
||||
return (this.#linkTarget = linkTarget);
|
||||
}
|
||||
@@ -89772,7 +90107,7 @@ class PathBase {
|
||||
/* c8 ignore stop */
|
||||
try {
|
||||
const read = this.#fs.readlinkSync(this.fullpath());
|
||||
const linkTarget = this.parent.resolve(read);
|
||||
const linkTarget = (this.parent.realpathSync())?.resolve(read);
|
||||
if (linkTarget) {
|
||||
return (this.#linkTarget = linkTarget);
|
||||
}
|
||||
@@ -89787,7 +90122,9 @@ class PathBase {
|
||||
this.#type |= READDIR_CALLED;
|
||||
// mark all remaining provisional children as ENOENT
|
||||
for (let p = children.provisional; p < children.length; p++) {
|
||||
children[p].#markENOENT();
|
||||
const c = children[p];
|
||||
if (c)
|
||||
c.#markENOENT();
|
||||
}
|
||||
}
|
||||
#markENOENT() {
|
||||
@@ -93860,7 +94197,7 @@ exports.parse = parse;
|
||||
/***/ ((module) => {
|
||||
|
||||
"use strict";
|
||||
module.exports = {"i8":"2.3.0"};
|
||||
module.exports = {"i8":"2.3.1"};
|
||||
|
||||
/***/ }),
|
||||
|
||||
@@ -93940,7 +94277,7 @@ module.exports = JSON.parse('[["0","\\u0000",128],["a1","。",62],["8140","
|
||||
/***/ ((module) => {
|
||||
|
||||
"use strict";
|
||||
module.exports = JSON.parse('{"name":"make-fetch-happen","version":"13.0.0","description":"Opinionated, caching, retrying fetch client","main":"lib/index.js","files":["bin/","lib/"],"scripts":{"test":"tap","posttest":"npm run lint","eslint":"eslint","lint":"eslint \\"**/*.js\\"","lintfix":"npm run lint -- --fix","postlint":"template-oss-check","snap":"tap","template-oss-apply":"template-oss-apply --force"},"repository":{"type":"git","url":"https://github.com/npm/make-fetch-happen.git"},"keywords":["http","request","fetch","mean girls","caching","cache","subresource integrity"],"author":"GitHub Inc.","license":"ISC","dependencies":{"@npmcli/agent":"^2.0.0","cacache":"^18.0.0","http-cache-semantics":"^4.1.1","is-lambda":"^1.0.1","minipass":"^7.0.2","minipass-fetch":"^3.0.0","minipass-flush":"^1.0.5","minipass-pipeline":"^1.2.4","negotiator":"^0.6.3","promise-retry":"^2.0.1","ssri":"^10.0.0"},"devDependencies":{"@npmcli/eslint-config":"^4.0.0","@npmcli/template-oss":"4.18.0","nock":"^13.2.4","safe-buffer":"^5.2.1","standard-version":"^9.3.2","tap":"^16.0.0"},"engines":{"node":"^16.14.0 || >=18.0.0"},"tap":{"color":1,"files":"test/*.js","check-coverage":true,"timeout":60,"nyc-arg":["--exclude","tap-snapshots/**"]},"templateOSS":{"//@npmcli/template-oss":"This file is partially managed by @npmcli/template-oss. Edits may be overwritten.","ciVersions":["16.14.0","16.x","18.0.0","18.x"],"version":"4.18.0","publish":"true"}}');
|
||||
module.exports = JSON.parse('{"name":"make-fetch-happen","version":"13.0.1","description":"Opinionated, caching, retrying fetch client","main":"lib/index.js","files":["bin/","lib/"],"scripts":{"test":"tap","posttest":"npm run lint","eslint":"eslint","lint":"eslint \\"**/*.{js,cjs,ts,mjs,jsx,tsx}\\"","lintfix":"npm run lint -- --fix","postlint":"template-oss-check","snap":"tap","template-oss-apply":"template-oss-apply --force"},"repository":{"type":"git","url":"https://github.com/npm/make-fetch-happen.git"},"keywords":["http","request","fetch","mean girls","caching","cache","subresource integrity"],"author":"GitHub Inc.","license":"ISC","dependencies":{"@npmcli/agent":"^2.0.0","cacache":"^18.0.0","http-cache-semantics":"^4.1.1","is-lambda":"^1.0.1","minipass":"^7.0.2","minipass-fetch":"^3.0.0","minipass-flush":"^1.0.5","minipass-pipeline":"^1.2.4","negotiator":"^0.6.3","proc-log":"^4.2.0","promise-retry":"^2.0.1","ssri":"^10.0.0"},"devDependencies":{"@npmcli/eslint-config":"^4.0.0","@npmcli/template-oss":"4.21.4","nock":"^13.2.4","safe-buffer":"^5.2.1","standard-version":"^9.3.2","tap":"^16.0.0"},"engines":{"node":"^16.14.0 || >=18.0.0"},"tap":{"color":1,"files":"test/*.js","check-coverage":true,"timeout":60,"nyc-arg":["--exclude","tap-snapshots/**"]},"templateOSS":{"//@npmcli/template-oss":"This file is partially managed by @npmcli/template-oss. Edits may be overwritten.","version":"4.21.4","publish":"true"}}');
|
||||
|
||||
/***/ }),
|
||||
|
||||
|
||||
+19
@@ -2887,6 +2887,25 @@ will be liable to anyone for any damages related to this
|
||||
software or this license, under any kind of legal claim.***
|
||||
|
||||
|
||||
proc-log
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) GitHub, Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
promise-retry
|
||||
MIT
|
||||
Copyright (c) 2014 IndigoUnited
|
||||
|
||||
Generated
+283
-444
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "actions/attest",
|
||||
"description": "Generate signed attestations for workflow artifacts",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.2",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/actions/attest",
|
||||
@@ -72,25 +72,25 @@
|
||||
"@actions/attest": "^1.2.1",
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/glob": "^0.4.0",
|
||||
"@sigstore/oci": "^0.3.0",
|
||||
"@sigstore/oci": "^0.3.3",
|
||||
"csv-parse": "^5.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sigstore/mock": "^0.7.2",
|
||||
"@sigstore/mock": "^0.7.3",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/make-fetch-happen": "^10.0.4",
|
||||
"@types/node": "^20.12.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.8.0",
|
||||
"@typescript-eslint/parser": "^7.8.0",
|
||||
"@types/node": "^20.12.11",
|
||||
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
||||
"@typescript-eslint/parser": "^7.9.0",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-github": "^4.10.2",
|
||||
"eslint-plugin-jest": "^28.3.0",
|
||||
"eslint-plugin-jest": "^28.5.0",
|
||||
"eslint-plugin-jsonc": "^2.15.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"jest": "^29.7.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"markdownlint-cli": "^0.39.0",
|
||||
"markdownlint-cli": "^0.40.0",
|
||||
"nock": "^13.5.4",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-eslint": "^16.3.0",
|
||||
|
||||
+28
-3
@@ -13,16 +13,30 @@ type SigstoreInstance = 'public-good' | 'github'
|
||||
type AttestedSubject = { subject: Subject; attestationID: string }
|
||||
|
||||
const COLOR_CYAN = '\x1B[36m'
|
||||
const COLOR_GRAY = '\x1B[38;5;244m'
|
||||
const COLOR_DEFAULT = '\x1B[39m'
|
||||
const ATTESTATION_FILE_NAME = 'attestation.jsonl'
|
||||
|
||||
const MAX_SUBJECT_COUNT = 64
|
||||
|
||||
const OCI_TIMEOUT = 2000
|
||||
const OCI_RETRY = 3
|
||||
|
||||
/* istanbul ignore next */
|
||||
const logHandler = (level: string, ...args: unknown[]): void => {
|
||||
// Send any HTTP-related log events to the GitHub Actions debug log
|
||||
if (level === 'http') {
|
||||
core.debug(args.join(' '))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function for the action.
|
||||
* @returns {Promise<void>} Resolves when the action is complete.
|
||||
*/
|
||||
export async function run(): Promise<void> {
|
||||
process.on('log', logHandler)
|
||||
|
||||
// Provenance visibility will be public ONLY if we can confirm that the
|
||||
// repository is public AND the undocumented "private-signing" arg is NOT set.
|
||||
// Otherwise, it will be private.
|
||||
@@ -86,14 +100,19 @@ export async function run(): Promise<void> {
|
||||
} catch (err) {
|
||||
// Fail the workflow run if an error occurs
|
||||
core.setFailed(
|
||||
err instanceof Error ? err.message : /* istanbul ignore next */ `${err}`
|
||||
err instanceof Error ? err : /* istanbul ignore next */ `${err}`
|
||||
)
|
||||
|
||||
// Log the cause of the error if one is available
|
||||
/* istanbul ignore if */
|
||||
if (err instanceof Error && 'cause' in err) {
|
||||
const innerErr = err.cause
|
||||
core.debug(innerErr instanceof Error ? innerErr.message : `${innerErr}}`)
|
||||
core.info(
|
||||
mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`)
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
process.removeListener('log', logHandler)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +166,8 @@ const createAttestation = async (
|
||||
annotations: {
|
||||
'dev.sigstore.bundle.content': 'dsse-envelope',
|
||||
'dev.sigstore.bundle.predicateType': core.getInput('predicate-type')
|
||||
}
|
||||
},
|
||||
fetchOpts: { timeout: OCI_TIMEOUT, retry: OCI_RETRY }
|
||||
})
|
||||
core.info(highlight('Attestation uploaded to registry'))
|
||||
core.info(`${subject.name}@${artifact.digest}`)
|
||||
@@ -156,8 +176,13 @@ const createAttestation = async (
|
||||
return attestation
|
||||
}
|
||||
|
||||
// Emphasis string using ANSI color codes
|
||||
const highlight = (str: string): string => `${COLOR_CYAN}${str}${COLOR_DEFAULT}`
|
||||
|
||||
// De-emphasize string using ANSI color codes
|
||||
/* istanbul ignore next */
|
||||
const mute = (str: string): string => `${COLOR_GRAY}${str}${COLOR_DEFAULT}`
|
||||
|
||||
const tempDir = (): string => {
|
||||
const basePath = process.env['RUNNER_TEMP']
|
||||
|
||||
|
||||
+9
-2
@@ -17,6 +17,9 @@ export const subjectFromInputs = async (): Promise<Subject[]> => {
|
||||
const subjectPath = core.getInput('subject-path', { required: false })
|
||||
const subjectDigest = core.getInput('subject-digest', { required: false })
|
||||
const subjectName = core.getInput('subject-name', { required: false })
|
||||
const pushToRegistry = core.getBooleanInput('push-to-registry', {
|
||||
required: false
|
||||
})
|
||||
|
||||
if (!subjectPath && !subjectDigest) {
|
||||
throw new Error('One of subject-path or subject-digest must be provided')
|
||||
@@ -32,10 +35,14 @@ export const subjectFromInputs = async (): Promise<Subject[]> => {
|
||||
throw new Error('subject-name must be provided when using subject-digest')
|
||||
}
|
||||
|
||||
// If push-to-registry is enabled, ensure the subject name is lowercase
|
||||
// to conform to OCI image naming conventions
|
||||
const name = pushToRegistry ? subjectName.toLowerCase() : subjectName
|
||||
|
||||
if (subjectPath) {
|
||||
return await getSubjectFromPath(subjectPath, subjectName)
|
||||
return await getSubjectFromPath(subjectPath, name)
|
||||
} else {
|
||||
return [getSubjectFromDigest(subjectDigest, subjectName)]
|
||||
return [getSubjectFromDigest(subjectDigest, name)]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user