more test coverage (#18)
Signed-off-by: Brian DeHamer <[email protected]>
This commit is contained in:
@@ -34,8 +34,8 @@ attest:
|
|||||||
contents: write # TODO: Update this
|
contents: write # TODO: Update this
|
||||||
```
|
```
|
||||||
|
|
||||||
The `id-token` permission gives the action the ability to mint the OIDC
|
The `id-token` permission gives the action the ability to mint the OIDC token
|
||||||
token necessary to request a Sigstore signing certificate. The `contents`
|
necessary to request a Sigstore signing certificate. The `contents`
|
||||||
permission is necessary to persist the attestation.
|
permission is necessary to persist the attestation.
|
||||||
|
|
||||||
1. Add the following to your workflow after your artifact has been built:
|
1. Add the following to your workflow after your artifact has been built:
|
||||||
@@ -100,8 +100,8 @@ See [action.yml](action.yml)
|
|||||||
<!-- markdownlint-disable MD013 -->
|
<!-- markdownlint-disable MD013 -->
|
||||||
|
|
||||||
| Name | Description | Example |
|
| Name | Description | Example |
|
||||||
| ------------- | -------------------------------------------------------------- | ----------------------- |
|
| ------------- | -------------------------------------------------------------- | ------------------------ |
|
||||||
| `bundle-path` | Absolute path to the file containing the generated attestation | `/tmp/attestaion.jsonl` |
|
| `bundle-path` | Absolute path to the file containing the generated attestation | `/tmp/attestation.jsonl` |
|
||||||
|
|
||||||
<!-- markdownlint-enable MD013 -->
|
<!-- markdownlint-enable MD013 -->
|
||||||
|
|
||||||
|
|||||||
+21
-2
@@ -9,6 +9,7 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import { mockFulcio, mockRekor, mockTSA } from '@sigstore/mock'
|
import { mockFulcio, mockRekor, mockTSA } from '@sigstore/mock'
|
||||||
|
import * as oci from '@sigstore/oci'
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import { SEARCH_PUBLIC_GOOD_URL } from '../src/endpoints'
|
import { SEARCH_PUBLIC_GOOD_URL } from '../src/endpoints'
|
||||||
import * as main from '../src/main'
|
import * as main from '../src/main'
|
||||||
@@ -43,7 +44,7 @@ describe('action', () => {
|
|||||||
'base64'
|
'base64'
|
||||||
)}.}`
|
)}.}`
|
||||||
|
|
||||||
const subjectName = 'subject'
|
const subjectName = 'registry/foo/bar'
|
||||||
const subjectDigest =
|
const subjectDigest =
|
||||||
'sha256:7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
|
'sha256:7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
|
||||||
const predicate = '{}'
|
const predicate = '{}'
|
||||||
@@ -189,6 +190,9 @@ describe('action', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('when the repository is public', () => {
|
describe('when the repository is public', () => {
|
||||||
|
const getRegCredsSpy = jest.spyOn(oci, 'getRegistryCredentials')
|
||||||
|
const attachArtifactSpy = jest.spyOn(oci, 'attachArtifactToImage')
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
'subject-digest': subjectDigest,
|
'subject-digest': subjectDigest,
|
||||||
'subject-name': subjectName,
|
'subject-name': subjectName,
|
||||||
@@ -206,13 +210,26 @@ describe('action', () => {
|
|||||||
|
|
||||||
// Mock the action's inputs
|
// Mock the action's inputs
|
||||||
getInputMock.mockImplementation(mockInput(inputs))
|
getInputMock.mockImplementation(mockInput(inputs))
|
||||||
getBooleanInputMock.mockImplementation(() => false)
|
// This is where we mock the push-to-registry input
|
||||||
|
getBooleanInputMock.mockImplementation(() => true)
|
||||||
|
|
||||||
await mockFulcio({
|
await mockFulcio({
|
||||||
baseURL: 'https://fulcio.sigstore.dev',
|
baseURL: 'https://fulcio.sigstore.dev',
|
||||||
strict: false
|
strict: false
|
||||||
})
|
})
|
||||||
await mockRekor({ baseURL: 'https://rekor.sigstore.dev' })
|
await mockRekor({ baseURL: 'https://rekor.sigstore.dev' })
|
||||||
|
|
||||||
|
getRegCredsSpy.mockImplementation(() => ({
|
||||||
|
username: 'username',
|
||||||
|
password: 'password'
|
||||||
|
}))
|
||||||
|
attachArtifactSpy.mockImplementation(async () =>
|
||||||
|
Promise.resolve({
|
||||||
|
digest: 'sha256:123456',
|
||||||
|
mediaType: 'application/vnd.cncf.notary.v2',
|
||||||
|
size: 123456
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('invokes the action w/o error', async () => {
|
it('invokes the action w/o error', async () => {
|
||||||
@@ -220,6 +237,8 @@ describe('action', () => {
|
|||||||
|
|
||||||
expect(runMock).toHaveReturned()
|
expect(runMock).toHaveReturned()
|
||||||
expect(setFailedMock).not.toHaveBeenCalled()
|
expect(setFailedMock).not.toHaveBeenCalled()
|
||||||
|
expect(getRegCredsSpy).toHaveBeenCalledWith(subjectName)
|
||||||
|
expect(attachArtifactSpy).toHaveBeenCalled()
|
||||||
expect(infoMock).toHaveBeenNthCalledWith(
|
expect(infoMock).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
expect.stringMatching(
|
expect.stringMatching(
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import fs from 'fs/promises'
|
||||||
|
import os from 'os'
|
||||||
|
import path from 'path'
|
||||||
|
import { predicateFromInputs } from '../src/predicate'
|
||||||
|
|
||||||
|
describe('subjectFromInputs', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
process.env['INPUT_PREDICATE'] = ''
|
||||||
|
process.env['INPUT_PREDICATE-PATH'] = ''
|
||||||
|
process.env['INPUT_PREDICATE-TYPE'] = ''
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when no inputs are provided', () => {
|
||||||
|
it('throws an error', () => {
|
||||||
|
expect(() => predicateFromInputs()).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(
|
||||||
|
/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(
|
||||||
|
/only one of predicate-path or predicate may be provided/i
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when specifying a predicate path', () => {
|
||||||
|
let dir = ''
|
||||||
|
const filename = 'subject'
|
||||||
|
const content = '{}'
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// Set-up temp directory
|
||||||
|
const tmpDir = await fs.realpath(os.tmpdir())
|
||||||
|
dir = await fs.mkdtemp(tmpDir + path.sep)
|
||||||
|
|
||||||
|
// Write file to temp directory
|
||||||
|
await fs.writeFile(path.join(dir, filename), 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'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the predicate', () => {
|
||||||
|
expect(predicateFromInputs()).toEqual({
|
||||||
|
type: 'https://example.com/predicate',
|
||||||
|
params: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when specifying a predicate value', () => {
|
||||||
|
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: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
import crypto from 'crypto'
|
||||||
|
import fs from 'fs/promises'
|
||||||
|
import os from 'os'
|
||||||
|
import path from 'path'
|
||||||
|
import { subjectFromInputs } from '../src/subject'
|
||||||
|
|
||||||
|
describe('subjectFromInputs', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
process.env['INPUT_SUBJECT-PATH'] = ''
|
||||||
|
process.env['INPUT_SUBJECT-DIGEST'] = ''
|
||||||
|
process.env['INPUT_SUBJECT-NAME'] = ''
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when no inputs are provided', () => {
|
||||||
|
it('throws an error', async () => {
|
||||||
|
await expect(subjectFromInputs()).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(
|
||||||
|
/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(
|
||||||
|
/subject-name must be provided when using subject-digest/i
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when specifying a subject digest', () => {
|
||||||
|
const name = 'subject'
|
||||||
|
|
||||||
|
describe('when the digest is malformed', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
|
||||||
|
process.env['INPUT_SUBJECT-NAME'] = 'subject'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws an error', async () => {
|
||||||
|
await expect(subjectFromInputs()).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'] = 'subject'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws an error', async () => {
|
||||||
|
await expect(subjectFromInputs()).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'] = 'subject'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws an error', async () => {
|
||||||
|
await expect(subjectFromInputs()).rejects.toThrow(
|
||||||
|
/subject-digest must be in the format "sha256:<hex-digest>"/i
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when the sha256 digest is valid', () => {
|
||||||
|
const alg = 'sha256'
|
||||||
|
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()
|
||||||
|
|
||||||
|
expect(subject).toBeDefined()
|
||||||
|
expect(subject).toHaveLength(1)
|
||||||
|
expect(subject[0].name).toEqual(name)
|
||||||
|
expect(subject[0].digest).toEqual({ [alg]: digest })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
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(
|
||||||
|
/could not find subject at path/i
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when the file eixts', () => {
|
||||||
|
let dir = ''
|
||||||
|
const filename = 'subject'
|
||||||
|
const content = 'file content'
|
||||||
|
|
||||||
|
const expectedDigest = crypto
|
||||||
|
.createHash('sha256')
|
||||||
|
.update(content)
|
||||||
|
.digest('hex')
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// Set-up temp directory
|
||||||
|
const tmpDir = await fs.realpath(os.tmpdir())
|
||||||
|
dir = await fs.mkdtemp(tmpDir + path.sep)
|
||||||
|
|
||||||
|
// Write file to temp directory
|
||||||
|
await fs.writeFile(path.join(dir, filename), content)
|
||||||
|
|
||||||
|
// Add files for glob testing
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
await fs.writeFile(path.join(dir, `${filename}-${i}`), content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
// Clean-up temp directory
|
||||||
|
await fs.rm(dir, { recursive: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
expect(subject).toBeDefined()
|
||||||
|
expect(subject).toHaveLength(1)
|
||||||
|
expect(subject[0].name).toEqual(filename)
|
||||||
|
expect(subject[0].digest).toEqual({ sha256: expectedDigest })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
expect(subject).toBeDefined()
|
||||||
|
expect(subject).toHaveLength(1)
|
||||||
|
expect(subject[0].name).toEqual(name)
|
||||||
|
expect(subject[0].digest).toEqual({ sha256: expectedDigest })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
expect(subjects).toBeDefined()
|
||||||
|
expect(subjects).toHaveLength(3)
|
||||||
|
|
||||||
|
/* eslint-disable-next-line github/array-foreach */
|
||||||
|
subjects.forEach((subject, i) => {
|
||||||
|
expect(subject.name).toEqual(`${filename}-${i}`)
|
||||||
|
expect(subject.digest).toEqual({ sha256: expectedDigest })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
+4
-15
@@ -65122,10 +65122,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.storePredicate = exports.predicateFromInputs = void 0;
|
exports.predicateFromInputs = void 0;
|
||||||
const core = __importStar(__nccwpck_require__(2186));
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
const fs_1 = __importDefault(__nccwpck_require__(7147));
|
const fs_1 = __importDefault(__nccwpck_require__(7147));
|
||||||
const path = __importStar(__nccwpck_require__(1017));
|
|
||||||
// Returns the predicate specified by the action's inputs. The predicate value
|
// 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.
|
// may be specified as a path to a file or as a string.
|
||||||
const predicateFromInputs = () => {
|
const predicateFromInputs = () => {
|
||||||
@@ -65135,25 +65134,15 @@ const predicateFromInputs = () => {
|
|||||||
if (!predicatePath && !predicateStr) {
|
if (!predicatePath && !predicateStr) {
|
||||||
throw new Error('One of predicate-path or predicate must be provided');
|
throw new Error('One of predicate-path or predicate must be provided');
|
||||||
}
|
}
|
||||||
|
if (predicatePath && predicateStr) {
|
||||||
|
throw new Error('Only one of predicate-path or predicate may be provided');
|
||||||
|
}
|
||||||
const params = predicatePath
|
const params = predicatePath
|
||||||
? fs_1.default.readFileSync(predicatePath, 'utf-8')
|
? fs_1.default.readFileSync(predicatePath, 'utf-8')
|
||||||
: predicateStr;
|
: predicateStr;
|
||||||
return { type: predicateType, params: JSON.parse(params) };
|
return { type: predicateType, params: JSON.parse(params) };
|
||||||
};
|
};
|
||||||
exports.predicateFromInputs = predicateFromInputs;
|
exports.predicateFromInputs = predicateFromInputs;
|
||||||
const storePredicate = (predicate) => {
|
|
||||||
// random tempfile
|
|
||||||
const basePath = process.env['RUNNER_TEMP'];
|
|
||||||
if (!basePath) {
|
|
||||||
throw new Error('Missing RUNNER_TEMP environment variable');
|
|
||||||
}
|
|
||||||
const tmpDir = fs_1.default.mkdtempSync(path.join(basePath, path.sep));
|
|
||||||
const tempFile = path.join(tmpDir, 'predicate.json');
|
|
||||||
// write predicate to file
|
|
||||||
fs_1.default.writeFileSync(tempFile, JSON.stringify(predicate.params));
|
|
||||||
return tempFile;
|
|
||||||
};
|
|
||||||
exports.storePredicate = storePredicate;
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|||||||
+5
-17
@@ -1,6 +1,6 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import * as path from 'path'
|
|
||||||
import type { Predicate } from '@actions/attest'
|
import type { Predicate } from '@actions/attest'
|
||||||
|
|
||||||
// Returns the predicate specified by the action's inputs. The predicate value
|
// Returns the predicate specified by the action's inputs. The predicate value
|
||||||
@@ -14,25 +14,13 @@ export const predicateFromInputs = (): Predicate => {
|
|||||||
throw new Error('One of predicate-path or predicate must be provided')
|
throw new Error('One of predicate-path or predicate must be provided')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (predicatePath && predicateStr) {
|
||||||
|
throw new Error('Only one of predicate-path or predicate may be provided')
|
||||||
|
}
|
||||||
|
|
||||||
const params = predicatePath
|
const params = predicatePath
|
||||||
? fs.readFileSync(predicatePath, 'utf-8')
|
? fs.readFileSync(predicatePath, 'utf-8')
|
||||||
: predicateStr
|
: predicateStr
|
||||||
|
|
||||||
return { type: predicateType, params: JSON.parse(params) }
|
return { type: predicateType, params: JSON.parse(params) }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const storePredicate = (predicate: Predicate): string => {
|
|
||||||
// random tempfile
|
|
||||||
const basePath = process.env['RUNNER_TEMP']
|
|
||||||
|
|
||||||
if (!basePath) {
|
|
||||||
throw new Error('Missing RUNNER_TEMP environment variable')
|
|
||||||
}
|
|
||||||
|
|
||||||
const tmpDir = fs.mkdtempSync(path.join(basePath, path.sep))
|
|
||||||
const tempFile = path.join(tmpDir, 'predicate.json')
|
|
||||||
|
|
||||||
// write predicate to file
|
|
||||||
fs.writeFileSync(tempFile, JSON.stringify(predicate.params))
|
|
||||||
return tempFile
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user