Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 32795ed917 | |||
| 4fa34e85c5 | |||
| 9e752e3d76 | |||
| a0652efe33 | |||
| 5b17eb7cb0 | |||
| faa6467995 | |||
| 3ff4eb4c69 | |||
| 074a7714de | |||
| 72776582f8 | |||
| e4e9a599b8 | |||
| 80d9f23382 |
@@ -65,7 +65,7 @@ See [action.yml](action.yml)
|
||||
with:
|
||||
# Path to the artifact serving as the subject of the attestation. Must
|
||||
# specify exactly one of "subject-path" or "subject-digest". May contain
|
||||
# a glob pattern or list of paths (total subject count cannot exceed 64).
|
||||
# a glob pattern or list of paths (total subject count cannot exceed 2500).
|
||||
subject-path:
|
||||
|
||||
# SHA256 digest of the subject for the attestation. Must be in the form
|
||||
@@ -81,12 +81,14 @@ See [action.yml](action.yml)
|
||||
# URI identifying the type of the predicate.
|
||||
predicate-type:
|
||||
|
||||
# JSON string containing the value for the attestation predicate. Must
|
||||
# supply exactly one of "predicate-path" or "predicate".
|
||||
# String containing the value for the attestation predicate. String length
|
||||
# cannot exceed 16MB. Must supply exactly one of "predicate-path" or
|
||||
# "predicate".
|
||||
predicate:
|
||||
|
||||
# Path to the file which contains the JSON content for the attestation
|
||||
# predicate. Must supply exactly one of "predicate-path" or "predicate".
|
||||
# Path to the file which contains the content for the attestation predicate.
|
||||
# File size cannot exceed 16MB. Must supply exactly one of "predicate-path"
|
||||
# or "predicate".
|
||||
predicate-path:
|
||||
|
||||
# Whether to push the attestation to the image registry. Requires that the
|
||||
@@ -115,6 +117,20 @@ If multiple subjects are being attested at the same time, each attestation will
|
||||
be written to the output file on a separate line (using the [JSON Lines][7]
|
||||
format).
|
||||
|
||||
## Attestation Limits
|
||||
|
||||
### Subject Limits
|
||||
|
||||
No more than 2500 subjects can be attested at the same time. Subjects will be
|
||||
processed in batches 50. After the initial group of 50, each subsequent batch
|
||||
will incur an exponentially increasing amount of delay (capped at 1 minute of
|
||||
delay per batch) to avoid overwhelming the attestation API.
|
||||
|
||||
### Predicate Limits
|
||||
|
||||
Whether supplied via the `predicate` or `predicatePath` input, the predicate
|
||||
string cannot exceed 16MB.
|
||||
|
||||
## Examples
|
||||
|
||||
### Identify Subject by Path
|
||||
@@ -175,8 +191,8 @@ 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 "docker.io" as the
|
||||
> registry portion of the image name.
|
||||
> **NOTE**: When pushing to Docker Hub, please use "docker.io" as the registry
|
||||
> portion of the image name.
|
||||
|
||||
```yaml
|
||||
name: build-attested-image
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
* Unit tests for the action's entrypoint, src/index.ts
|
||||
*/
|
||||
|
||||
import * as core from '@actions/core'
|
||||
import * as main from '../src/main'
|
||||
|
||||
// Mock the action's entrypoint
|
||||
const runMock = jest.spyOn(main, 'run').mockImplementation()
|
||||
const getBooleanInputMock = jest.spyOn(core, 'getBooleanInput')
|
||||
|
||||
describe('index', () => {
|
||||
beforeEach(() => {
|
||||
getBooleanInputMock.mockImplementation(() => false)
|
||||
})
|
||||
it('calls run when imported', async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require('../src/index')
|
||||
|
||||
+139
-61
@@ -20,8 +20,6 @@ import * as main from '../src/main'
|
||||
// Mock the GitHub Actions core library
|
||||
const infoMock = jest.spyOn(core, 'info')
|
||||
const startGroupMock = jest.spyOn(core, 'startGroup')
|
||||
const getInputMock = jest.spyOn(core, 'getInput')
|
||||
const getBooleanInputMock = jest.spyOn(core, 'getBooleanInput')
|
||||
const setOutputMock = jest.spyOn(core, 'setOutput')
|
||||
const setFailedMock = jest.spyOn(core, 'setFailed')
|
||||
|
||||
@@ -38,12 +36,28 @@ const runMock = jest.spyOn(main, 'run')
|
||||
const mockAgent = new MockAgent()
|
||||
setGlobalDispatcher(mockAgent)
|
||||
|
||||
const defaultInputs: main.RunInputs = {
|
||||
predicate: '',
|
||||
predicateType: '',
|
||||
predicatePath: '',
|
||||
subjectName: '',
|
||||
subjectDigest: '',
|
||||
subjectPath: '',
|
||||
pushToRegistry: false,
|
||||
githubToken: '',
|
||||
privateSigning: false,
|
||||
batchSize: 50
|
||||
}
|
||||
|
||||
describe('action', () => {
|
||||
// Capture original environment variables and GitHub context so we can restore
|
||||
// them after each test
|
||||
const originalEnv = process.env
|
||||
const originalContext = { ...github.context }
|
||||
|
||||
// Mock OIDC token endpoint
|
||||
const tokenURL = 'https://token.url'
|
||||
|
||||
// Fake an OIDC token
|
||||
const oidcSubject = 'foo@bar.com'
|
||||
const oidcPayload = { sub: oidcSubject, iss: '' }
|
||||
@@ -62,9 +76,6 @@ describe('action', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
// Mock OIDC token endpoint
|
||||
const tokenURL = 'https://token.url'
|
||||
|
||||
nock(tokenURL)
|
||||
.get('/')
|
||||
.query({ audience: 'sigstore' })
|
||||
@@ -95,24 +106,22 @@ describe('action', () => {
|
||||
})
|
||||
|
||||
describe('when ACTIONS_ID_TOKEN_REQUEST_URL is not set', () => {
|
||||
const inputs = {
|
||||
'subject-digest': subjectDigest,
|
||||
'subject-name': subjectName,
|
||||
'predicate-type': predicateType,
|
||||
const inputs: main.RunInputs = {
|
||||
...defaultInputs,
|
||||
subjectDigest,
|
||||
subjectName,
|
||||
predicateType,
|
||||
predicate,
|
||||
'github-token': 'gh-token'
|
||||
githubToken: 'gh-token'
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
// Nullify the OIDC token URL
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_URL = ''
|
||||
|
||||
getInputMock.mockImplementation(mockInput(inputs))
|
||||
getBooleanInputMock.mockImplementation(() => false)
|
||||
})
|
||||
|
||||
it('sets a failed status', async () => {
|
||||
await main.run()
|
||||
await main.run(inputs)
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
@@ -124,12 +133,8 @@ describe('action', () => {
|
||||
})
|
||||
|
||||
describe('when no inputs are provided', () => {
|
||||
beforeEach(() => {
|
||||
getInputMock.mockImplementation(() => '')
|
||||
})
|
||||
|
||||
it('sets a failed status', async () => {
|
||||
await main.run()
|
||||
await main.run(defaultInputs)
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
@@ -139,12 +144,13 @@ describe('action', () => {
|
||||
})
|
||||
|
||||
describe('when the repository is private', () => {
|
||||
const inputs = {
|
||||
'subject-digest': subjectDigest,
|
||||
'subject-name': subjectName,
|
||||
'predicate-type': predicateType,
|
||||
const inputs: main.RunInputs = {
|
||||
...defaultInputs,
|
||||
subjectDigest,
|
||||
subjectName,
|
||||
predicateType,
|
||||
predicate,
|
||||
'github-token': 'gh-token'
|
||||
githubToken: 'gh-token'
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -154,9 +160,6 @@ describe('action', () => {
|
||||
repo: { owner: 'foo', repo: 'bar' }
|
||||
})
|
||||
|
||||
getInputMock.mockImplementation(mockInput(inputs))
|
||||
getBooleanInputMock.mockImplementation(() => false)
|
||||
|
||||
await mockFulcio({
|
||||
baseURL: 'https://fulcio.githubapp.com',
|
||||
strict: false
|
||||
@@ -165,7 +168,7 @@ describe('action', () => {
|
||||
})
|
||||
|
||||
it('invokes the action w/o error', async () => {
|
||||
await main.run()
|
||||
await main.run(inputs)
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).not.toHaveBeenCalledWith()
|
||||
@@ -204,12 +207,14 @@ describe('action', () => {
|
||||
const getRegCredsSpy = jest.spyOn(oci, 'getRegistryCredentials')
|
||||
const attachArtifactSpy = jest.spyOn(oci, 'attachArtifactToImage')
|
||||
|
||||
const inputs = {
|
||||
'subject-digest': subjectDigest,
|
||||
'subject-name': subjectName,
|
||||
'predicate-type': predicateType,
|
||||
const inputs: main.RunInputs = {
|
||||
...defaultInputs,
|
||||
subjectDigest,
|
||||
subjectName,
|
||||
predicateType,
|
||||
predicate,
|
||||
'github-token': 'gh-token'
|
||||
githubToken: 'gh-token',
|
||||
pushToRegistry: true
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -219,11 +224,6 @@ describe('action', () => {
|
||||
repo: { owner: 'foo', repo: 'bar' }
|
||||
})
|
||||
|
||||
// Mock the action's inputs
|
||||
getInputMock.mockImplementation(mockInput(inputs))
|
||||
// This is where we mock the push-to-registry input
|
||||
getBooleanInputMock.mockImplementation(() => true)
|
||||
|
||||
await mockFulcio({
|
||||
baseURL: 'https://fulcio.sigstore.dev',
|
||||
strict: false
|
||||
@@ -244,7 +244,7 @@ describe('action', () => {
|
||||
})
|
||||
|
||||
it('invokes the action w/o error', async () => {
|
||||
await main.run()
|
||||
await main.run(inputs)
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).not.toHaveBeenCalled()
|
||||
@@ -289,11 +289,16 @@ describe('action', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('when too many subjects are specified', () => {
|
||||
describe('when the subject count exceeds the batch size', () => {
|
||||
let dir = ''
|
||||
const filename = 'subject'
|
||||
let scope: nock.Scope
|
||||
|
||||
beforeEach(async () => {
|
||||
const filename = 'subject'
|
||||
// Start from scratch
|
||||
nock.cleanAll()
|
||||
|
||||
const subjectCount = 5
|
||||
const content = 'file content'
|
||||
|
||||
// Set-up temp directory
|
||||
@@ -301,7 +306,90 @@ describe('action', () => {
|
||||
dir = await fs.mkdtemp(tmpDir + path.sep)
|
||||
|
||||
// Add files for glob testing
|
||||
for (let i = 0; i < 65; i++) {
|
||||
for (let i = 0; i < subjectCount; i++) {
|
||||
await fs.writeFile(path.join(dir, `${filename}-${i}`), content)
|
||||
|
||||
// Set-up a Fulcio mock for each subject
|
||||
await mockFulcio({
|
||||
baseURL: 'https://fulcio.githubapp.com',
|
||||
strict: false
|
||||
})
|
||||
|
||||
// Set-up a TSA mock for each subject
|
||||
await mockTSA({ baseURL: 'https://timestamp.githubapp.com' })
|
||||
|
||||
// Set-up a GH API mock for each subject
|
||||
mockAgent
|
||||
.get('https://api.github.com')
|
||||
.intercept({
|
||||
path: /^\/repos\/.*\/.*\/attestations$/,
|
||||
method: 'post'
|
||||
})
|
||||
.reply(201, { id: attestationID })
|
||||
}
|
||||
|
||||
// Set-up a OIDC token mock for each subject
|
||||
scope = nock(tokenURL)
|
||||
.get('/')
|
||||
.query({ audience: 'sigstore' })
|
||||
.times(subjectCount)
|
||||
.reply(200, { value: oidcToken })
|
||||
|
||||
// Set the GH context with private repository visibility and a repo owner.
|
||||
setGHContext({
|
||||
payload: { repository: { visibility: 'private' } },
|
||||
repo: { owner: 'foo', repo: 'bar' }
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean-up temp directory
|
||||
await fs.rm(dir, { recursive: true })
|
||||
})
|
||||
|
||||
it('invokes the action w/o error', async () => {
|
||||
const inputs: main.RunInputs = {
|
||||
...defaultInputs,
|
||||
subjectPath: path.join(dir, `${filename}-*`),
|
||||
predicateType,
|
||||
predicate,
|
||||
githubToken: 'gh-token',
|
||||
batchSize: 2
|
||||
}
|
||||
await main.run(inputs)
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).not.toHaveBeenCalled()
|
||||
expect(infoMock).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.stringMatching('Processing subject batch 1/3')
|
||||
)
|
||||
expect(infoMock).toHaveBeenNthCalledWith(
|
||||
10,
|
||||
expect.stringMatching('Processing subject batch 2/3')
|
||||
)
|
||||
expect(infoMock).toHaveBeenNthCalledWith(
|
||||
19,
|
||||
expect.stringMatching('Processing subject batch 3/3')
|
||||
)
|
||||
expect(scope.isDone()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the subject count exceeds the max', () => {
|
||||
let dir = ''
|
||||
const filename = 'subject'
|
||||
|
||||
beforeEach(async () => {
|
||||
const subjectCount = 2501
|
||||
const content = 'file content'
|
||||
|
||||
// Set-up temp directory
|
||||
const tmpDir = await fs.realpath(os.tmpdir())
|
||||
dir = await fs.mkdtemp(tmpDir + path.sep)
|
||||
|
||||
// Add files for glob testing
|
||||
for (let i = 0; i < subjectCount; i++) {
|
||||
await fs.writeFile(path.join(dir, `${filename}-${i}`), content)
|
||||
}
|
||||
|
||||
@@ -310,14 +398,6 @@ describe('action', () => {
|
||||
payload: { repository: { visibility: 'private' } },
|
||||
repo: { owner: 'foo', repo: 'bar' }
|
||||
})
|
||||
|
||||
// Mock the action's inputs
|
||||
getInputMock.mockImplementation(
|
||||
mockInput({
|
||||
predicate: '{}',
|
||||
'subject-path': path.join(dir, `${filename}-*`)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -326,27 +406,25 @@ describe('action', () => {
|
||||
})
|
||||
|
||||
it('sets a failed status', async () => {
|
||||
await main.run()
|
||||
const inputs: main.RunInputs = {
|
||||
...defaultInputs,
|
||||
subjectPath: path.join(dir, `${filename}-*`),
|
||||
predicateType,
|
||||
predicate,
|
||||
githubToken: 'gh-token'
|
||||
}
|
||||
await main.run(inputs)
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
new Error(
|
||||
'Too many subjects specified. The maximum number of subjects is 64.'
|
||||
'Too many subjects specified. The maximum number of subjects is 2500.'
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function mockInput(inputs: Record<string, string>): typeof core.getInput {
|
||||
return (name: string): string => {
|
||||
if (name in inputs) {
|
||||
return inputs[name]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
// Stubbing the GitHub context is a bit tricky. We need to use
|
||||
// `Object.defineProperty` because `github.context` is read-only.
|
||||
function setGHContext(context: object): void {
|
||||
|
||||
+77
-40
@@ -1,92 +1,129 @@
|
||||
import fs from 'fs/promises'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import { predicateFromInputs } from '../src/predicate'
|
||||
import { predicateFromInputs, PredicateInputs } from '../src/predicate'
|
||||
|
||||
describe('subjectFromInputs', () => {
|
||||
afterEach(() => {
|
||||
process.env['INPUT_PREDICATE'] = ''
|
||||
process.env['INPUT_PREDICATE-PATH'] = ''
|
||||
process.env['INPUT_PREDICATE-TYPE'] = ''
|
||||
})
|
||||
const blankInputs: PredicateInputs = {
|
||||
predicateType: '',
|
||||
predicate: '',
|
||||
predicatePath: ''
|
||||
}
|
||||
|
||||
describe('when no inputs are provided', () => {
|
||||
it('throws an error', () => {
|
||||
expect(() => predicateFromInputs()).toThrow(/predicate-type/i)
|
||||
expect(() => predicateFromInputs(blankInputs)).toThrow(/predicate-type/i)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when neither predicate path nor predicate are provided', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
|
||||
})
|
||||
|
||||
it('throws an error', () => {
|
||||
expect(() => predicateFromInputs()).toThrow(
|
||||
const inputs: PredicateInputs = {
|
||||
...blankInputs,
|
||||
predicateType: 'https://example.com/predicate'
|
||||
}
|
||||
|
||||
expect(() => predicateFromInputs(inputs)).toThrow(
|
||||
/one of predicate-path or predicate must be provided/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when both predicate path and predicate are provided', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PREDICATE-PATH'] = 'path/to/predicate'
|
||||
process.env['INPUT_PREDICATE'] = '{}'
|
||||
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
|
||||
})
|
||||
|
||||
it('throws an error', () => {
|
||||
expect(() => predicateFromInputs()).toThrow(
|
||||
const inputs: PredicateInputs = {
|
||||
predicateType: 'https://example.com/predicate',
|
||||
predicate: '{}',
|
||||
predicatePath: 'path/to/predicate'
|
||||
}
|
||||
|
||||
expect(() => predicateFromInputs(inputs)).toThrow(
|
||||
/only one of predicate-path or predicate may be provided/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a predicate path', () => {
|
||||
let dir = ''
|
||||
const filename = 'subject'
|
||||
const predicateType = 'https://example.com/predicate'
|
||||
const content = '{}'
|
||||
let predicatePath = ''
|
||||
|
||||
beforeEach(async () => {
|
||||
// Set-up temp directory
|
||||
const tmpDir = await fs.realpath(os.tmpdir())
|
||||
dir = await fs.mkdtemp(tmpDir + path.sep)
|
||||
const dir = await fs.mkdtemp(tmpDir + path.sep)
|
||||
|
||||
const filename = 'subject'
|
||||
predicatePath = path.join(dir, filename)
|
||||
|
||||
// Write file to temp directory
|
||||
await fs.writeFile(path.join(dir, filename), content)
|
||||
await fs.writeFile(predicatePath, content)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean-up temp directory
|
||||
await fs.rm(dir, { recursive: true })
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PREDICATE-PATH'] = path.join(dir, filename)
|
||||
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
|
||||
await fs.rm(path.parse(predicatePath).dir, { recursive: true })
|
||||
})
|
||||
|
||||
it('returns the predicate', () => {
|
||||
expect(predicateFromInputs()).toEqual({
|
||||
type: 'https://example.com/predicate',
|
||||
params: {}
|
||||
const inputs: PredicateInputs = {
|
||||
...blankInputs,
|
||||
predicateType,
|
||||
predicatePath
|
||||
}
|
||||
expect(predicateFromInputs(inputs)).toEqual({
|
||||
type: predicateType,
|
||||
params: JSON.parse(content)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a predicate path that does not exist', () => {
|
||||
const predicateType = 'https://example.com/predicate'
|
||||
const predicatePath = 'foo'
|
||||
|
||||
it('returns the predicate', () => {
|
||||
const inputs: PredicateInputs = {
|
||||
...blankInputs,
|
||||
predicateType,
|
||||
predicatePath
|
||||
}
|
||||
expect(() => predicateFromInputs(inputs)).toThrow(/file not found/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a predicate value', () => {
|
||||
const predicateType = 'https://example.com/predicate'
|
||||
const content = '{}'
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PREDICATE'] = content
|
||||
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
|
||||
})
|
||||
|
||||
it('returns the predicate', () => {
|
||||
expect(predicateFromInputs()).toEqual({
|
||||
type: 'https://example.com/predicate',
|
||||
params: {}
|
||||
const inputs: PredicateInputs = {
|
||||
...blankInputs,
|
||||
predicateType,
|
||||
predicate: content
|
||||
}
|
||||
|
||||
expect(predicateFromInputs(inputs)).toEqual({
|
||||
type: predicateType,
|
||||
params: JSON.parse(content)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a predicate value exceeding the max size', () => {
|
||||
const predicateType = 'https://example.com/predicate'
|
||||
const content = JSON.stringify({ a: 'a'.repeat(16 * 1024 * 1024) })
|
||||
|
||||
it('throws an error', () => {
|
||||
const inputs: PredicateInputs = {
|
||||
...blankInputs,
|
||||
predicateType,
|
||||
predicate: content
|
||||
}
|
||||
|
||||
expect(() => predicateFromInputs(inputs)).toThrow(
|
||||
/predicate string exceeds maximum/
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { highlight, mute } from '../src/style'
|
||||
|
||||
describe('style', () => {
|
||||
describe('highlight', () => {
|
||||
it('adds cyan color to the string', () => {
|
||||
expect(highlight('foo')).toBe('\x1B[36mfoo\x1B[39m')
|
||||
})
|
||||
})
|
||||
|
||||
describe('mute', () => {
|
||||
it('adds gray color to the string', () => {
|
||||
expect(mute('foo')).toBe('\x1B[38;5;244mfoo\x1B[39m')
|
||||
})
|
||||
})
|
||||
})
|
||||
+107
-99
@@ -2,47 +2,45 @@ import crypto from 'crypto'
|
||||
import fs from 'fs/promises'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import { subjectFromInputs } from '../src/subject'
|
||||
import { subjectFromInputs, SubjectInputs } from '../src/subject'
|
||||
|
||||
describe('subjectFromInputs', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PUSH-TO-REGISTRY'] = 'false'
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = ''
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = ''
|
||||
process.env['INPUT_SUBJECT-NAME'] = ''
|
||||
})
|
||||
const blankInputs: SubjectInputs = {
|
||||
subjectPath: '',
|
||||
subjectName: '',
|
||||
subjectDigest: ''
|
||||
}
|
||||
|
||||
describe('when no inputs are provided', () => {
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
await expect(subjectFromInputs(blankInputs)).rejects.toThrow(
|
||||
/one of subject-path or subject-digest must be provided/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when both subject path and subject digest are provided', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = 'path/to/subject'
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
const inputs: SubjectInputs = {
|
||||
subjectName: 'foo',
|
||||
subjectPath: 'path/to/subject',
|
||||
subjectDigest: 'digest'
|
||||
}
|
||||
|
||||
await expect(subjectFromInputs(inputs)).rejects.toThrow(
|
||||
/only one of subject-path or subject-digest may be provided/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when subject digest is provided but not the name', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectDigest: 'digest'
|
||||
}
|
||||
|
||||
await expect(subjectFromInputs(inputs)).rejects.toThrow(
|
||||
/subject-name must be provided when using subject-digest/i
|
||||
)
|
||||
})
|
||||
@@ -52,39 +50,42 @@ describe('subjectFromInputs', () => {
|
||||
const name = 'Subject'
|
||||
|
||||
describe('when the digest is malformed', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectDigest: 'digest',
|
||||
subjectName: name
|
||||
}
|
||||
|
||||
await expect(subjectFromInputs(inputs)).rejects.toThrow(
|
||||
/subject-digest must be in the format "sha256:<hex-digest>"/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the alogrithm is not supported', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'md5:deadbeef'
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectDigest: 'md5:deadbeef',
|
||||
subjectName: name
|
||||
}
|
||||
|
||||
await expect(subjectFromInputs(inputs)).rejects.toThrow(
|
||||
/subject-digest must be in the format "sha256:<hex-digest>"/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the sha256 digest is malformed', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'sha256:deadbeef'
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectDigest: 'sha256:deadbeef',
|
||||
subjectName: name
|
||||
}
|
||||
|
||||
await expect(subjectFromInputs(inputs)).rejects.toThrow(
|
||||
/subject-digest must be in the format "sha256:<hex-digest>"/i
|
||||
)
|
||||
})
|
||||
@@ -95,13 +96,14 @@ describe('subjectFromInputs', () => {
|
||||
const digest =
|
||||
'7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = `${alg}:${digest}`
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('returns the subject', async () => {
|
||||
const subject = await subjectFromInputs()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectDigest: `${alg}:${digest}`,
|
||||
subjectName: name
|
||||
}
|
||||
|
||||
const subject = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subject).toBeDefined()
|
||||
expect(subject).toHaveLength(1)
|
||||
@@ -110,20 +112,21 @@ describe('subjectFromInputs', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the push-to-registry is true', () => {
|
||||
describe('when the downcaseName 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()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectDigest: `${alg}:${digest}`,
|
||||
subjectName: imageName,
|
||||
downcaseName: true
|
||||
}
|
||||
|
||||
const subject = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subject).toBeDefined()
|
||||
expect(subject).toHaveLength(1)
|
||||
@@ -135,19 +138,20 @@ describe('subjectFromInputs', () => {
|
||||
|
||||
describe('when specifying a subject path', () => {
|
||||
describe('when the file does NOT exist', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = '/f/a/k/e'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectPath: '/f/a/k/e'
|
||||
}
|
||||
|
||||
await expect(subjectFromInputs(inputs)).rejects.toThrow(
|
||||
/could not find subject at path/i
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the file eixts', () => {
|
||||
describe('when the file exists', () => {
|
||||
let dir = ''
|
||||
const filename = 'subject'
|
||||
const content = 'file content'
|
||||
@@ -178,12 +182,13 @@ describe('subjectFromInputs', () => {
|
||||
})
|
||||
|
||||
describe('when no name is provided', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, filename)
|
||||
})
|
||||
|
||||
it('returns the subject', async () => {
|
||||
const subject = await subjectFromInputs()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectPath: path.join(dir, filename)
|
||||
}
|
||||
|
||||
const subject = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subject).toBeDefined()
|
||||
expect(subject).toHaveLength(1)
|
||||
@@ -195,13 +200,14 @@ describe('subjectFromInputs', () => {
|
||||
describe('when a name is provided', () => {
|
||||
const name = 'mysubject'
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, filename)
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('returns the subject', async () => {
|
||||
const subject = await subjectFromInputs()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectPath: path.join(dir, filename),
|
||||
subjectName: name
|
||||
}
|
||||
|
||||
const subject = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subject).toBeDefined()
|
||||
expect(subject).toHaveLength(1)
|
||||
@@ -211,12 +217,13 @@ describe('subjectFromInputs', () => {
|
||||
})
|
||||
|
||||
describe('when a file glob is supplied', () => {
|
||||
beforeEach(async () => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, 'subject-*')
|
||||
})
|
||||
|
||||
it('returns the multiple subjects', async () => {
|
||||
const subjects = await subjectFromInputs()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectPath: path.join(dir, 'subject-*')
|
||||
}
|
||||
|
||||
const subjects = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subjects).toBeDefined()
|
||||
expect(subjects).toHaveLength(3)
|
||||
@@ -230,12 +237,13 @@ describe('subjectFromInputs', () => {
|
||||
})
|
||||
|
||||
describe('when a file glob is supplied which also matches non-files', () => {
|
||||
beforeEach(async () => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = `${dir}*`
|
||||
})
|
||||
|
||||
it('returns the subjects (excluding non-files)', async () => {
|
||||
const subjects = await subjectFromInputs()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectPath: `${dir}*`
|
||||
}
|
||||
|
||||
const subjects = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subjects).toBeDefined()
|
||||
expect(subjects).toHaveLength(7)
|
||||
@@ -243,13 +251,13 @@ describe('subjectFromInputs', () => {
|
||||
})
|
||||
|
||||
describe('when a comma-separated list is supplied', () => {
|
||||
beforeEach(async () => {
|
||||
process.env['INPUT_SUBJECT-PATH'] =
|
||||
`${path.join(dir, 'subject-1')},${path.join(dir, 'subject-2')}`
|
||||
})
|
||||
|
||||
it('returns the multiple subjects', async () => {
|
||||
const subjects = await subjectFromInputs()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectPath: `${path.join(dir, 'subject-1')},${path.join(dir, 'subject-2')}`
|
||||
}
|
||||
|
||||
const subjects = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subjects).toBeDefined()
|
||||
expect(subjects).toHaveLength(2)
|
||||
@@ -266,13 +274,13 @@ describe('subjectFromInputs', () => {
|
||||
})
|
||||
|
||||
describe('when a multi-line list is supplied', () => {
|
||||
beforeEach(async () => {
|
||||
process.env['INPUT_SUBJECT-PATH'] =
|
||||
`${path.join(dir, 'subject-0')}\n${path.join(dir, 'subject-2')}`
|
||||
})
|
||||
|
||||
it('returns the multiple subjects', async () => {
|
||||
const subjects = await subjectFromInputs()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectPath: `${path.join(dir, 'subject-0')}\n${path.join(dir, 'subject-2')}`
|
||||
}
|
||||
|
||||
const subjects = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subjects).toBeDefined()
|
||||
expect(subjects).toHaveLength(2)
|
||||
@@ -289,13 +297,13 @@ describe('subjectFromInputs', () => {
|
||||
})
|
||||
|
||||
describe('when a multi-line glob list is supplied', () => {
|
||||
beforeEach(async () => {
|
||||
process.env['INPUT_SUBJECT-PATH'] =
|
||||
`${path.join(dir, 'subject-*')}\n ${path.join(dir, 'other-*')} `
|
||||
})
|
||||
|
||||
it('returns the multiple subjects', async () => {
|
||||
const subjects = await subjectFromInputs()
|
||||
const inputs: SubjectInputs = {
|
||||
...blankInputs,
|
||||
subjectPath: `${path.join(dir, 'subject-*')}\n ${path.join(dir, 'other-*')} `
|
||||
}
|
||||
|
||||
const subjects = await subjectFromInputs(inputs)
|
||||
|
||||
expect(subjects).toBeDefined()
|
||||
expect(subjects).toHaveLength(6)
|
||||
|
||||
+6
-4
@@ -10,7 +10,7 @@ inputs:
|
||||
description: >
|
||||
Path to the artifact serving as the subject of the attestation. Must
|
||||
specify exactly one of "subject-path" or "subject-digest". May contain a
|
||||
glob pattern or list of paths (total subject count cannot exceed 64).
|
||||
glob pattern or list of paths (total subject count cannot exceed 2500).
|
||||
required: false
|
||||
subject-digest:
|
||||
description: >
|
||||
@@ -30,13 +30,15 @@ inputs:
|
||||
required: true
|
||||
predicate:
|
||||
description: >
|
||||
String containing the value for the attestation predicate. Must supply
|
||||
exactly one of "predicate-path" or "predicate".
|
||||
String containing the value for the attestation predicate. String length
|
||||
cannot exceed 16MB. Must supply exactly one of "predicate-path" or
|
||||
"predicate".
|
||||
required: false
|
||||
predicate-path:
|
||||
description: >
|
||||
Path to the file which contains the content for the attestation predicate.
|
||||
Must supply exactly one of "predicate-path" or "predicate".
|
||||
File size cannot exceed 16MB. Must supply exactly one of "predicate-path"
|
||||
or "predicate".
|
||||
required: false
|
||||
push-to-registry:
|
||||
description: >
|
||||
|
||||
+359
-228
@@ -11814,7 +11814,7 @@ async function _OCIImage_createReferrersIndexByTag(opts) {
|
||||
if (!referrerManifest.manifests.some((manifest) => manifest.digest === opts.artifact.digest)) {
|
||||
// Add the artifact to the index
|
||||
referrerManifest.manifests.push(opts.artifact);
|
||||
__classPrivateFieldGet(this, _OCIImage_client, "f").uploadManifest(JSON.stringify(referrerManifest), {
|
||||
await __classPrivateFieldGet(this, _OCIImage_client, "f").uploadManifest(JSON.stringify(referrerManifest), {
|
||||
mediaType: constants_1.CONTENT_TYPE_OCI_INDEX,
|
||||
reference: referrerTag,
|
||||
etag,
|
||||
@@ -13199,7 +13199,7 @@ exports.ClientTrustConfig = exports.SigningConfig = exports.TrustedRoot = export
|
||||
/* eslint-disable */
|
||||
const sigstore_common_1 = __nccwpck_require__(82193);
|
||||
function createBaseTransparencyLogInstance() {
|
||||
return { baseUrl: "", hashAlgorithm: 0, publicKey: undefined, logId: undefined };
|
||||
return { baseUrl: "", hashAlgorithm: 0, publicKey: undefined, logId: undefined, checkpointKeyId: undefined };
|
||||
}
|
||||
exports.TransparencyLogInstance = {
|
||||
fromJSON(object) {
|
||||
@@ -13208,6 +13208,7 @@ exports.TransparencyLogInstance = {
|
||||
hashAlgorithm: isSet(object.hashAlgorithm) ? (0, sigstore_common_1.hashAlgorithmFromJSON)(object.hashAlgorithm) : 0,
|
||||
publicKey: isSet(object.publicKey) ? sigstore_common_1.PublicKey.fromJSON(object.publicKey) : undefined,
|
||||
logId: isSet(object.logId) ? sigstore_common_1.LogId.fromJSON(object.logId) : undefined,
|
||||
checkpointKeyId: isSet(object.checkpointKeyId) ? sigstore_common_1.LogId.fromJSON(object.checkpointKeyId) : undefined,
|
||||
};
|
||||
},
|
||||
toJSON(message) {
|
||||
@@ -13217,6 +13218,8 @@ exports.TransparencyLogInstance = {
|
||||
message.publicKey !== undefined &&
|
||||
(obj.publicKey = message.publicKey ? sigstore_common_1.PublicKey.toJSON(message.publicKey) : undefined);
|
||||
message.logId !== undefined && (obj.logId = message.logId ? sigstore_common_1.LogId.toJSON(message.logId) : undefined);
|
||||
message.checkpointKeyId !== undefined &&
|
||||
(obj.checkpointKeyId = message.checkpointKeyId ? sigstore_common_1.LogId.toJSON(message.checkpointKeyId) : undefined);
|
||||
return obj;
|
||||
},
|
||||
};
|
||||
@@ -79893,6 +79896,63 @@ function wrappy (fn, cb) {
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 26056:
|
||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.createAttestation = void 0;
|
||||
const attest_1 = __nccwpck_require__(74113);
|
||||
const oci_1 = __nccwpck_require__(47353);
|
||||
const OCI_TIMEOUT = 2000;
|
||||
const OCI_RETRY = 3;
|
||||
const createAttestation = async (subject, predicate, opts) => {
|
||||
// Sign provenance w/ Sigstore
|
||||
const attestation = await (0, attest_1.attest)({
|
||||
subjectName: subject.name,
|
||||
subjectDigest: subject.digest,
|
||||
predicateType: predicate.type,
|
||||
predicate: predicate.params,
|
||||
sigstore: opts.sigstoreInstance,
|
||||
token: opts.githubToken
|
||||
});
|
||||
const subDigest = subjectDigest(subject);
|
||||
const result = {
|
||||
...attestation,
|
||||
subjectName: subject.name,
|
||||
subjectDigest: subDigest
|
||||
};
|
||||
if (opts.pushToRegistry) {
|
||||
const credentials = (0, oci_1.getRegistryCredentials)(subject.name);
|
||||
const artifact = await (0, oci_1.attachArtifactToImage)({
|
||||
credentials,
|
||||
imageName: subject.name,
|
||||
imageDigest: subDigest,
|
||||
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
|
||||
mediaType: attestation.bundle.mediaType,
|
||||
annotations: {
|
||||
'dev.sigstore.bundle.content': 'dsse-envelope',
|
||||
'dev.sigstore.bundle.predicateType': predicate.type
|
||||
},
|
||||
fetchOpts: { timeout: OCI_TIMEOUT, retry: OCI_RETRY }
|
||||
});
|
||||
// Add the attestation's digest to the result
|
||||
result.attestationDigest = artifact.digest;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
exports.createAttestation = createAttestation;
|
||||
// Returns the subject's digest as a formatted string of the form
|
||||
// "<algorithm>:<digest>".
|
||||
const subjectDigest = (subject) => {
|
||||
const alg = Object.keys(subject.digest).sort()[0];
|
||||
return `${alg}:${subject.digest[alg]}`;
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 69112:
|
||||
@@ -79905,6 +79965,61 @@ exports.SEARCH_PUBLIC_GOOD_URL = void 0;
|
||||
exports.SEARCH_PUBLIC_GOOD_URL = 'https://search.sigstore.dev';
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 6144:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
/**
|
||||
* The entrypoint for the action.
|
||||
*/
|
||||
const core = __importStar(__nccwpck_require__(42186));
|
||||
const main_1 = __nccwpck_require__(70399);
|
||||
const DEFAULT_BATCH_SIZE = 50;
|
||||
const inputs = {
|
||||
subjectPath: core.getInput('subject-path'),
|
||||
subjectName: core.getInput('subject-name'),
|
||||
subjectDigest: core.getInput('subject-digest'),
|
||||
predicateType: core.getInput('predicate-type'),
|
||||
predicate: core.getInput('predicate'),
|
||||
predicatePath: core.getInput('predicate-path'),
|
||||
pushToRegistry: core.getBooleanInput('push-to-registry'),
|
||||
githubToken: core.getInput('github-token'),
|
||||
// undocumented -- not part of public interface
|
||||
privateSigning: ['true', 'True', 'TRUE', '1'].includes(core.getInput('private-signing')),
|
||||
// internal only
|
||||
batchSize: DEFAULT_BATCH_SIZE
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
(0, main_1.run)(inputs);
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 70399:
|
||||
@@ -79940,23 +80055,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.run = void 0;
|
||||
const attest_1 = __nccwpck_require__(74113);
|
||||
const core = __importStar(__nccwpck_require__(42186));
|
||||
const github = __importStar(__nccwpck_require__(95438));
|
||||
const oci_1 = __nccwpck_require__(47353);
|
||||
const fs_1 = __importDefault(__nccwpck_require__(57147));
|
||||
const os_1 = __importDefault(__nccwpck_require__(22037));
|
||||
const path_1 = __importDefault(__nccwpck_require__(71017));
|
||||
const attest_1 = __nccwpck_require__(26056);
|
||||
const endpoints_1 = __nccwpck_require__(69112);
|
||||
const predicate_1 = __nccwpck_require__(72103);
|
||||
const style = __importStar(__nccwpck_require__(41583));
|
||||
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;
|
||||
const DELAY_INTERVAL_MS = 75;
|
||||
const DELAY_MAX_MS = 1200;
|
||||
/* istanbul ignore next */
|
||||
const logHandler = (level, ...args) => {
|
||||
// Send any HTTP-related log events to the GitHub Actions debug log
|
||||
@@ -79968,13 +80079,13 @@ const logHandler = (level, ...args) => {
|
||||
* The main function for the action.
|
||||
* @returns {Promise<void>} Resolves when the action is complete.
|
||||
*/
|
||||
async function run() {
|
||||
async function run(inputs) {
|
||||
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.
|
||||
const sigstoreInstance = github.context.payload.repository?.visibility === 'public' &&
|
||||
core.getInput('private-signing') !== 'true'
|
||||
!inputs.privateSigning
|
||||
? 'public-good'
|
||||
: 'github';
|
||||
try {
|
||||
@@ -79982,35 +80093,41 @@ async function run() {
|
||||
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
|
||||
throw new Error('missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.');
|
||||
}
|
||||
// Gather list of subjets
|
||||
const subjects = await (0, subject_1.subjectFromInputs)();
|
||||
if (subjects.length > MAX_SUBJECT_COUNT) {
|
||||
throw new Error(`Too many subjects specified. The maximum number of subjects is ${MAX_SUBJECT_COUNT}.`);
|
||||
}
|
||||
const predicate = (0, predicate_1.predicateFromInputs)();
|
||||
const subjects = await (0, subject_1.subjectFromInputs)({
|
||||
...inputs,
|
||||
downcaseName: inputs.pushToRegistry
|
||||
});
|
||||
const predicate = (0, predicate_1.predicateFromInputs)(inputs);
|
||||
const outputPath = path_1.default.join(tempDir(), ATTESTATION_FILE_NAME);
|
||||
// Generate attestations for each subject serially
|
||||
for (const subject of subjects) {
|
||||
const att = await createAttestation(subject, predicate, sigstoreInstance);
|
||||
// Write attestation bundle to output file
|
||||
fs_1.default.writeFileSync(outputPath, JSON.stringify(att.bundle) + os_1.default.EOL, {
|
||||
encoding: 'utf-8',
|
||||
flag: 'a'
|
||||
});
|
||||
if (att.attestationID) {
|
||||
atts.push({ subject, attestationID: att.attestationID });
|
||||
}
|
||||
}
|
||||
if (atts.length > 0) {
|
||||
core.summary.addHeading(
|
||||
/* istanbul ignore next */
|
||||
atts.length > 1 ? 'Attestations Created' : 'Attestation Created', 3);
|
||||
for (const { subject, attestationID } of atts) {
|
||||
core.summary.addLink(`${subject.name}@${subjectDigest(subject)}`, attestationURL(attestationID));
|
||||
}
|
||||
core.summary.write();
|
||||
}
|
||||
core.setOutput('bundle-path', outputPath);
|
||||
const subjectChunks = chunkArray(subjects, inputs.batchSize);
|
||||
// Generate attestations for each subject serially, working in batches
|
||||
for (let i = 0; i < subjectChunks.length; i++) {
|
||||
if (subjectChunks.length > 1) {
|
||||
core.info(`Processing subject batch ${i + 1}/${subjectChunks.length}`);
|
||||
}
|
||||
// Calculate the delay time for this batch
|
||||
const delayTime = delay(i);
|
||||
for (const subject of subjectChunks[i]) {
|
||||
// Delay between attestations (only when chunk size > 1)
|
||||
if (i > 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, delayTime));
|
||||
}
|
||||
const att = await (0, attest_1.createAttestation)(subject, predicate, {
|
||||
sigstoreInstance,
|
||||
pushToRegistry: inputs.pushToRegistry,
|
||||
githubToken: inputs.githubToken
|
||||
});
|
||||
atts.push(att);
|
||||
logAttestation(att, sigstoreInstance);
|
||||
// Write attestation bundle to output file
|
||||
fs_1.default.writeFileSync(outputPath, JSON.stringify(att.bundle) + os_1.default.EOL, {
|
||||
encoding: 'utf-8',
|
||||
flag: 'a'
|
||||
});
|
||||
}
|
||||
}
|
||||
logSummary(atts);
|
||||
}
|
||||
catch (err) {
|
||||
// Fail the workflow run if an error occurs
|
||||
@@ -80019,7 +80136,7 @@ async function run() {
|
||||
/* istanbul ignore if */
|
||||
if (err instanceof Error && 'cause' in err) {
|
||||
const innerErr = err.cause;
|
||||
core.info(mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`));
|
||||
core.info(style.mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@@ -80027,53 +80144,40 @@ async function run() {
|
||||
}
|
||||
}
|
||||
exports.run = run;
|
||||
const createAttestation = async (subject, predicate, sigstoreInstance) => {
|
||||
// Sign provenance w/ Sigstore
|
||||
const attestation = await (0, attest_1.attest)({
|
||||
subjectName: subject.name,
|
||||
subjectDigest: subject.digest,
|
||||
predicateType: predicate.type,
|
||||
predicate: predicate.params,
|
||||
sigstore: sigstoreInstance,
|
||||
token: core.getInput('github-token')
|
||||
});
|
||||
core.info(`Attestation created for ${subject.name}@${subjectDigest(subject)}`);
|
||||
// Log details about the attestation to the GitHub Actions run
|
||||
const logAttestation = (attestation, sigstoreInstance) => {
|
||||
core.info(`Attestation created for ${attestation.subjectName}@${attestation.subjectDigest}`);
|
||||
const instanceName = sigstoreInstance === 'public-good' ? 'Public Good' : 'GitHub';
|
||||
core.startGroup(highlight(`Attestation signed using certificate from ${instanceName} Sigstore instance`));
|
||||
core.startGroup(style.highlight(`Attestation signed using certificate from ${instanceName} Sigstore instance`));
|
||||
core.info(attestation.certificate);
|
||||
core.endGroup();
|
||||
if (attestation.tlogID) {
|
||||
core.info(highlight('Attestation signature uploaded to Rekor transparency log'));
|
||||
core.info(style.highlight('Attestation signature uploaded to Rekor transparency log'));
|
||||
core.info(`${endpoints_1.SEARCH_PUBLIC_GOOD_URL}?logIndex=${attestation.tlogID}`);
|
||||
}
|
||||
if (attestation.attestationID) {
|
||||
core.info(highlight('Attestation uploaded to repository'));
|
||||
core.info(style.highlight('Attestation uploaded to repository'));
|
||||
core.info(attestationURL(attestation.attestationID));
|
||||
}
|
||||
if (core.getBooleanInput('push-to-registry', { required: false })) {
|
||||
const credentials = (0, oci_1.getRegistryCredentials)(subject.name);
|
||||
const artifact = await (0, oci_1.attachArtifactToImage)({
|
||||
credentials,
|
||||
imageName: subject.name,
|
||||
imageDigest: subjectDigest(subject),
|
||||
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
|
||||
mediaType: attestation.bundle.mediaType,
|
||||
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}`);
|
||||
if (attestation.attestationDigest) {
|
||||
core.info(style.highlight('Attestation uploaded to registry'));
|
||||
core.info(`${attestation.subjectName}@${attestation.attestationDigest}`);
|
||||
}
|
||||
};
|
||||
// Attach summary information to the GitHub Actions run
|
||||
const logSummary = (attestations) => {
|
||||
if (attestations.length > 0) {
|
||||
core.summary.addHeading(
|
||||
/* istanbul ignore next */
|
||||
attestations.length > 1 ? 'Attestations Created' : 'Attestation Created', 3);
|
||||
for (const { subjectName, subjectDigest, attestationID } of attestations) {
|
||||
if (attestationID) {
|
||||
core.summary.addLink(`${subjectName}@${subjectDigest}`, attestationURL(attestationID));
|
||||
}
|
||||
}
|
||||
core.summary.write();
|
||||
}
|
||||
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 */
|
||||
@@ -80082,12 +80186,13 @@ const tempDir = () => {
|
||||
}
|
||||
return fs_1.default.mkdtempSync(path_1.default.join(basePath, path_1.default.sep));
|
||||
};
|
||||
// Returns the subject's digest as a formatted string of the form
|
||||
// "<algorithm>:<digest>".
|
||||
const subjectDigest = (subject) => {
|
||||
const alg = Object.keys(subject.digest).sort()[0];
|
||||
return `${alg}:${subject.digest[alg]}`;
|
||||
// Transforms an array into an array of arrays, each containing at most
|
||||
// `chunkSize` elements.
|
||||
const chunkArray = (array, chunkSize) => {
|
||||
return Array.from({ length: Math.ceil(array.length / chunkSize) }, (_, index) => array.slice(index * chunkSize, (index + 1) * chunkSize));
|
||||
};
|
||||
// Calculate the delay time for a given iteration
|
||||
const delay = (iteration) => Math.min(DELAY_INTERVAL_MS * 2 ** iteration, DELAY_MAX_MS);
|
||||
const attestationURL = (id) => `${github.context.serverUrl}/${github.context.repo.owner}/${github.context.repo.repo}/attestations/${id}`;
|
||||
|
||||
|
||||
@@ -80098,56 +80203,68 @@ const attestationURL = (id) => `${github.context.serverUrl}/${github.context.rep
|
||||
|
||||
"use strict";
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.predicateFromInputs = void 0;
|
||||
const core = __importStar(__nccwpck_require__(42186));
|
||||
const fs_1 = __importDefault(__nccwpck_require__(57147));
|
||||
const MAX_PREDICATE_SIZE_BYTES = 16 * 1024 * 1024;
|
||||
// Returns the predicate specified by the action's inputs. The predicate value
|
||||
// may be specified as a path to a file or as a string.
|
||||
const predicateFromInputs = () => {
|
||||
const predicateType = core.getInput('predicate-type', { required: true });
|
||||
const predicateStr = core.getInput('predicate', { required: false });
|
||||
const predicatePath = core.getInput('predicate-path', { required: false });
|
||||
if (!predicatePath && !predicateStr) {
|
||||
const predicateFromInputs = (inputs) => {
|
||||
const { predicateType, predicate, predicatePath } = inputs;
|
||||
if (!predicateType) {
|
||||
throw new Error('predicate-type must be provided');
|
||||
}
|
||||
if (!predicatePath && !predicate) {
|
||||
throw new Error('One of predicate-path or predicate must be provided');
|
||||
}
|
||||
if (predicatePath && predicateStr) {
|
||||
if (predicatePath && predicate) {
|
||||
throw new Error('Only one of predicate-path or predicate may be provided');
|
||||
}
|
||||
const params = predicatePath
|
||||
? fs_1.default.readFileSync(predicatePath, 'utf-8')
|
||||
: predicateStr;
|
||||
let params = predicate;
|
||||
if (predicatePath) {
|
||||
if (!fs_1.default.existsSync(predicatePath)) {
|
||||
throw new Error(`predicate file not found: ${predicatePath}`);
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
if (fs_1.default.statSync(predicatePath).size > MAX_PREDICATE_SIZE_BYTES) {
|
||||
throw new Error(`predicate file exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`);
|
||||
}
|
||||
params = fs_1.default.readFileSync(predicatePath, 'utf-8');
|
||||
}
|
||||
else {
|
||||
if (predicate.length > MAX_PREDICATE_SIZE_BYTES) {
|
||||
throw new Error(`predicate string exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`);
|
||||
}
|
||||
params = predicate;
|
||||
}
|
||||
return { type: predicateType, params: JSON.parse(params) };
|
||||
};
|
||||
exports.predicateFromInputs = predicateFromInputs;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 41583:
|
||||
/***/ ((__unused_webpack_module, exports) => {
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.mute = exports.highlight = void 0;
|
||||
const COLOR_CYAN = '\x1B[36m';
|
||||
const COLOR_GRAY = '\x1B[38;5;244m';
|
||||
const COLOR_DEFAULT = '\x1B[39m';
|
||||
// Emphasis string using ANSI color codes
|
||||
const highlight = (str) => `${COLOR_CYAN}${str}${COLOR_DEFAULT}`;
|
||||
exports.highlight = highlight;
|
||||
// De-emphasize string using ANSI color codes
|
||||
const mute = (str) => `${COLOR_GRAY}${str}${COLOR_DEFAULT}`;
|
||||
exports.mute = mute;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 95206:
|
||||
@@ -80183,24 +80300,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.subjectFromInputs = void 0;
|
||||
const core = __importStar(__nccwpck_require__(42186));
|
||||
const glob = __importStar(__nccwpck_require__(28090));
|
||||
const crypto_1 = __importDefault(__nccwpck_require__(6113));
|
||||
const sync_1 = __nccwpck_require__(74393);
|
||||
const fs_1 = __importDefault(__nccwpck_require__(57147));
|
||||
const path_1 = __importDefault(__nccwpck_require__(71017));
|
||||
const MAX_SUBJECT_COUNT = 2500;
|
||||
const DIGEST_ALGORITHM = 'sha256';
|
||||
// Returns the subject specified by the action's inputs. The subject may be
|
||||
// specified as a path to a file or as a digest. If a path is provided, the
|
||||
// file's digest is calculated and returned along with the subject's name. If a
|
||||
// digest is provided, the name must also be provided.
|
||||
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
|
||||
});
|
||||
const subjectFromInputs = async (inputs) => {
|
||||
const { subjectPath, subjectDigest, subjectName, downcaseName } = inputs;
|
||||
if (!subjectPath && !subjectDigest) {
|
||||
throw new Error('One of subject-path or subject-digest must be provided');
|
||||
}
|
||||
@@ -80212,7 +80324,7 @@ const subjectFromInputs = async () => {
|
||||
}
|
||||
// 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;
|
||||
const name = downcaseName ? subjectName.toLowerCase() : subjectName;
|
||||
if (subjectPath) {
|
||||
return await getSubjectFromPath(subjectPath, name);
|
||||
}
|
||||
@@ -80224,27 +80336,31 @@ exports.subjectFromInputs = subjectFromInputs;
|
||||
// Returns the subject specified by the path to a file. The file's digest is
|
||||
// calculated and returned along with the subject's name.
|
||||
const getSubjectFromPath = async (subjectPath, subjectName) => {
|
||||
const subjects = [];
|
||||
const digestedSubjects = [];
|
||||
const files = [];
|
||||
// Parse the list of subject paths
|
||||
const subjectPaths = parseList(subjectPath);
|
||||
// Expand the globbed paths to a list of files
|
||||
for (const subPath of subjectPaths) {
|
||||
// Expand the globbed path to a list of files
|
||||
/* eslint-disable-next-line github/no-then */
|
||||
const files = await glob.create(subPath).then(async (g) => g.glob());
|
||||
for (const file of files) {
|
||||
// Skip anything that is NOT a file
|
||||
if (!fs_1.default.statSync(file).isFile()) {
|
||||
continue;
|
||||
}
|
||||
const name = subjectName || path_1.default.parse(file).base;
|
||||
const digest = await digestFile(DIGEST_ALGORITHM, file);
|
||||
subjects.push({ name, digest: { [DIGEST_ALGORITHM]: digest } });
|
||||
}
|
||||
files.push(...(await glob.create(subPath).then(async (g) => g.glob())));
|
||||
}
|
||||
if (subjects.length === 0) {
|
||||
if (files.length > MAX_SUBJECT_COUNT) {
|
||||
throw new Error(`Too many subjects specified. The maximum number of subjects is ${MAX_SUBJECT_COUNT}.`);
|
||||
}
|
||||
for (const file of files) {
|
||||
// Skip anything that is NOT a file
|
||||
if (!fs_1.default.statSync(file).isFile()) {
|
||||
continue;
|
||||
}
|
||||
const name = subjectName || path_1.default.parse(file).base;
|
||||
const digest = await digestFile(DIGEST_ALGORITHM, file);
|
||||
digestedSubjects.push({ name, digest: { [DIGEST_ALGORITHM]: digest } });
|
||||
}
|
||||
if (digestedSubjects.length === 0) {
|
||||
throw new Error(`Could not find subject at path ${subjectPath}`);
|
||||
}
|
||||
return Promise.all(subjects);
|
||||
return digestedSubjects;
|
||||
};
|
||||
// Returns the subject specified by the digest of a file. The digest is returned
|
||||
// along with the subject's name.
|
||||
@@ -80423,6 +80539,14 @@ module.exports = require("node:fs");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 93977:
|
||||
/***/ ((module) => {
|
||||
|
||||
"use strict";
|
||||
module.exports = require("node:fs/promises");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 70612:
|
||||
/***/ ((module) => {
|
||||
|
||||
@@ -80447,6 +80571,22 @@ module.exports = require("node:stream");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 76915:
|
||||
/***/ ((module) => {
|
||||
|
||||
"use strict";
|
||||
module.exports = require("node:string_decoder");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 41041:
|
||||
/***/ ((module) => {
|
||||
|
||||
"use strict";
|
||||
module.exports = require("node:url");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 47261:
|
||||
/***/ ((module) => {
|
||||
|
||||
@@ -88350,9 +88490,9 @@ const proc = typeof process === 'object' && process
|
||||
stdout: null,
|
||||
stderr: null,
|
||||
};
|
||||
const events_1 = __nccwpck_require__(82361);
|
||||
const stream_1 = __importDefault(__nccwpck_require__(12781));
|
||||
const string_decoder_1 = __nccwpck_require__(71576);
|
||||
const node_events_1 = __nccwpck_require__(15673);
|
||||
const node_stream_1 = __importDefault(__nccwpck_require__(84492));
|
||||
const node_string_decoder_1 = __nccwpck_require__(76915);
|
||||
/**
|
||||
* Return true if the argument is a Minipass stream, Node stream, or something
|
||||
* else that Minipass can interact with.
|
||||
@@ -88360,7 +88500,7 @@ const string_decoder_1 = __nccwpck_require__(71576);
|
||||
const isStream = (s) => !!s &&
|
||||
typeof s === 'object' &&
|
||||
(s instanceof Minipass ||
|
||||
s instanceof stream_1.default ||
|
||||
s instanceof node_stream_1.default ||
|
||||
(0, exports.isReadable)(s) ||
|
||||
(0, exports.isWritable)(s));
|
||||
exports.isStream = isStream;
|
||||
@@ -88369,17 +88509,17 @@ exports.isStream = isStream;
|
||||
*/
|
||||
const isReadable = (s) => !!s &&
|
||||
typeof s === 'object' &&
|
||||
s instanceof events_1.EventEmitter &&
|
||||
s instanceof node_events_1.EventEmitter &&
|
||||
typeof s.pipe === 'function' &&
|
||||
// node core Writable streams have a pipe() method, but it throws
|
||||
s.pipe !== stream_1.default.Writable.prototype.pipe;
|
||||
s.pipe !== node_stream_1.default.Writable.prototype.pipe;
|
||||
exports.isReadable = isReadable;
|
||||
/**
|
||||
* Return true if the argument is a valid {@link Minipass.Writable}
|
||||
*/
|
||||
const isWritable = (s) => !!s &&
|
||||
typeof s === 'object' &&
|
||||
s instanceof events_1.EventEmitter &&
|
||||
s instanceof node_events_1.EventEmitter &&
|
||||
typeof s.write === 'function' &&
|
||||
typeof s.end === 'function';
|
||||
exports.isWritable = isWritable;
|
||||
@@ -88486,7 +88626,7 @@ const isEncodingOptions = (o) => !o.objectMode && !!o.encoding && o.encoding !==
|
||||
* `Events` is the set of event handler signatures that this object
|
||||
* will emit, see {@link Minipass.Events}
|
||||
*/
|
||||
class Minipass extends events_1.EventEmitter {
|
||||
class Minipass extends node_events_1.EventEmitter {
|
||||
[FLOWING] = false;
|
||||
[PAUSED] = false;
|
||||
[PIPES] = [];
|
||||
@@ -88541,7 +88681,7 @@ class Minipass extends events_1.EventEmitter {
|
||||
}
|
||||
this[ASYNC] = !!options.async;
|
||||
this[DECODER] = this[ENCODING]
|
||||
? new string_decoder_1.StringDecoder(this[ENCODING])
|
||||
? new node_string_decoder_1.StringDecoder(this[ENCODING])
|
||||
: null;
|
||||
//@ts-ignore - private option for debugging and testing
|
||||
if (options && options.debugExposeBuffer === true) {
|
||||
@@ -89400,14 +89540,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.PathScurry = exports.Path = exports.PathScurryDarwin = exports.PathScurryPosix = exports.PathScurryWin32 = exports.PathScurryBase = exports.PathPosix = exports.PathWin32 = exports.PathBase = exports.ChildrenCache = exports.ResolveCache = void 0;
|
||||
const lru_cache_1 = __nccwpck_require__(66091);
|
||||
const path_1 = __nccwpck_require__(71017);
|
||||
const url_1 = __nccwpck_require__(57310);
|
||||
const actualFS = __importStar(__nccwpck_require__(57147));
|
||||
const node_path_1 = __nccwpck_require__(49411);
|
||||
const node_url_1 = __nccwpck_require__(41041);
|
||||
const fs_1 = __nccwpck_require__(57147);
|
||||
const actualFS = __importStar(__nccwpck_require__(87561));
|
||||
const realpathSync = fs_1.realpathSync.native;
|
||||
// TODO: test perf of fs/promises realpath vs realpathCB,
|
||||
// since the promises one uses realpath.native
|
||||
const promises_1 = __nccwpck_require__(73292);
|
||||
const promises_1 = __nccwpck_require__(93977);
|
||||
const minipass_1 = __nccwpck_require__(14968);
|
||||
const defaultFS = {
|
||||
lstatSync: fs_1.lstatSync,
|
||||
@@ -89423,8 +89563,8 @@ const defaultFS = {
|
||||
},
|
||||
};
|
||||
// if they just gave us require('fs') then use our default
|
||||
const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS
|
||||
? defaultFS
|
||||
const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS ?
|
||||
defaultFS
|
||||
: {
|
||||
...defaultFS,
|
||||
...fsOption,
|
||||
@@ -89465,20 +89605,13 @@ const ENOREADLINK = 0b0001_0000_0000;
|
||||
const ENOREALPATH = 0b0010_0000_0000;
|
||||
const ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
|
||||
const TYPEMASK = 0b0011_1111_1111;
|
||||
const entToType = (s) => s.isFile()
|
||||
? IFREG
|
||||
: s.isDirectory()
|
||||
? IFDIR
|
||||
: s.isSymbolicLink()
|
||||
? IFLNK
|
||||
: s.isCharacterDevice()
|
||||
? IFCHR
|
||||
: s.isBlockDevice()
|
||||
? IFBLK
|
||||
: s.isSocket()
|
||||
? IFSOCK
|
||||
: s.isFIFO()
|
||||
? IFIFO
|
||||
const entToType = (s) => s.isFile() ? IFREG
|
||||
: s.isDirectory() ? IFDIR
|
||||
: s.isSymbolicLink() ? IFLNK
|
||||
: s.isCharacterDevice() ? IFCHR
|
||||
: s.isBlockDevice() ? IFBLK
|
||||
: s.isSocket() ? IFSOCK
|
||||
: s.isFIFO() ? IFIFO
|
||||
: UNKNOWN;
|
||||
// normalize unicode path names
|
||||
const normalizeCache = new Map();
|
||||
@@ -89582,6 +89715,11 @@ class PathBase {
|
||||
* @internal
|
||||
*/
|
||||
nocase;
|
||||
/**
|
||||
* boolean indicating that this path is the current working directory
|
||||
* of the PathScurry collection that contains it.
|
||||
*/
|
||||
isCWD = false;
|
||||
// potential default fs override
|
||||
#fs;
|
||||
// Stats fields
|
||||
@@ -89669,13 +89807,19 @@ class PathBase {
|
||||
#realpath;
|
||||
/**
|
||||
* This property is for compatibility with the Dirent class as of
|
||||
* Node v20, where Dirent['path'] refers to the path of the directory
|
||||
* that was passed to readdir. So, somewhat counterintuitively, this
|
||||
* property refers to the *parent* path, not the path object itself.
|
||||
* For root entries, it's the path to the entry itself.
|
||||
* Node v20, where Dirent['parentPath'] refers to the path of the
|
||||
* directory that was passed to readdir. For root entries, it's the path
|
||||
* to the entry itself.
|
||||
*/
|
||||
get parentPath() {
|
||||
return (this.parent || this).fullpath();
|
||||
}
|
||||
/**
|
||||
* Deprecated alias for Dirent['parentPath'] Somewhat counterintuitively,
|
||||
* this property refers to the *parent* path, not the path object itself.
|
||||
*/
|
||||
get path() {
|
||||
return (this.parent || this).fullpath();
|
||||
return this.parentPath;
|
||||
}
|
||||
/**
|
||||
* Do not create new Path objects directly. They should always be accessed
|
||||
@@ -89730,8 +89874,8 @@ class PathBase {
|
||||
const rootPath = this.getRootString(path);
|
||||
const dir = path.substring(rootPath.length);
|
||||
const dirParts = dir.split(this.splitSep);
|
||||
const result = rootPath
|
||||
? this.getRoot(rootPath).#resolveParts(dirParts)
|
||||
const result = rootPath ?
|
||||
this.getRoot(rootPath).#resolveParts(dirParts)
|
||||
: this.#resolveParts(dirParts);
|
||||
return result;
|
||||
}
|
||||
@@ -89782,9 +89926,7 @@ class PathBase {
|
||||
}
|
||||
// find the child
|
||||
const children = this.children();
|
||||
const name = this.nocase
|
||||
? normalizeNocase(pathPart)
|
||||
: normalize(pathPart);
|
||||
const name = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
|
||||
for (const p of children) {
|
||||
if (p.#matchName === name) {
|
||||
return p;
|
||||
@@ -89794,9 +89936,7 @@ class PathBase {
|
||||
// actually exist. If we know the parent isn't a dir, then
|
||||
// in fact it CAN'T exist.
|
||||
const s = this.parent ? this.sep : '';
|
||||
const fullpath = this.#fullpath
|
||||
? this.#fullpath + s + pathPart
|
||||
: undefined;
|
||||
const fullpath = this.#fullpath ? this.#fullpath + s + pathPart : undefined;
|
||||
const pchild = this.newChild(pathPart, UNKNOWN, {
|
||||
...opts,
|
||||
parent: this,
|
||||
@@ -89815,6 +89955,8 @@ class PathBase {
|
||||
* the cwd, then this ends up being equivalent to the fullpath()
|
||||
*/
|
||||
relative() {
|
||||
if (this.isCWD)
|
||||
return '';
|
||||
if (this.#relative !== undefined) {
|
||||
return this.#relative;
|
||||
}
|
||||
@@ -89835,6 +89977,8 @@ class PathBase {
|
||||
relativePosix() {
|
||||
if (this.sep === '/')
|
||||
return this.relative();
|
||||
if (this.isCWD)
|
||||
return '';
|
||||
if (this.#relativePosix !== undefined)
|
||||
return this.#relativePosix;
|
||||
const name = this.name;
|
||||
@@ -89900,23 +90044,15 @@ class PathBase {
|
||||
return this[`is${type}`]();
|
||||
}
|
||||
getType() {
|
||||
return this.isUnknown()
|
||||
? 'Unknown'
|
||||
: this.isDirectory()
|
||||
? 'Directory'
|
||||
: this.isFile()
|
||||
? 'File'
|
||||
: this.isSymbolicLink()
|
||||
? 'SymbolicLink'
|
||||
: this.isFIFO()
|
||||
? 'FIFO'
|
||||
: this.isCharacterDevice()
|
||||
? 'CharacterDevice'
|
||||
: this.isBlockDevice()
|
||||
? 'BlockDevice'
|
||||
: /* c8 ignore start */ this.isSocket()
|
||||
? 'Socket'
|
||||
: 'Unknown';
|
||||
return (this.isUnknown() ? 'Unknown'
|
||||
: this.isDirectory() ? 'Directory'
|
||||
: this.isFile() ? 'File'
|
||||
: this.isSymbolicLink() ? 'SymbolicLink'
|
||||
: this.isFIFO() ? 'FIFO'
|
||||
: this.isCharacterDevice() ? 'CharacterDevice'
|
||||
: this.isBlockDevice() ? 'BlockDevice'
|
||||
: /* c8 ignore start */ this.isSocket() ? 'Socket'
|
||||
: 'Unknown');
|
||||
/* c8 ignore stop */
|
||||
}
|
||||
/**
|
||||
@@ -90050,8 +90186,8 @@ class PathBase {
|
||||
* directly.
|
||||
*/
|
||||
isNamed(n) {
|
||||
return !this.nocase
|
||||
? this.#matchName === normalize(n)
|
||||
return !this.nocase ?
|
||||
this.#matchName === normalize(n)
|
||||
: this.#matchName === normalizeNocase(n);
|
||||
}
|
||||
/**
|
||||
@@ -90107,7 +90243,7 @@ class PathBase {
|
||||
/* c8 ignore stop */
|
||||
try {
|
||||
const read = this.#fs.readlinkSync(this.fullpath());
|
||||
const linkTarget = (this.parent.realpathSync())?.resolve(read);
|
||||
const linkTarget = this.parent.realpathSync()?.resolve(read);
|
||||
if (linkTarget) {
|
||||
return (this.#linkTarget = linkTarget);
|
||||
}
|
||||
@@ -90228,9 +90364,7 @@ class PathBase {
|
||||
#readdirMaybePromoteChild(e, c) {
|
||||
for (let p = c.provisional; p < c.length; p++) {
|
||||
const pchild = c[p];
|
||||
const name = this.nocase
|
||||
? normalizeNocase(e.name)
|
||||
: normalize(e.name);
|
||||
const name = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
|
||||
if (name !== pchild.#matchName) {
|
||||
continue;
|
||||
}
|
||||
@@ -90529,6 +90663,8 @@ class PathBase {
|
||||
[setAsCwd](oldCwd) {
|
||||
if (oldCwd === this)
|
||||
return;
|
||||
oldCwd.isCWD = false;
|
||||
this.isCWD = true;
|
||||
const changed = new Set([]);
|
||||
let rp = [];
|
||||
let p = this;
|
||||
@@ -90583,7 +90719,7 @@ class PathWin32 extends PathBase {
|
||||
* @internal
|
||||
*/
|
||||
getRootString(path) {
|
||||
return path_1.win32.parse(path).root;
|
||||
return node_path_1.win32.parse(path).root;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
@@ -90705,7 +90841,7 @@ class PathScurryBase {
|
||||
constructor(cwd = process.cwd(), pathImpl, sep, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS, } = {}) {
|
||||
this.#fs = fsFromOption(fs);
|
||||
if (cwd instanceof URL || cwd.startsWith('file://')) {
|
||||
cwd = (0, url_1.fileURLToPath)(cwd);
|
||||
cwd = (0, node_url_1.fileURLToPath)(cwd);
|
||||
}
|
||||
// resolve and split root, and then add to the store.
|
||||
// this is the only time we call path.resolve()
|
||||
@@ -91294,7 +91430,7 @@ class PathScurryWin32 extends PathScurryBase {
|
||||
sep = '\\';
|
||||
constructor(cwd = process.cwd(), opts = {}) {
|
||||
const { nocase = true } = opts;
|
||||
super(cwd, path_1.win32, '\\', { ...opts, nocase });
|
||||
super(cwd, node_path_1.win32, '\\', { ...opts, nocase });
|
||||
this.nocase = nocase;
|
||||
for (let p = this.cwd; p; p = p.parent) {
|
||||
p.nocase = this.nocase;
|
||||
@@ -91307,7 +91443,7 @@ class PathScurryWin32 extends PathScurryBase {
|
||||
// if the path starts with a single separator, it's not a UNC, and we'll
|
||||
// just get separator as the root, and driveFromUNC will return \
|
||||
// In that case, mount \ on the root from the cwd.
|
||||
return path_1.win32.parse(dir).root.toUpperCase();
|
||||
return node_path_1.win32.parse(dir).root.toUpperCase();
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
@@ -91337,7 +91473,7 @@ class PathScurryPosix extends PathScurryBase {
|
||||
sep = '/';
|
||||
constructor(cwd = process.cwd(), opts = {}) {
|
||||
const { nocase = false } = opts;
|
||||
super(cwd, path_1.posix, '/', { ...opts, nocase });
|
||||
super(cwd, node_path_1.posix, '/', { ...opts, nocase });
|
||||
this.nocase = nocase;
|
||||
}
|
||||
/**
|
||||
@@ -91387,10 +91523,8 @@ exports.Path = process.platform === 'win32' ? PathWin32 : PathPosix;
|
||||
* {@link PathScurryWin32} on Windows systems, {@link PathScurryDarwin} on
|
||||
* Darwin (macOS) systems, {@link PathScurryPosix} on all others.
|
||||
*/
|
||||
exports.PathScurry = process.platform === 'win32'
|
||||
? PathScurryWin32
|
||||
: process.platform === 'darwin'
|
||||
? PathScurryDarwin
|
||||
exports.PathScurry = process.platform === 'win32' ? PathScurryWin32
|
||||
: process.platform === 'darwin' ? PathScurryDarwin
|
||||
: PathScurryPosix;
|
||||
//# sourceMappingURL=index.js.map
|
||||
|
||||
@@ -93284,6 +93418,13 @@ const normalize_options = function(opts){
|
||||
`got ${JSON.stringify(options.on_record)}`
|
||||
], options);
|
||||
}
|
||||
// Normalize option `on_skip`
|
||||
// options.on_skip ??= (err, chunk) => {
|
||||
// this.emit('skip', err, chunk);
|
||||
// };
|
||||
if(options.on_skip !== undefined && options.on_skip !== null && typeof options.on_skip !== 'function'){
|
||||
throw new Error(`Invalid Option: on_skip must be a function, got ${JSON.stringify(options.on_skip)}`);
|
||||
}
|
||||
// Normalize option `quote`
|
||||
if(options.quote === null || options.quote === false || options.quote === ''){
|
||||
options.quote = null;
|
||||
@@ -94339,22 +94480,12 @@ module.exports = {"i8":"3.0.4"};
|
||||
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
|
||||
/******/
|
||||
/************************************************************************/
|
||||
var __webpack_exports__ = {};
|
||||
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
|
||||
(() => {
|
||||
"use strict";
|
||||
var exports = __webpack_exports__;
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
/**
|
||||
* The entrypoint for the action.
|
||||
*/
|
||||
const main_1 = __nccwpck_require__(70399);
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
(0, main_1.run)();
|
||||
|
||||
})();
|
||||
|
||||
module.exports = __webpack_exports__;
|
||||
/******/
|
||||
/******/ // startup
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ // This entry module is referenced by other modules so it can't be inlined
|
||||
/******/ var __webpack_exports__ = __nccwpck_require__(6144);
|
||||
/******/ module.exports = __webpack_exports__;
|
||||
/******/
|
||||
/******/ })()
|
||||
;
|
||||
Generated
+234
-192
@@ -1,40 +1,40 @@
|
||||
{
|
||||
"name": "actions/attest",
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "actions/attest",
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/attest": "^1.2.1",
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/glob": "^0.4.0",
|
||||
"@sigstore/oci": "^0.3.3",
|
||||
"csv-parse": "^5.5.5"
|
||||
"@sigstore/oci": "^0.3.4",
|
||||
"csv-parse": "^5.5.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sigstore/mock": "^0.7.3",
|
||||
"@sigstore/mock": "^0.7.4",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/make-fetch-happen": "^10.0.4",
|
||||
"@types/node": "^20.12.11",
|
||||
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
||||
"@typescript-eslint/parser": "^7.9.0",
|
||||
"@types/node": "^20.12.12",
|
||||
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
||||
"@typescript-eslint/parser": "^7.11.0",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-github": "^4.10.2",
|
||||
"eslint-plugin-jest": "^28.5.0",
|
||||
"eslint-plugin-jsonc": "^2.15.1",
|
||||
"eslint-plugin-jsonc": "^2.16.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"jest": "^29.7.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"markdownlint-cli": "^0.40.0",
|
||||
"markdownlint-cli": "^0.41.0",
|
||||
"nock": "^13.5.4",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-eslint": "^16.3.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-jest": "^29.1.3",
|
||||
"typescript": "^5.4.5",
|
||||
"undici": "^5.28.4"
|
||||
},
|
||||
@@ -1708,14 +1708,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sigstore/mock": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/mock/-/mock-0.7.3.tgz",
|
||||
"integrity": "sha512-Nztnzos5YubhLv5A+2TJxI7k/75P30hKrb+PwyeUwDMEzuztWpbgAinkICytQnrNHkqkoLiE3rDiX/cxsfTkzA==",
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/mock/-/mock-0.7.4.tgz",
|
||||
"integrity": "sha512-ij9X2Fij9fcH7upxf3KuAZ38ecGSMm+Asvbik5xiHTBUcwe1+bZ5eG6k5p1eHaNY+XJ581bC6O33871Bm5m5mQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@peculiar/webcrypto": "^1.4.6",
|
||||
"@peculiar/x509": "^1.9.7",
|
||||
"@sigstore/protobuf-specs": "^0.3.0",
|
||||
"@sigstore/protobuf-specs": "^0.3.2",
|
||||
"asn1js": "^3.0.5",
|
||||
"bytestreamjs": "^2.0.1",
|
||||
"canonicalize": "^2.0.0",
|
||||
@@ -1729,9 +1729,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sigstore/oci": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/oci/-/oci-0.3.3.tgz",
|
||||
"integrity": "sha512-GFNS7BVC0YvZnajj/ZtboH98A8T0rApkkI3988BzkuIJ5f3Z+mTXr/b5K7OekfHv7LvLzSziXXRRnsb6Cx8zXg==",
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/oci/-/oci-0.3.4.tgz",
|
||||
"integrity": "sha512-ydRTsvHOmLWnlR2BTtG1pHYvLkHG/oaqVyd2WDkfLU7B3dIWfqavE80VCzidNWuZpXN7m8+uBNatus2Qva1ktA==",
|
||||
"dependencies": {
|
||||
"make-fetch-happen": "^13.0.1",
|
||||
"proc-log": "^4.2.0"
|
||||
@@ -1741,9 +1741,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sigstore/protobuf-specs": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.1.tgz",
|
||||
"integrity": "sha512-aIL8Z9NsMr3C64jyQzE0XlkEyBLpgEJJFDHLVVStkFV5Q3Il/r/YtY6NJWKQ4cy4AE7spP1IX5Jq7VCAxHHMfQ==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz",
|
||||
"integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==",
|
||||
"engines": {
|
||||
"node": "^16.14.0 || >=18.0.0"
|
||||
}
|
||||
@@ -1960,9 +1960,9 @@
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.12.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
|
||||
"integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
|
||||
"version": "20.12.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
|
||||
"integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
@@ -2037,16 +2037,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz",
|
||||
"integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.11.0.tgz",
|
||||
"integrity": "sha512-P+qEahbgeHW4JQ/87FuItjBj8O3MYv5gELDzr8QaQ7fsll1gSMTYb6j87MYyxwf3DtD7uGFB9ShwgmCJB5KmaQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "7.9.0",
|
||||
"@typescript-eslint/type-utils": "7.9.0",
|
||||
"@typescript-eslint/utils": "7.9.0",
|
||||
"@typescript-eslint/visitor-keys": "7.9.0",
|
||||
"@typescript-eslint/scope-manager": "7.11.0",
|
||||
"@typescript-eslint/type-utils": "7.11.0",
|
||||
"@typescript-eslint/utils": "7.11.0",
|
||||
"@typescript-eslint/visitor-keys": "7.11.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -2070,15 +2070,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz",
|
||||
"integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.11.0.tgz",
|
||||
"integrity": "sha512-yimw99teuaXVWsBcPO1Ais02kwJ1jmNA1KxE7ng0aT7ndr1pT1wqj0OJnsYVGKKlc4QJai86l/025L6z8CljOg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "7.9.0",
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/typescript-estree": "7.9.0",
|
||||
"@typescript-eslint/visitor-keys": "7.9.0",
|
||||
"@typescript-eslint/scope-manager": "7.11.0",
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"@typescript-eslint/typescript-estree": "7.11.0",
|
||||
"@typescript-eslint/visitor-keys": "7.11.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2098,13 +2098,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz",
|
||||
"integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.11.0.tgz",
|
||||
"integrity": "sha512-27tGdVEiutD4POirLZX4YzT180vevUURJl4wJGmm6TrQoiYwuxTIY98PBp6L2oN+JQxzE0URvYlzJaBHIekXAw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/visitor-keys": "7.9.0"
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"@typescript-eslint/visitor-keys": "7.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
@@ -2115,13 +2115,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz",
|
||||
"integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.11.0.tgz",
|
||||
"integrity": "sha512-WmppUEgYy+y1NTseNMJ6mCFxt03/7jTOy08bcg7bxJJdsM4nuhnchyBbE8vryveaJUf62noH7LodPSo5Z0WUCg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "7.9.0",
|
||||
"@typescript-eslint/utils": "7.9.0",
|
||||
"@typescript-eslint/typescript-estree": "7.11.0",
|
||||
"@typescript-eslint/utils": "7.11.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.3.0"
|
||||
},
|
||||
@@ -2142,9 +2142,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz",
|
||||
"integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-MPEsDRZTyCiXkD4vd3zywDCifi7tatc4K37KqTprCvaXptP7Xlpdw0NR2hRJTetG5TxbWDB79Ys4kLmHliEo/w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
@@ -2155,13 +2155,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz",
|
||||
"integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.11.0.tgz",
|
||||
"integrity": "sha512-cxkhZ2C/iyi3/6U9EPc5y+a6csqHItndvN/CzbNXTNrsC3/ASoYQZEt9uMaEp+xFNjasqQyszp5TumAVKKvJeQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/visitor-keys": "7.9.0",
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"@typescript-eslint/visitor-keys": "7.11.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -2207,15 +2207,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz",
|
||||
"integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.11.0.tgz",
|
||||
"integrity": "sha512-xlAWwPleNRHwF37AhrZurOxA1wyXowW4PqVXZVUNCLjB48CqdPJoJWkrpH2nij9Q3Lb7rtWindtoXwxjxlKKCA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "7.9.0",
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/typescript-estree": "7.9.0"
|
||||
"@typescript-eslint/scope-manager": "7.11.0",
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"@typescript-eslint/typescript-estree": "7.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
@@ -2229,12 +2229,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz",
|
||||
"integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.11.0.tgz",
|
||||
"integrity": "sha512-7syYk4MzjxTEk0g/w3iqtgxnFQspDJfn6QKD36xMuuhTzjcxY7F8EmBLnALjVyaOF1/bVocu3bS/2/F7rXrveQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3037,9 +3037,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
|
||||
"integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -3095,9 +3095,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/csv-parse": {
|
||||
"version": "5.5.5",
|
||||
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.5.tgz",
|
||||
"integrity": "sha512-erCk7tyU3yLWAhk6wvKxnyPtftuy/6Ak622gOO7BCJ05+TYffnPCJF905wmOQm+BpkX54OdAl8pveJwUdpnCXQ=="
|
||||
"version": "5.5.6",
|
||||
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.6.tgz",
|
||||
"integrity": "sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A=="
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
@@ -3793,9 +3793,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-jsonc": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.15.1.tgz",
|
||||
"integrity": "sha512-PVFrqIJa8BbM/e828RSn0SwB/Z5ye+2LDuy2XqG6AymNgPsfApRRcznsbxP7VrjdLEU4Nb+g9n/d6opyp0jp9A==",
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.16.0.tgz",
|
||||
"integrity": "sha512-Af/ZL5mgfb8FFNleH6KlO4/VdmDuTqmM+SPnWcdoWywTetv7kq+vQe99UyQb9XO3b0OWLVuTH7H0d/PXYCMdSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
@@ -6251,14 +6251,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/markdownlint-cli": {
|
||||
"version": "0.40.0",
|
||||
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz",
|
||||
"integrity": "sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA==",
|
||||
"version": "0.41.0",
|
||||
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz",
|
||||
"integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"commander": "~12.0.0",
|
||||
"commander": "~12.1.0",
|
||||
"get-stdin": "~9.0.0",
|
||||
"glob": "~10.3.12",
|
||||
"glob": "~10.4.1",
|
||||
"ignore": "~5.3.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonc-parser": "~3.2.1",
|
||||
@@ -6266,7 +6266,7 @@
|
||||
"markdownlint": "~0.34.0",
|
||||
"minimatch": "~9.0.4",
|
||||
"run-con": "~1.3.2",
|
||||
"toml": "~3.0.0"
|
||||
"smol-toml": "~1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"markdownlint": "markdownlint.js"
|
||||
@@ -6285,27 +6285,45 @@
|
||||
}
|
||||
},
|
||||
"node_modules/markdownlint-cli/node_modules/glob": {
|
||||
"version": "10.3.12",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
|
||||
"integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
|
||||
"integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^2.3.6",
|
||||
"minimatch": "^9.0.1",
|
||||
"minipass": "^7.0.4",
|
||||
"path-scurry": "^1.10.2"
|
||||
"jackspeak": "^3.1.2",
|
||||
"minimatch": "^9.0.4",
|
||||
"minipass": "^7.1.2",
|
||||
"path-scurry": "^1.11.1"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
"node": ">=16 || 14 >=14.18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/markdownlint-cli/node_modules/jackspeak": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
|
||||
"integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/markdownlint-cli/node_modules/minimatch": {
|
||||
"version": "9.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
|
||||
@@ -6410,8 +6428,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.0.4",
|
||||
"license": "ISC",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
@@ -6847,15 +6866,15 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
|
||||
"integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^10.2.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
"node": ">=16 || 14 >=14.18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
@@ -7683,6 +7702,16 @@
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/smol-toml": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.0.tgz",
|
||||
"integrity": "sha512-KObxdQANC/xje3OoatMbSwQf2XAvJ0RbK+4nmQRszFNZptbNRnMWqbLF/zb4sMi9xJ6HNyhWXeuZ9zC/I/XY7w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 18",
|
||||
"pnpm": ">= 9"
|
||||
}
|
||||
},
|
||||
"node_modules/socks": {
|
||||
"version": "2.7.3",
|
||||
"license": "MIT",
|
||||
@@ -8032,12 +8061,6 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toml": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
|
||||
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
|
||||
@@ -8051,9 +8074,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-jest": {
|
||||
"version": "29.1.2",
|
||||
"version": "29.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.3.tgz",
|
||||
"integrity": "sha512-6L9qz3ginTd1NKhOxmkP0qU3FyKjj5CPoY+anszfVn6Pmv/RIKzhiMCsH7Yb7UvJR9I2A64rm4zQl531s2F1iw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bs-logger": "0.x",
|
||||
"fast-json-stable-stringify": "2.x",
|
||||
@@ -8068,10 +8092,11 @@
|
||||
"ts-jest": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.10.0 || ^18.0.0 || >=20.0.0"
|
||||
"node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": ">=7.0.0-beta.0 <8",
|
||||
"@jest/transform": "^29.0.0",
|
||||
"@jest/types": "^29.0.0",
|
||||
"babel-jest": "^29.0.0",
|
||||
"jest": "^29.0.0",
|
||||
@@ -8081,6 +8106,9 @@
|
||||
"@babel/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@jest/transform": {
|
||||
"optional": true
|
||||
},
|
||||
"@jest/types": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -9787,14 +9815,14 @@
|
||||
"integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg=="
|
||||
},
|
||||
"@sigstore/mock": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/mock/-/mock-0.7.3.tgz",
|
||||
"integrity": "sha512-Nztnzos5YubhLv5A+2TJxI7k/75P30hKrb+PwyeUwDMEzuztWpbgAinkICytQnrNHkqkoLiE3rDiX/cxsfTkzA==",
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/mock/-/mock-0.7.4.tgz",
|
||||
"integrity": "sha512-ij9X2Fij9fcH7upxf3KuAZ38ecGSMm+Asvbik5xiHTBUcwe1+bZ5eG6k5p1eHaNY+XJ581bC6O33871Bm5m5mQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@peculiar/webcrypto": "^1.4.6",
|
||||
"@peculiar/x509": "^1.9.7",
|
||||
"@sigstore/protobuf-specs": "^0.3.0",
|
||||
"@sigstore/protobuf-specs": "^0.3.2",
|
||||
"asn1js": "^3.0.5",
|
||||
"bytestreamjs": "^2.0.1",
|
||||
"canonicalize": "^2.0.0",
|
||||
@@ -9805,18 +9833,18 @@
|
||||
}
|
||||
},
|
||||
"@sigstore/oci": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/oci/-/oci-0.3.3.tgz",
|
||||
"integrity": "sha512-GFNS7BVC0YvZnajj/ZtboH98A8T0rApkkI3988BzkuIJ5f3Z+mTXr/b5K7OekfHv7LvLzSziXXRRnsb6Cx8zXg==",
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/oci/-/oci-0.3.4.tgz",
|
||||
"integrity": "sha512-ydRTsvHOmLWnlR2BTtG1pHYvLkHG/oaqVyd2WDkfLU7B3dIWfqavE80VCzidNWuZpXN7m8+uBNatus2Qva1ktA==",
|
||||
"requires": {
|
||||
"make-fetch-happen": "^13.0.1",
|
||||
"proc-log": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"@sigstore/protobuf-specs": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.1.tgz",
|
||||
"integrity": "sha512-aIL8Z9NsMr3C64jyQzE0XlkEyBLpgEJJFDHLVVStkFV5Q3Il/r/YtY6NJWKQ4cy4AE7spP1IX5Jq7VCAxHHMfQ=="
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz",
|
||||
"integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw=="
|
||||
},
|
||||
"@sigstore/sign": {
|
||||
"version": "2.3.1",
|
||||
@@ -10012,9 +10040,9 @@
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "20.12.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
|
||||
"integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
|
||||
"version": "20.12.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
|
||||
"integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
|
||||
"requires": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
@@ -10083,16 +10111,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz",
|
||||
"integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.11.0.tgz",
|
||||
"integrity": "sha512-P+qEahbgeHW4JQ/87FuItjBj8O3MYv5gELDzr8QaQ7fsll1gSMTYb6j87MYyxwf3DtD7uGFB9ShwgmCJB5KmaQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "7.9.0",
|
||||
"@typescript-eslint/type-utils": "7.9.0",
|
||||
"@typescript-eslint/utils": "7.9.0",
|
||||
"@typescript-eslint/visitor-keys": "7.9.0",
|
||||
"@typescript-eslint/scope-manager": "7.11.0",
|
||||
"@typescript-eslint/type-utils": "7.11.0",
|
||||
"@typescript-eslint/utils": "7.11.0",
|
||||
"@typescript-eslint/visitor-keys": "7.11.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -10100,54 +10128,54 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz",
|
||||
"integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.11.0.tgz",
|
||||
"integrity": "sha512-yimw99teuaXVWsBcPO1Ais02kwJ1jmNA1KxE7ng0aT7ndr1pT1wqj0OJnsYVGKKlc4QJai86l/025L6z8CljOg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "7.9.0",
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/typescript-estree": "7.9.0",
|
||||
"@typescript-eslint/visitor-keys": "7.9.0",
|
||||
"@typescript-eslint/scope-manager": "7.11.0",
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"@typescript-eslint/typescript-estree": "7.11.0",
|
||||
"@typescript-eslint/visitor-keys": "7.11.0",
|
||||
"debug": "^4.3.4"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz",
|
||||
"integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.11.0.tgz",
|
||||
"integrity": "sha512-27tGdVEiutD4POirLZX4YzT180vevUURJl4wJGmm6TrQoiYwuxTIY98PBp6L2oN+JQxzE0URvYlzJaBHIekXAw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/visitor-keys": "7.9.0"
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"@typescript-eslint/visitor-keys": "7.11.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/type-utils": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz",
|
||||
"integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.11.0.tgz",
|
||||
"integrity": "sha512-WmppUEgYy+y1NTseNMJ6mCFxt03/7jTOy08bcg7bxJJdsM4nuhnchyBbE8vryveaJUf62noH7LodPSo5Z0WUCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/typescript-estree": "7.9.0",
|
||||
"@typescript-eslint/utils": "7.9.0",
|
||||
"@typescript-eslint/typescript-estree": "7.11.0",
|
||||
"@typescript-eslint/utils": "7.11.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz",
|
||||
"integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-MPEsDRZTyCiXkD4vd3zywDCifi7tatc4K37KqTprCvaXptP7Xlpdw0NR2hRJTetG5TxbWDB79Ys4kLmHliEo/w==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz",
|
||||
"integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.11.0.tgz",
|
||||
"integrity": "sha512-cxkhZ2C/iyi3/6U9EPc5y+a6csqHItndvN/CzbNXTNrsC3/ASoYQZEt9uMaEp+xFNjasqQyszp5TumAVKKvJeQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/visitor-keys": "7.9.0",
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"@typescript-eslint/visitor-keys": "7.11.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -10177,24 +10205,24 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz",
|
||||
"integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.11.0.tgz",
|
||||
"integrity": "sha512-xlAWwPleNRHwF37AhrZurOxA1wyXowW4PqVXZVUNCLjB48CqdPJoJWkrpH2nij9Q3Lb7rtWindtoXwxjxlKKCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "7.9.0",
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/typescript-estree": "7.9.0"
|
||||
"@typescript-eslint/scope-manager": "7.11.0",
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"@typescript-eslint/typescript-estree": "7.11.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz",
|
||||
"integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.11.0.tgz",
|
||||
"integrity": "sha512-7syYk4MzjxTEk0g/w3iqtgxnFQspDJfn6QKD36xMuuhTzjcxY7F8EmBLnALjVyaOF1/bVocu3bS/2/F7rXrveQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "7.9.0",
|
||||
"@typescript-eslint/types": "7.11.0",
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
}
|
||||
},
|
||||
@@ -10704,9 +10732,9 @@
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
|
||||
"integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||
"dev": true
|
||||
},
|
||||
"common-tags": {
|
||||
@@ -10742,9 +10770,9 @@
|
||||
}
|
||||
},
|
||||
"csv-parse": {
|
||||
"version": "5.5.5",
|
||||
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.5.tgz",
|
||||
"integrity": "sha512-erCk7tyU3yLWAhk6wvKxnyPtftuy/6Ak622gOO7BCJ05+TYffnPCJF905wmOQm+BpkX54OdAl8pveJwUdpnCXQ=="
|
||||
"version": "5.5.6",
|
||||
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.6.tgz",
|
||||
"integrity": "sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A=="
|
||||
},
|
||||
"damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
@@ -11249,9 +11277,9 @@
|
||||
}
|
||||
},
|
||||
"eslint-plugin-jsonc": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.15.1.tgz",
|
||||
"integrity": "sha512-PVFrqIJa8BbM/e828RSn0SwB/Z5ye+2LDuy2XqG6AymNgPsfApRRcznsbxP7VrjdLEU4Nb+g9n/d6opyp0jp9A==",
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.16.0.tgz",
|
||||
"integrity": "sha512-Af/ZL5mgfb8FFNleH6KlO4/VdmDuTqmM+SPnWcdoWywTetv7kq+vQe99UyQb9XO3b0OWLVuTH7H0d/PXYCMdSg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
@@ -12902,14 +12930,14 @@
|
||||
}
|
||||
},
|
||||
"markdownlint-cli": {
|
||||
"version": "0.40.0",
|
||||
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz",
|
||||
"integrity": "sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA==",
|
||||
"version": "0.41.0",
|
||||
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz",
|
||||
"integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "~12.0.0",
|
||||
"commander": "~12.1.0",
|
||||
"get-stdin": "~9.0.0",
|
||||
"glob": "~10.3.12",
|
||||
"glob": "~10.4.1",
|
||||
"ignore": "~5.3.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonc-parser": "~3.2.1",
|
||||
@@ -12917,7 +12945,7 @@
|
||||
"markdownlint": "~0.34.0",
|
||||
"minimatch": "~9.0.4",
|
||||
"run-con": "~1.3.2",
|
||||
"toml": "~3.0.0"
|
||||
"smol-toml": "~1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"brace-expansion": {
|
||||
@@ -12930,16 +12958,26 @@
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "10.3.12",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
|
||||
"integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
|
||||
"integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^2.3.6",
|
||||
"minimatch": "^9.0.1",
|
||||
"minipass": "^7.0.4",
|
||||
"path-scurry": "^1.10.2"
|
||||
"jackspeak": "^3.1.2",
|
||||
"minimatch": "^9.0.4",
|
||||
"minipass": "^7.1.2",
|
||||
"path-scurry": "^1.11.1"
|
||||
}
|
||||
},
|
||||
"jackspeak": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
|
||||
"integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@isaacs/cliui": "^8.0.2",
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
@@ -13007,7 +13045,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "7.0.4"
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
|
||||
},
|
||||
"minipass-collect": {
|
||||
"version": "2.0.1",
|
||||
@@ -13288,9 +13328,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"path-scurry": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
|
||||
"integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||
"requires": {
|
||||
"lru-cache": "^10.2.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
@@ -13788,6 +13828,12 @@
|
||||
"smart-buffer": {
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"smol-toml": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.0.tgz",
|
||||
"integrity": "sha512-KObxdQANC/xje3OoatMbSwQf2XAvJ0RbK+4nmQRszFNZptbNRnMWqbLF/zb4sMi9xJ6HNyhWXeuZ9zC/I/XY7w==",
|
||||
"dev": true
|
||||
},
|
||||
"socks": {
|
||||
"version": "2.7.3",
|
||||
"requires": {
|
||||
@@ -14017,12 +14063,6 @@
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"toml": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
|
||||
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
|
||||
"dev": true
|
||||
},
|
||||
"ts-api-utils": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
|
||||
@@ -14031,7 +14071,9 @@
|
||||
"requires": {}
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "29.1.2",
|
||||
"version": "29.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.3.tgz",
|
||||
"integrity": "sha512-6L9qz3ginTd1NKhOxmkP0qU3FyKjj5CPoY+anszfVn6Pmv/RIKzhiMCsH7Yb7UvJR9I2A64rm4zQl531s2F1iw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bs-logger": "0.x",
|
||||
|
||||
+10
-10
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "actions/attest",
|
||||
"description": "Generate signed attestations for workflow artifacts",
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.0",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/actions/attest",
|
||||
@@ -72,29 +72,29 @@
|
||||
"@actions/attest": "^1.2.1",
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/glob": "^0.4.0",
|
||||
"@sigstore/oci": "^0.3.3",
|
||||
"csv-parse": "^5.5.5"
|
||||
"@sigstore/oci": "^0.3.4",
|
||||
"csv-parse": "^5.5.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sigstore/mock": "^0.7.3",
|
||||
"@sigstore/mock": "^0.7.4",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/make-fetch-happen": "^10.0.4",
|
||||
"@types/node": "^20.12.11",
|
||||
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
||||
"@typescript-eslint/parser": "^7.9.0",
|
||||
"@types/node": "^20.12.12",
|
||||
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
||||
"@typescript-eslint/parser": "^7.11.0",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-github": "^4.10.2",
|
||||
"eslint-plugin-jest": "^28.5.0",
|
||||
"eslint-plugin-jsonc": "^2.15.1",
|
||||
"eslint-plugin-jsonc": "^2.16.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"jest": "^29.7.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"markdownlint-cli": "^0.40.0",
|
||||
"markdownlint-cli": "^0.41.0",
|
||||
"nock": "^13.5.4",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-eslint": "^16.3.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-jest": "^29.1.3",
|
||||
"typescript": "^5.4.5",
|
||||
"undici": "^5.28.4"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Attestation, Predicate, Subject, attest } from '@actions/attest'
|
||||
import { attachArtifactToImage, getRegistryCredentials } from '@sigstore/oci'
|
||||
|
||||
const OCI_TIMEOUT = 2000
|
||||
const OCI_RETRY = 3
|
||||
|
||||
export type SigstoreInstance = 'public-good' | 'github'
|
||||
export type AttestResult = Attestation & {
|
||||
subjectName: string
|
||||
subjectDigest: string
|
||||
attestationDigest?: string
|
||||
}
|
||||
|
||||
export const createAttestation = async (
|
||||
subject: Subject,
|
||||
predicate: Predicate,
|
||||
opts: {
|
||||
sigstoreInstance: SigstoreInstance
|
||||
pushToRegistry: boolean
|
||||
githubToken: string
|
||||
}
|
||||
): Promise<AttestResult> => {
|
||||
// Sign provenance w/ Sigstore
|
||||
const attestation = await attest({
|
||||
subjectName: subject.name,
|
||||
subjectDigest: subject.digest,
|
||||
predicateType: predicate.type,
|
||||
predicate: predicate.params,
|
||||
sigstore: opts.sigstoreInstance,
|
||||
token: opts.githubToken
|
||||
})
|
||||
|
||||
const subDigest = subjectDigest(subject)
|
||||
const result: AttestResult = {
|
||||
...attestation,
|
||||
subjectName: subject.name,
|
||||
subjectDigest: subDigest
|
||||
}
|
||||
|
||||
if (opts.pushToRegistry) {
|
||||
const credentials = getRegistryCredentials(subject.name)
|
||||
const artifact = await attachArtifactToImage({
|
||||
credentials,
|
||||
imageName: subject.name,
|
||||
imageDigest: subDigest,
|
||||
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
|
||||
mediaType: attestation.bundle.mediaType,
|
||||
annotations: {
|
||||
'dev.sigstore.bundle.content': 'dsse-envelope',
|
||||
'dev.sigstore.bundle.predicateType': predicate.type
|
||||
},
|
||||
fetchOpts: { timeout: OCI_TIMEOUT, retry: OCI_RETRY }
|
||||
})
|
||||
|
||||
// Add the attestation's digest to the result
|
||||
result.attestationDigest = artifact.digest
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the subject's digest as a formatted string of the form
|
||||
// "<algorithm>:<digest>".
|
||||
const subjectDigest = (subject: Subject): string => {
|
||||
const alg = Object.keys(subject.digest).sort()[0]
|
||||
return `${alg}:${subject.digest[alg]}`
|
||||
}
|
||||
+22
-2
@@ -1,7 +1,27 @@
|
||||
/**
|
||||
* The entrypoint for the action.
|
||||
*/
|
||||
import { run } from './main'
|
||||
import * as core from '@actions/core'
|
||||
import { run, RunInputs } from './main'
|
||||
|
||||
const DEFAULT_BATCH_SIZE = 50
|
||||
|
||||
const inputs: RunInputs = {
|
||||
subjectPath: core.getInput('subject-path'),
|
||||
subjectName: core.getInput('subject-name'),
|
||||
subjectDigest: core.getInput('subject-digest'),
|
||||
predicateType: core.getInput('predicate-type'),
|
||||
predicate: core.getInput('predicate'),
|
||||
predicatePath: core.getInput('predicate-path'),
|
||||
pushToRegistry: core.getBooleanInput('push-to-registry'),
|
||||
githubToken: core.getInput('github-token'),
|
||||
// undocumented -- not part of public interface
|
||||
privateSigning: ['true', 'True', 'TRUE', '1'].includes(
|
||||
core.getInput('private-signing')
|
||||
),
|
||||
// internal only
|
||||
batchSize: DEFAULT_BATCH_SIZE
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
run()
|
||||
run(inputs)
|
||||
|
||||
+105
-104
@@ -1,26 +1,25 @@
|
||||
import { Attestation, Predicate, Subject, attest } from '@actions/attest'
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import { attachArtifactToImage, getRegistryCredentials } from '@sigstore/oci'
|
||||
import fs from 'fs'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import { AttestResult, SigstoreInstance, createAttestation } from './attest'
|
||||
import { SEARCH_PUBLIC_GOOD_URL } from './endpoints'
|
||||
import { predicateFromInputs } from './predicate'
|
||||
import { subjectFromInputs } from './subject'
|
||||
import { PredicateInputs, predicateFromInputs } from './predicate'
|
||||
import * as style from './style'
|
||||
import { SubjectInputs, subjectFromInputs } from './subject'
|
||||
|
||||
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 DELAY_INTERVAL_MS = 75
|
||||
const DELAY_MAX_MS = 1200
|
||||
|
||||
const MAX_SUBJECT_COUNT = 64
|
||||
|
||||
const OCI_TIMEOUT = 2000
|
||||
const OCI_RETRY = 3
|
||||
export type RunInputs = SubjectInputs &
|
||||
PredicateInputs & {
|
||||
pushToRegistry: boolean
|
||||
githubToken: string
|
||||
privateSigning: boolean
|
||||
batchSize: number
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
const logHandler = (level: string, ...args: unknown[]): void => {
|
||||
@@ -34,7 +33,7 @@ const logHandler = (level: string, ...args: unknown[]): void => {
|
||||
* The main function for the action.
|
||||
* @returns {Promise<void>} Resolves when the action is complete.
|
||||
*/
|
||||
export async function run(): Promise<void> {
|
||||
export async function run(inputs: RunInputs): Promise<void> {
|
||||
process.on('log', logHandler)
|
||||
|
||||
// Provenance visibility will be public ONLY if we can confirm that the
|
||||
@@ -42,61 +41,62 @@ export async function run(): Promise<void> {
|
||||
// Otherwise, it will be private.
|
||||
const sigstoreInstance: SigstoreInstance =
|
||||
github.context.payload.repository?.visibility === 'public' &&
|
||||
core.getInput('private-signing') !== 'true'
|
||||
!inputs.privateSigning
|
||||
? 'public-good'
|
||||
: 'github'
|
||||
|
||||
try {
|
||||
const atts: AttestedSubject[] = []
|
||||
const atts: AttestResult[] = []
|
||||
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
|
||||
throw new Error(
|
||||
'missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.'
|
||||
)
|
||||
}
|
||||
|
||||
// Gather list of subjets
|
||||
const subjects = await subjectFromInputs()
|
||||
if (subjects.length > MAX_SUBJECT_COUNT) {
|
||||
throw new Error(
|
||||
`Too many subjects specified. The maximum number of subjects is ${MAX_SUBJECT_COUNT}.`
|
||||
)
|
||||
}
|
||||
const subjects = await subjectFromInputs({
|
||||
...inputs,
|
||||
downcaseName: inputs.pushToRegistry
|
||||
})
|
||||
const predicate = predicateFromInputs(inputs)
|
||||
|
||||
const predicate = predicateFromInputs()
|
||||
const outputPath = path.join(tempDir(), ATTESTATION_FILE_NAME)
|
||||
|
||||
// Generate attestations for each subject serially
|
||||
for (const subject of subjects) {
|
||||
const att = await createAttestation(subject, predicate, sigstoreInstance)
|
||||
|
||||
// Write attestation bundle to output file
|
||||
fs.writeFileSync(outputPath, JSON.stringify(att.bundle) + os.EOL, {
|
||||
encoding: 'utf-8',
|
||||
flag: 'a'
|
||||
})
|
||||
|
||||
if (att.attestationID) {
|
||||
atts.push({ subject, attestationID: att.attestationID })
|
||||
}
|
||||
}
|
||||
|
||||
if (atts.length > 0) {
|
||||
core.summary.addHeading(
|
||||
/* istanbul ignore next */
|
||||
atts.length > 1 ? 'Attestations Created' : 'Attestation Created',
|
||||
3
|
||||
)
|
||||
|
||||
for (const { subject, attestationID } of atts) {
|
||||
core.summary.addLink(
|
||||
`${subject.name}@${subjectDigest(subject)}`,
|
||||
attestationURL(attestationID)
|
||||
)
|
||||
}
|
||||
core.summary.write()
|
||||
}
|
||||
|
||||
core.setOutput('bundle-path', outputPath)
|
||||
|
||||
const subjectChunks = chunkArray(subjects, inputs.batchSize)
|
||||
|
||||
// Generate attestations for each subject serially, working in batches
|
||||
for (let i = 0; i < subjectChunks.length; i++) {
|
||||
if (subjectChunks.length > 1) {
|
||||
core.info(`Processing subject batch ${i + 1}/${subjectChunks.length}`)
|
||||
}
|
||||
|
||||
// Calculate the delay time for this batch
|
||||
const delayTime = delay(i)
|
||||
|
||||
for (const subject of subjectChunks[i]) {
|
||||
// Delay between attestations (only when chunk size > 1)
|
||||
if (i > 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, delayTime))
|
||||
}
|
||||
|
||||
const att = await createAttestation(subject, predicate, {
|
||||
sigstoreInstance,
|
||||
pushToRegistry: inputs.pushToRegistry,
|
||||
githubToken: inputs.githubToken
|
||||
})
|
||||
atts.push(att)
|
||||
|
||||
logAttestation(att, sigstoreInstance)
|
||||
|
||||
// Write attestation bundle to output file
|
||||
fs.writeFileSync(outputPath, JSON.stringify(att.bundle) + os.EOL, {
|
||||
encoding: 'utf-8',
|
||||
flag: 'a'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
logSummary(atts)
|
||||
} catch (err) {
|
||||
// Fail the workflow run if an error occurs
|
||||
core.setFailed(
|
||||
@@ -108,7 +108,9 @@ export async function run(): Promise<void> {
|
||||
if (err instanceof Error && 'cause' in err) {
|
||||
const innerErr = err.cause
|
||||
core.info(
|
||||
mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`)
|
||||
style.mute(
|
||||
innerErr instanceof Error ? innerErr.toString() : `${innerErr}`
|
||||
)
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
@@ -116,27 +118,19 @@ export async function run(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
const createAttestation = async (
|
||||
subject: Subject,
|
||||
predicate: Predicate,
|
||||
// Log details about the attestation to the GitHub Actions run
|
||||
const logAttestation = (
|
||||
attestation: AttestResult,
|
||||
sigstoreInstance: SigstoreInstance
|
||||
): Promise<Attestation> => {
|
||||
// Sign provenance w/ Sigstore
|
||||
const attestation = await attest({
|
||||
subjectName: subject.name,
|
||||
subjectDigest: subject.digest,
|
||||
predicateType: predicate.type,
|
||||
predicate: predicate.params,
|
||||
sigstore: sigstoreInstance,
|
||||
token: core.getInput('github-token')
|
||||
})
|
||||
|
||||
core.info(`Attestation created for ${subject.name}@${subjectDigest(subject)}`)
|
||||
): void => {
|
||||
core.info(
|
||||
`Attestation created for ${attestation.subjectName}@${attestation.subjectDigest}`
|
||||
)
|
||||
|
||||
const instanceName =
|
||||
sigstoreInstance === 'public-good' ? 'Public Good' : 'GitHub'
|
||||
core.startGroup(
|
||||
highlight(
|
||||
style.highlight(
|
||||
`Attestation signed using certificate from ${instanceName} Sigstore instance`
|
||||
)
|
||||
)
|
||||
@@ -145,43 +139,44 @@ const createAttestation = async (
|
||||
|
||||
if (attestation.tlogID) {
|
||||
core.info(
|
||||
highlight('Attestation signature uploaded to Rekor transparency log')
|
||||
style.highlight(
|
||||
'Attestation signature uploaded to Rekor transparency log'
|
||||
)
|
||||
)
|
||||
core.info(`${SEARCH_PUBLIC_GOOD_URL}?logIndex=${attestation.tlogID}`)
|
||||
}
|
||||
|
||||
if (attestation.attestationID) {
|
||||
core.info(highlight('Attestation uploaded to repository'))
|
||||
core.info(style.highlight('Attestation uploaded to repository'))
|
||||
core.info(attestationURL(attestation.attestationID))
|
||||
}
|
||||
|
||||
if (core.getBooleanInput('push-to-registry', { required: false })) {
|
||||
const credentials = getRegistryCredentials(subject.name)
|
||||
const artifact = await attachArtifactToImage({
|
||||
credentials,
|
||||
imageName: subject.name,
|
||||
imageDigest: subjectDigest(subject),
|
||||
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
|
||||
mediaType: attestation.bundle.mediaType,
|
||||
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}`)
|
||||
if (attestation.attestationDigest) {
|
||||
core.info(style.highlight('Attestation uploaded to registry'))
|
||||
core.info(`${attestation.subjectName}@${attestation.attestationDigest}`)
|
||||
}
|
||||
|
||||
return attestation
|
||||
}
|
||||
|
||||
// Emphasis string using ANSI color codes
|
||||
const highlight = (str: string): string => `${COLOR_CYAN}${str}${COLOR_DEFAULT}`
|
||||
// Attach summary information to the GitHub Actions run
|
||||
const logSummary = (attestations: AttestResult[]): void => {
|
||||
if (attestations.length > 0) {
|
||||
core.summary.addHeading(
|
||||
/* istanbul ignore next */
|
||||
attestations.length > 1 ? 'Attestations Created' : 'Attestation Created',
|
||||
3
|
||||
)
|
||||
|
||||
// De-emphasize string using ANSI color codes
|
||||
/* istanbul ignore next */
|
||||
const mute = (str: string): string => `${COLOR_GRAY}${str}${COLOR_DEFAULT}`
|
||||
for (const { subjectName, subjectDigest, attestationID } of attestations) {
|
||||
if (attestationID) {
|
||||
core.summary.addLink(
|
||||
`${subjectName}@${subjectDigest}`,
|
||||
attestationURL(attestationID)
|
||||
)
|
||||
}
|
||||
}
|
||||
core.summary.write()
|
||||
}
|
||||
}
|
||||
|
||||
const tempDir = (): string => {
|
||||
const basePath = process.env['RUNNER_TEMP']
|
||||
@@ -194,12 +189,18 @@ const tempDir = (): string => {
|
||||
return fs.mkdtempSync(path.join(basePath, path.sep))
|
||||
}
|
||||
|
||||
// Returns the subject's digest as a formatted string of the form
|
||||
// "<algorithm>:<digest>".
|
||||
const subjectDigest = (subject: Subject): string => {
|
||||
const alg = Object.keys(subject.digest).sort()[0]
|
||||
return `${alg}:${subject.digest[alg]}`
|
||||
// Transforms an array into an array of arrays, each containing at most
|
||||
// `chunkSize` elements.
|
||||
const chunkArray = <T>(array: T[], chunkSize: number): T[][] => {
|
||||
return Array.from(
|
||||
{ length: Math.ceil(array.length / chunkSize) },
|
||||
(_, index) => array.slice(index * chunkSize, (index + 1) * chunkSize)
|
||||
)
|
||||
}
|
||||
|
||||
// Calculate the delay time for a given iteration
|
||||
const delay = (iteration: number): number =>
|
||||
Math.min(DELAY_INTERVAL_MS * 2 ** iteration, DELAY_MAX_MS)
|
||||
|
||||
const attestationURL = (id: string): string =>
|
||||
`${github.context.serverUrl}/${github.context.repo.owner}/${github.context.repo.repo}/attestations/${id}`
|
||||
|
||||
+40
-10
@@ -1,26 +1,56 @@
|
||||
import * as core from '@actions/core'
|
||||
import fs from 'fs'
|
||||
|
||||
import type { Predicate } from '@actions/attest'
|
||||
|
||||
export type PredicateInputs = {
|
||||
predicateType: string
|
||||
predicate: string
|
||||
predicatePath: string
|
||||
}
|
||||
|
||||
const MAX_PREDICATE_SIZE_BYTES = 16 * 1024 * 1024
|
||||
|
||||
// Returns the predicate specified by the action's inputs. The predicate value
|
||||
// may be specified as a path to a file or as a string.
|
||||
export const predicateFromInputs = (): Predicate => {
|
||||
const predicateType = core.getInput('predicate-type', { required: true })
|
||||
const predicateStr = core.getInput('predicate', { required: false })
|
||||
const predicatePath = core.getInput('predicate-path', { required: false })
|
||||
export const predicateFromInputs = (inputs: PredicateInputs): Predicate => {
|
||||
const { predicateType, predicate, predicatePath } = inputs
|
||||
|
||||
if (!predicatePath && !predicateStr) {
|
||||
if (!predicateType) {
|
||||
throw new Error('predicate-type must be provided')
|
||||
}
|
||||
|
||||
if (!predicatePath && !predicate) {
|
||||
throw new Error('One of predicate-path or predicate must be provided')
|
||||
}
|
||||
|
||||
if (predicatePath && predicateStr) {
|
||||
if (predicatePath && predicate) {
|
||||
throw new Error('Only one of predicate-path or predicate may be provided')
|
||||
}
|
||||
|
||||
const params = predicatePath
|
||||
? fs.readFileSync(predicatePath, 'utf-8')
|
||||
: predicateStr
|
||||
let params: string = predicate
|
||||
|
||||
if (predicatePath) {
|
||||
if (!fs.existsSync(predicatePath)) {
|
||||
throw new Error(`predicate file not found: ${predicatePath}`)
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (fs.statSync(predicatePath).size > MAX_PREDICATE_SIZE_BYTES) {
|
||||
throw new Error(
|
||||
`predicate file exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`
|
||||
)
|
||||
}
|
||||
|
||||
params = fs.readFileSync(predicatePath, 'utf-8')
|
||||
} else {
|
||||
if (predicate.length > MAX_PREDICATE_SIZE_BYTES) {
|
||||
throw new Error(
|
||||
`predicate string exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`
|
||||
)
|
||||
}
|
||||
|
||||
params = predicate
|
||||
}
|
||||
|
||||
return { type: predicateType, params: JSON.parse(params) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
const COLOR_CYAN = '\x1B[36m'
|
||||
const COLOR_GRAY = '\x1B[38;5;244m'
|
||||
const COLOR_DEFAULT = '\x1B[39m'
|
||||
|
||||
// Emphasis string using ANSI color codes
|
||||
export const highlight = (str: string): string =>
|
||||
`${COLOR_CYAN}${str}${COLOR_DEFAULT}`
|
||||
|
||||
// De-emphasize string using ANSI color codes
|
||||
export const mute = (str: string): string =>
|
||||
`${COLOR_GRAY}${str}${COLOR_DEFAULT}`
|
||||
+36
-26
@@ -1,4 +1,3 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as glob from '@actions/glob'
|
||||
import crypto from 'crypto'
|
||||
import { parse } from 'csv-parse/sync'
|
||||
@@ -7,19 +6,23 @@ import path from 'path'
|
||||
|
||||
import type { Subject } from '@actions/attest'
|
||||
|
||||
const MAX_SUBJECT_COUNT = 2500
|
||||
const DIGEST_ALGORITHM = 'sha256'
|
||||
|
||||
export type SubjectInputs = {
|
||||
subjectPath: string
|
||||
subjectName: string
|
||||
subjectDigest: string
|
||||
downcaseName?: boolean
|
||||
}
|
||||
// Returns the subject specified by the action's inputs. The subject may be
|
||||
// specified as a path to a file or as a digest. If a path is provided, the
|
||||
// file's digest is calculated and returned along with the subject's name. If a
|
||||
// digest is provided, the name must also be provided.
|
||||
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
|
||||
})
|
||||
export const subjectFromInputs = async (
|
||||
inputs: SubjectInputs
|
||||
): Promise<Subject[]> => {
|
||||
const { subjectPath, subjectDigest, subjectName, downcaseName } = inputs
|
||||
|
||||
if (!subjectPath && !subjectDigest) {
|
||||
throw new Error('One of subject-path or subject-digest must be provided')
|
||||
@@ -37,7 +40,7 @@ export const subjectFromInputs = async (): Promise<Subject[]> => {
|
||||
|
||||
// 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
|
||||
const name = downcaseName ? subjectName.toLowerCase() : subjectName
|
||||
|
||||
if (subjectPath) {
|
||||
return await getSubjectFromPath(subjectPath, name)
|
||||
@@ -52,34 +55,41 @@ const getSubjectFromPath = async (
|
||||
subjectPath: string,
|
||||
subjectName?: string
|
||||
): Promise<Subject[]> => {
|
||||
const subjects: Subject[] = []
|
||||
const digestedSubjects: Subject[] = []
|
||||
const files: string[] = []
|
||||
|
||||
// Parse the list of subject paths
|
||||
const subjectPaths = parseList(subjectPath)
|
||||
|
||||
// Expand the globbed paths to a list of files
|
||||
for (const subPath of subjectPaths) {
|
||||
// Expand the globbed path to a list of files
|
||||
/* eslint-disable-next-line github/no-then */
|
||||
const files = await glob.create(subPath).then(async g => g.glob())
|
||||
|
||||
for (const file of files) {
|
||||
// Skip anything that is NOT a file
|
||||
if (!fs.statSync(file).isFile()) {
|
||||
continue
|
||||
}
|
||||
|
||||
const name = subjectName || path.parse(file).base
|
||||
const digest = await digestFile(DIGEST_ALGORITHM, file)
|
||||
|
||||
subjects.push({ name, digest: { [DIGEST_ALGORITHM]: digest } })
|
||||
}
|
||||
files.push(...(await glob.create(subPath).then(async g => g.glob())))
|
||||
}
|
||||
|
||||
if (subjects.length === 0) {
|
||||
if (files.length > MAX_SUBJECT_COUNT) {
|
||||
throw new Error(
|
||||
`Too many subjects specified. The maximum number of subjects is ${MAX_SUBJECT_COUNT}.`
|
||||
)
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
// Skip anything that is NOT a file
|
||||
if (!fs.statSync(file).isFile()) {
|
||||
continue
|
||||
}
|
||||
|
||||
const name = subjectName || path.parse(file).base
|
||||
const digest = await digestFile(DIGEST_ALGORITHM, file)
|
||||
|
||||
digestedSubjects.push({ name, digest: { [DIGEST_ALGORITHM]: digest } })
|
||||
}
|
||||
|
||||
if (digestedSubjects.length === 0) {
|
||||
throw new Error(`Could not find subject at path ${subjectPath}`)
|
||||
}
|
||||
|
||||
return Promise.all(subjects)
|
||||
return digestedSubjects
|
||||
}
|
||||
|
||||
// Returns the subject specified by the digest of a file. The digest is returned
|
||||
|
||||
Reference in New Issue
Block a user