diff --git a/README.md b/README.md
index 682f01ca..e9227466 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ This action allows you to automate the release process of your npm modules, apps
- [Example](#example-1)
- [How to add a build step to your workflow](#how-to-add-a-build-step-to-your-workflow)
- [Prerelease support](#prerelease-support)
+- [Provenance](#provenance)
- [Inputs](#inputs)
- [Motivation](#motivation)
- [Playground / Testing](#playground--testing)
@@ -83,14 +84,20 @@ jobs:
contents: write
issues: write
pull-requests: write
+ # optional: `id-token: write` permission is required if `provenance: true` is set below
+ id-token: write
steps:
- uses: nearform-actions/optic-release-automation-action@v4
with:
- npm-token: ${{ secrets.NPM_TOKEN }}
- optic-token: ${{ secrets.OPTIC_TOKEN }}
commit-message: ${{ github.event.inputs.commit-message }}
semver: ${{ github.event.inputs.semver }}
npm-tag: ${{ github.event.inputs.tag }}
+ # optional: set this secret in your repo config for publishing to NPM
+ npm-token: ${{ secrets.NPM_TOKEN }}
+ # optional: set this secret in your repo config for 2FA with Optic
+ optic-token: ${{ secrets.OPTIC_TOKEN }}
+ # optional: NPM will generate provenance statement, or abort release if it can't
+ provenance: true
```
The above workflow (when manually triggered) will:
@@ -110,6 +117,7 @@ When you merge this PR:
- Upon successful retrieval of the OTP, it will publish the package to Npm.
- Create a Github release with change logs (You can customize release notes using [release.yml](https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#example-configuration))
- Leave a comment on each issues that are linked to the pull reqeuests of this release. This feature can be turned off by the `notify-on-the-issue` flag.
+- _(Optional)_ If `provenance: true` was set, NPM will add a [Provenance](#provenance) notice to the package's public NPM page.
When you close the PR without merging it: nothing will happen.
@@ -217,6 +225,43 @@ Generally, if you want to release a prerelease of a repository, and it is an NPM
Please note that in case of a prerelease the `sync-semver-tags` input will be treated as `false`, even if it's set to `true`. This because we don't want to update the main version tags to the latest prerelease commit but only to the latest official release.
+## Provenance
+
+If `provenance: true` is added to your `release.yml`'s **inputs**, NPM will [generate a provenance statement](https://docs.npmjs.com/generating-provenance-statements).
+
+NPM has some internal [requirements](https://docs.npmjs.com/generating-provenance-statements#prerequisites) for generating provenance. Unfortunately as of May 2023, not all are documented by NPM; some key requirements are:
+
+- `id-token: write` must be added to your `release.yml`'s **permissions**
+- NPM must be on version 9.5.0 or greater (this will be met if our recommended `runs-on: ubuntu-latest` is used)
+- NPM has some undocumented internal requirements on `package.json` completeness. For example, the [repository field](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#repository) is required, and some NPM versions may require its `"url"` property to match the format `"git+https://github.com/user/repo"`.
+
+If any requirements are not met, the release will be aborted before publishing the new version, and an appropriate error will be shown in the actions report. The release commit can be reverted and the action re-tried after fixing the issue highlighted in the logged error.
+
+The above [example yml action](#example) includes support for Provenance. To add provenance support to an existing action, add these two lines:
+
+```yml
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+ # add this permission which is required for provenance
+ id-token: write
+ steps:
+ - uses: nearform-actions/optic-release-automation-action@v4
+ with:
+ npm-token: ${{ secrets.NPM_TOKEN }}
+ optic-token: ${{ secrets.OPTIC_TOKEN }}
+ commit-message: ${{ github.event.inputs.commit-message }}
+ semver: ${{ github.event.inputs.semver }}
+ npm-tag: ${{ github.event.inputs.tag }}
+ # add this to activate the action's provenance feature
+ provenance: true
+```
+
+
## Inputs
| Input | Required | Description |
@@ -239,6 +284,8 @@ Please note that in case of a prerelease the `sync-semver-tags` input will be tr
| `version-prefix` | No | A prefix to apply to the version number, which reflects in the tag and GitHub release names.
(_Default: 'v'_) |
| `prerelease-prefix` | No | A prefix to apply to the prerelease version number. |
| `base-tag` | No | Choose a specific tag release for your release notes. This input allows you to specify a base release (for example, v1.0.0) and will include all changes made in releases between the base release and the latest release. This input is only used for generating release notes and has no functional implications on the rest of the workflow. |
+| `provenance`| No | Set as true to have NPM [generate a provenance statement](https://docs.npmjs.com/generating-provenance-statements). See [Provenance section above](#provenance) for requirements.
(_Default: `false`_) |
+
## Motivation
diff --git a/action.yml b/action.yml
index 3aa02c13..ef8a5515 100644
--- a/action.yml
+++ b/action.yml
@@ -75,6 +75,11 @@ inputs:
base-tag:
description: 'This input allows you to specify a base release and will include all changes made in releases between the base release and the latest release'
required: false
+ provenance:
+ description: 'If true, NPM >9.5 will attempt to generate and display a "provenance" badge. See https://docs.npmjs.com/generating-provenance-statements'
+ required: false
+ type: boolean
+ default: 'false'
runs:
using: 'composite'
diff --git a/dist/index.js b/dist/index.js
index 98a09066..369fe4c8 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -79219,6 +79219,10 @@ const { publishToNpm } = __nccwpck_require__(1433)
const { notifyIssues } = __nccwpck_require__(8361)
const { logError, logInfo, logWarning } = __nccwpck_require__(653)
const { execWithOutput } = __nccwpck_require__(8632)
+const {
+ checkProvenanceViability,
+ getNpmVersion,
+} = __nccwpck_require__(3365)
module.exports = async function ({ github, context, inputs }) {
logInfo('** Starting Release **')
@@ -79303,9 +79307,23 @@ module.exports = async function ({ github, context, inputs }) {
try {
const opticToken = inputs['optic-token']
const npmToken = inputs['npm-token']
+ const provenance = /true/i.test(inputs['provenance'])
+
+ // Fail fast with meaningful error if user wants provenance but their setup won't deliver
+ if (provenance) {
+ const npmVersion = await getNpmVersion()
+ checkProvenanceViability(npmVersion)
+ }
if (npmToken) {
- await publishToNpm({ npmToken, opticToken, opticUrl, npmTag, version })
+ await publishToNpm({
+ npmToken,
+ opticToken,
+ opticUrl,
+ npmTag,
+ version,
+ provenance,
+ })
} else {
logWarning('missing npm-token')
}
@@ -79558,16 +79576,18 @@ const { exec } = __nccwpck_require__(1514)
* @param {{cwd?: string}} options
* @returns Promise
*/
-async function execWithOutput(cmd, args, { cwd } = {}) {
+async function execWithOutput(
+ cmd,
+ args,
+ { cwd, silent = false, ...options } = {}
+) {
let output = ''
let errorOutput = ''
const stdoutDecoder = new StringDecoder('utf8')
const stderrDecoder = new StringDecoder('utf8')
- const options = {
- silent: false,
- }
+ options.silent = silent
/* istanbul ignore else */
if (cwd !== '') {
@@ -79764,6 +79784,84 @@ async function notifyIssues(
exports.notifyIssues = notifyIssues
+/***/ }),
+
+/***/ 3365:
+/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
+
+"use strict";
+
+const semver = __nccwpck_require__(1383)
+const { execWithOutput } = __nccwpck_require__(8632)
+
+/**
+ * Abort if the user specified they want NPM provenance, but their CI's NPM version doesn't support it.
+ * If we continued, the release will go ahead with no warnings, and no provenance will be generated.
+ */
+function checkIsSupported(npmVersion) {
+ const validNpmVersion = '>=9.5.0'
+
+ if (!semver.satisfies(npmVersion, validNpmVersion)) {
+ throw new Error(
+ `Provenance requires NPM ${validNpmVersion}, but this action is using v${npmVersion}.
+Either remove provenance from your release action's inputs, or update your release CI's NPM version.`
+ )
+ }
+}
+
+/**
+ * Abort with a meaningful error if the user would get a misleading error message from NPM
+ * due to an NPM bug that existed between 9.5.0 and 9.6.1.
+ * As of April 2023, this would affect anyone whose CI is set to Node 18 (which defaults to NPM 9.5.1).
+ */
+function checkPermissions(npmVersion) {
+ // Bug was fixed in this NPM version - see https://github.com/npm/cli/pull/6226
+ const correctNpmErrorVersion = '>=9.6.1'
+
+ if (
+ // Same test condition as in fixed versions of NPM
+ !process.env.ACTIONS_ID_TOKEN_REQUEST_URL &&
+ // Let NPM handle this itself after their bug was fixed, so we're not brittle against future changes
+ !semver.satisfies(npmVersion, correctNpmErrorVersion)
+ ) {
+ throw new Error(
+ // Same error message as in fixed versions of NPM
+ 'Provenance generation in GitHub Actions requires "write" access to the "id-token" permission'
+ )
+ }
+}
+
+/**
+ * Fail fast and throw a meaningful error if NPM Provenance will fail silently or misleadingly.
+ *
+ * @see https://docs.npmjs.com/generating-provenance-statements
+ *
+ * @param {string} npmVersion
+ */
+function checkProvenanceViability(npmVersion) {
+ if (!npmVersion) throw new Error('Current npm version not provided')
+ checkIsSupported(npmVersion)
+ checkPermissions(npmVersion)
+ // There are various other provenance requirements, such as specific package.json properties, but these
+ // may change in future NPM versions, and do fail with meaningful errors, so we let NPM handle those.
+}
+
+/**
+ * Gets npm version via `npm -v` on the command line.
+ * Split out as its own export so it can be easily mocked in tests.
+ */
+async function getNpmVersion() {
+ return execWithOutput('npm', ['-v'])
+}
+
+module.exports = {
+ checkProvenanceViability,
+ getNpmVersion,
+ checkIsSupported,
+ checkPermissions,
+}
+
+
/***/ }),
/***/ 1433:
@@ -79820,6 +79918,7 @@ async function publishToNpm({
opticUrl,
npmTag,
version,
+ provenance,
}) {
await execWithOutput('npm', [
'config',
@@ -79827,6 +79926,11 @@ async function publishToNpm({
`//registry.npmjs.org/:_authToken=${npmToken}`,
])
+ const flags = ['--tag', npmTag]
+ if (provenance) {
+ flags.push('--provenance')
+ }
+
if (await allowNpmPublish(version)) {
await execWithOutput('npm', ['pack', '--dry-run'])
if (opticToken) {
@@ -79834,9 +79938,9 @@ async function publishToNpm({
'-s',
`${opticUrl}${opticToken}`,
])
- await execWithOutput('npm', ['publish', '--otp', otp, '--tag', npmTag])
+ await execWithOutput('npm', ['publish', '--otp', otp, ...flags])
} else {
- await execWithOutput('npm', ['publish', '--tag', npmTag])
+ await execWithOutput('npm', ['publish', ...flags])
}
}
}
diff --git a/src/release.js b/src/release.js
index 6c5139b1..4de4ca6a 100644
--- a/src/release.js
+++ b/src/release.js
@@ -11,6 +11,10 @@ const { publishToNpm } = require('./utils/publishToNpm')
const { notifyIssues } = require('./utils/notifyIssues')
const { logError, logInfo, logWarning } = require('./log')
const { execWithOutput } = require('./utils/execWithOutput')
+const {
+ checkProvenanceViability,
+ getNpmVersion,
+} = require('./utils/provenance')
module.exports = async function ({ github, context, inputs }) {
logInfo('** Starting Release **')
@@ -95,9 +99,23 @@ module.exports = async function ({ github, context, inputs }) {
try {
const opticToken = inputs['optic-token']
const npmToken = inputs['npm-token']
+ const provenance = /true/i.test(inputs['provenance'])
+
+ // Fail fast with meaningful error if user wants provenance but their setup won't deliver
+ if (provenance) {
+ const npmVersion = await getNpmVersion()
+ checkProvenanceViability(npmVersion)
+ }
if (npmToken) {
- await publishToNpm({ npmToken, opticToken, opticUrl, npmTag, version })
+ await publishToNpm({
+ npmToken,
+ opticToken,
+ opticUrl,
+ npmTag,
+ version,
+ provenance,
+ })
} else {
logWarning('missing npm-token')
}
diff --git a/src/utils/execWithOutput.js b/src/utils/execWithOutput.js
index ce102ca3..6b583353 100644
--- a/src/utils/execWithOutput.js
+++ b/src/utils/execWithOutput.js
@@ -11,16 +11,18 @@ const { exec } = require('@actions/exec')
* @param {{cwd?: string}} options
* @returns Promise
*/
-async function execWithOutput(cmd, args, { cwd } = {}) {
+async function execWithOutput(
+ cmd,
+ args,
+ { cwd, silent = false, ...options } = {}
+) {
let output = ''
let errorOutput = ''
const stdoutDecoder = new StringDecoder('utf8')
const stderrDecoder = new StringDecoder('utf8')
- const options = {
- silent: false,
- }
+ options.silent = silent
/* istanbul ignore else */
if (cwd !== '') {
diff --git a/src/utils/provenance.js b/src/utils/provenance.js
new file mode 100644
index 00000000..6204cf10
--- /dev/null
+++ b/src/utils/provenance.js
@@ -0,0 +1,70 @@
+'use strict'
+const semver = require('semver')
+const { execWithOutput } = require('./execWithOutput')
+
+/**
+ * Abort if the user specified they want NPM provenance, but their CI's NPM version doesn't support it.
+ * If we continued, the release will go ahead with no warnings, and no provenance will be generated.
+ */
+function checkIsSupported(npmVersion) {
+ const validNpmVersion = '>=9.5.0'
+
+ if (!semver.satisfies(npmVersion, validNpmVersion)) {
+ throw new Error(
+ `Provenance requires NPM ${validNpmVersion}, but this action is using v${npmVersion}.
+Either remove provenance from your release action's inputs, or update your release CI's NPM version.`
+ )
+ }
+}
+
+/**
+ * Abort with a meaningful error if the user would get a misleading error message from NPM
+ * due to an NPM bug that existed between 9.5.0 and 9.6.1.
+ * As of April 2023, this would affect anyone whose CI is set to Node 18 (which defaults to NPM 9.5.1).
+ */
+function checkPermissions(npmVersion) {
+ // Bug was fixed in this NPM version - see https://github.com/npm/cli/pull/6226
+ const correctNpmErrorVersion = '>=9.6.1'
+
+ if (
+ // Same test condition as in fixed versions of NPM
+ !process.env.ACTIONS_ID_TOKEN_REQUEST_URL &&
+ // Let NPM handle this itself after their bug was fixed, so we're not brittle against future changes
+ !semver.satisfies(npmVersion, correctNpmErrorVersion)
+ ) {
+ throw new Error(
+ // Same error message as in fixed versions of NPM
+ 'Provenance generation in GitHub Actions requires "write" access to the "id-token" permission'
+ )
+ }
+}
+
+/**
+ * Fail fast and throw a meaningful error if NPM Provenance will fail silently or misleadingly.
+ *
+ * @see https://docs.npmjs.com/generating-provenance-statements
+ *
+ * @param {string} npmVersion
+ */
+function checkProvenanceViability(npmVersion) {
+ if (!npmVersion) throw new Error('Current npm version not provided')
+ checkIsSupported(npmVersion)
+ checkPermissions(npmVersion)
+ // There are various other provenance requirements, such as specific package.json properties, but these
+ // may change in future NPM versions, and do fail with meaningful errors, so we let NPM handle those.
+}
+
+/**
+ * Gets npm version via `npm -v` on the command line.
+ * Split out as its own export so it can be easily mocked in tests.
+ */
+async function getNpmVersion() {
+ return execWithOutput('npm', ['-v'])
+}
+
+module.exports = {
+ checkProvenanceViability,
+ getNpmVersion,
+ checkIsSupported,
+ checkPermissions,
+}
diff --git a/src/utils/publishToNpm.js b/src/utils/publishToNpm.js
index 662ec879..6a7bc7f0 100644
--- a/src/utils/publishToNpm.js
+++ b/src/utils/publishToNpm.js
@@ -48,6 +48,7 @@ async function publishToNpm({
opticUrl,
npmTag,
version,
+ provenance,
}) {
await execWithOutput('npm', [
'config',
@@ -55,6 +56,11 @@ async function publishToNpm({
`//registry.npmjs.org/:_authToken=${npmToken}`,
])
+ const flags = ['--tag', npmTag]
+ if (provenance) {
+ flags.push('--provenance')
+ }
+
if (await allowNpmPublish(version)) {
await execWithOutput('npm', ['pack', '--dry-run'])
if (opticToken) {
@@ -62,9 +68,9 @@ async function publishToNpm({
'-s',
`${opticUrl}${opticToken}`,
])
- await execWithOutput('npm', ['publish', '--otp', otp, '--tag', npmTag])
+ await execWithOutput('npm', ['publish', '--otp', otp, ...flags])
} else {
- await execWithOutput('npm', ['publish', '--tag', npmTag])
+ await execWithOutput('npm', ['publish', ...flags])
}
}
}
diff --git a/test/provenance.test.js b/test/provenance.test.js
new file mode 100644
index 00000000..12144c8b
--- /dev/null
+++ b/test/provenance.test.js
@@ -0,0 +1,84 @@
+'use strict'
+
+const tap = require('tap')
+const semver = require('semver')
+const sinon = require('sinon')
+const {
+ checkIsSupported,
+ checkPermissions,
+ checkProvenanceViability,
+ getNpmVersion,
+} = require('../src/utils/provenance')
+
+const MINIMUM_VERSION = '9.5.0'
+
+tap.afterEach(() => {
+ sinon.restore()
+})
+
+tap.test('getNpmVersion can get a real NPM version number', async t => {
+ const npmVersion = await getNpmVersion()
+
+ t.type(npmVersion, 'string')
+
+ // We don't care which version of NPM tests are run on, just that it gets any valid version
+ t.ok(semver.satisfies(npmVersion, '>0.0.1'))
+})
+
+tap.test('checkIsSupported passes on minimum NPM version', async t => {
+ t.doesNotThrow(() => checkIsSupported(MINIMUM_VERSION))
+})
+
+tap.test('checkIsSupported passes on major version after minimum', async t => {
+ t.doesNotThrow(() => checkIsSupported('10.0.0'))
+})
+
+tap.test('checkIsSupported fails on minor version before minimum', async t => {
+ t.throws(
+ () => checkIsSupported('9.4.0'),
+ `Provenance requires NPM ${MINIMUM_VERSION}`
+ )
+})
+
+tap.test('checkIsSupported fails on major version before minimum', async t => {
+ t.throws(
+ () => checkIsSupported('8.0.0'),
+ `Provenance requires NPM ${MINIMUM_VERSION}`
+ )
+})
+
+tap.test('checkPermissions always passes on NPM 9.6.1', async t => {
+ t.doesNotThrow(() => checkIsSupported('9.6.1'))
+})
+
+tap.test(
+ 'checkPermissions always passes on next major NPM version',
+ async t => {
+ t.doesNotThrow(() => checkIsSupported('10.0.0'))
+ }
+)
+
+tap.test('checkPermissions fails on minimum version without env', async t => {
+ t.throws(
+ () => checkPermissions(MINIMUM_VERSION),
+ 'Provenance generation in GitHub Actions requires "write" access to the "id-token" permission'
+ )
+})
+
+tap.test('checkPermissions passes on minimum version with env', async t => {
+ sinon
+ .stub(process, 'env')
+ .value({ ACTIONS_ID_TOKEN_REQUEST_URL: 'https://example.com' })
+
+ t.doesNotThrow(() => checkIsSupported(MINIMUM_VERSION))
+})
+
+tap.test(
+ 'checkProvenanceViability fails fast if NPM version unavailable',
+ async t => {
+ t.throws(
+ () => checkProvenanceViability(),
+ 'Current npm version not provided'
+ )
+ }
+)
diff --git a/test/publishToNpm.test.js b/test/publishToNpm.test.js
index 51045cf6..9dab7e2d 100644
--- a/test/publishToNpm.test.js
+++ b/test/publishToNpm.test.js
@@ -283,3 +283,21 @@ tap.test(
])
}
)
+
+tap.test('Adds --provenance flag when provenance option provided', async () => {
+ const { publishToNpmProxy, execWithOutputStub } = setup()
+ await publishToNpmProxy.publishToNpm({
+ npmToken: 'a-token',
+ opticUrl: 'https://optic-test.run.app/api/generate/',
+ npmTag: 'latest',
+ version: 'v5.1.3',
+ provenance: true,
+ })
+
+ sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [
+ 'publish',
+ '--tag',
+ 'latest',
+ '--provenance',
+ ])
+})
diff --git a/test/release.test.js b/test/release.test.js
index af203d97..6f893a08 100644
--- a/test/release.test.js
+++ b/test/release.test.js
@@ -63,7 +63,17 @@ const DEFAULT_ACTION_DATA = {
packageName: 'testPackageName',
}
-function setup() {
+/**
+ * @param {{ npmVersion: string | undefined, env: Record | undefined }} [options]
+ */
+function setup({ npmVersion, env } = {}) {
+ if (env) {
+ // Add any test-specific environment variables. They get cleaned up by tap.afterEach(sinon.restore).
+ Object.entries(env).forEach(([key, value]) => {
+ sinon.stub(process, 'env').value({ [key]: value })
+ })
+ }
+
const logStub = sinon.stub(actionLog)
const coreStub = sinon.stub(core)
deleteReleaseStub.resetHistory()
@@ -86,14 +96,20 @@ function setup() {
.stub(callApiAction, 'callApi')
.resolves({ data: { body: 'test_body', html_url: 'test_url' } })
- const release = proxyquire('../src/release', {
+ const proxyStubs = {
'./utils/execWithOutput': { execWithOutput: execWithOutputStub },
'./utils/tagVersion': tagVersionStub,
'./utils/revertCommit': revertCommitStub,
'./utils/publishToNpm': publishToNpmStub,
'./utils/notifyIssues': notifyIssuesStub,
'@actions/core': coreStub,
- })
+ }
+
+ if (npmVersion) {
+ proxyStubs['./utils/provenance'] = { getNpmVersion: () => npmVersion }
+ }
+
+ const release = proxyquire('../src/release', proxyStubs)
return {
release,
@@ -200,6 +216,73 @@ tap.test('Should publish to npm without optic', async () => {
})
})
+tap.test(
+ 'Should publish with provenance if flag set and conditions met',
+ async () => {
+ const { release, stubs } = setup({
+ npmVersion: '9.5.0', // valid
+ env: { ACTIONS_ID_TOKEN_REQUEST_URL: 'https://example.com' }, // valid
+ })
+ await release({
+ ...DEFAULT_ACTION_DATA,
+ inputs: {
+ 'app-name': APP_NAME,
+ 'npm-token': 'a-token',
+ provenance: 'true',
+ },
+ })
+
+ sinon.assert.notCalled(stubs.coreStub.setFailed)
+ sinon.assert.calledWithMatch(stubs.publishToNpmStub, {
+ npmToken: 'a-token',
+ opticUrl: 'https://optic-test.run.app/api/generate/',
+ npmTag: 'latest',
+ provenance: true,
+ })
+ }
+)
+
+tap.test('Aborts publish with provenance if NPM version too old', async () => {
+ const { release, stubs } = setup({
+ npmVersion: '9.4.0', // too old (is before 9.5.0)
+ env: { ACTIONS_ID_TOKEN_REQUEST_URL: 'https://example.com' }, // valid
+ })
+
+ await release({
+ ...DEFAULT_ACTION_DATA,
+ inputs: {
+ 'app-name': APP_NAME,
+ 'npm-token': 'a-token',
+ provenance: 'true',
+ },
+ })
+
+ sinon.assert.calledWithMatch(
+ stubs.coreStub.setFailed,
+ 'Provenance requires NPM >=9.5.0, but this action is using v9.4.0'
+ )
+})
+
+tap.test('Aborts publish with provenance if missing permission', async () => {
+ const { release, stubs } = setup({
+ npmVersion: '9.5.0', // valid, but before missing var is correctly handled on NPM's side (9.6.1)
+ // missing ACTIONS_ID_TOKEN_REQUEST_URL which is set from `id-token: write` permission.
+ })
+
+ await release({
+ ...DEFAULT_ACTION_DATA,
+ inputs: {
+ 'app-name': APP_NAME,
+ 'npm-token': 'a-token',
+ provenance: 'true',
+ },
+ })
+ sinon.assert.calledWithMatch(
+ stubs.coreStub.setFailed,
+ 'Provenance generation in GitHub Actions requires "write" access to the "id-token" permission'
+ )
+})
+
tap.test('Should not publish to npm if there is no npm token', async () => {
const { release, stubs } = setup()
stubs.callApiStub.throws()