Skip to content

Commit cb083ce

Browse files
authored
feat: allow --ignore-scripts to be disabled, if needed (#102)
1 parent 569541e commit cb083ce

18 files changed

+228
-60
lines changed

README.md

+58-14
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,45 @@ This package can be used three different ways:
3131

3232
- 🤖 A [**GitHub Action**](#github-action) as part of your CI/CD process
3333

34-
- 🧩 A [**function**](#javascript-function) that you call in your JavaScript code
34+
- 🧩 A [**function**](#javascript-api) that you call in your JavaScript code
3535

3636
- 🖥 A [**CLI**](#command-line-interface) that you run in your terminal
3737

3838
## v2 Migration Guide
3939

4040
The v1 to v2 upgrade brought a few notable **breaking changes**. To migrate, make the following updates:
4141

42-
- The `type` output is now an empty string instead of `none` when no release occurs
42+
- The `type` output is now an empty string instead of `'none'` when no release occurs
4343
```diff
4444
- run: echo "Version changed!"
4545
- if: ${{ steps.publish.outputs.type != 'none' }}
4646
+ if: ${{ steps.publish.outputs.type }}
4747
```
48+
- The `--ignore-scripts` option is now passed to `npm publish` as a security precaution. If you define any publish lifecycle scripts - `prepublishOnly`, `prepack`, `prepare`, `postpack`, `publish`, `postpublish` - run them explicitly or set the `ignore-scripts` input to `false`.
49+
```diff
50+
with:
51+
token: ${{ secrets.NPM_TOKEN }}
52+
+ ignore-scripts: false
53+
```
54+
- The workflow's `.npmrc` file is not longer modified. If you have any workarounds to adjust for this misbehavior - for example, if you're using `actions/setup-node` to configure `.npmrc` - you should remove them.
55+
56+
```diff
57+
- uses: actions/setup-node@v3
58+
with:
59+
node-version: '18'
60+
registry-url: https://registry.npmjs.org/
61+
62+
- uses: JS-DevTools/npm-publish@v1
63+
with:
64+
token: ${{ secrets.NPM_TOKEN }}
65+
66+
- name: Do some more stuff with npm
67+
run: npm whoami
68+
env:
69+
- INPUT_TOKEN: ${{ secrets.NPM_TOKEN }}
70+
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
71+
```
72+
4873
- The `check-version` and `greater-version-only` options have been removed and replaced with `strategy`.
4974
- Use `strategy: all` (default) to publish all versions that do not yet exist in the registry.
5075
```diff
@@ -62,10 +87,24 @@ The v1 to v2 upgrade brought a few notable **breaking changes**. To migrate, mak
6287
- greater-version-only: true
6388
+ strategy: upgrade
6489
```
65-
- `check-version: false` has been removed. You don't need this action if you're not checking already published versions; use `npm` directly, instead.
90+
- `check-version: false` has been removed. You may not need this action if you're not checking already published versions; [you can `npm` directly][publishing-nodejs-packages], instead.
91+
```diff
92+
- - uses: JS-DevTools/npm-publish@v1
93+
- with:
94+
- token: ${{ secrets.NPM_TOKEN }}
95+
- check-version: false
96+
+ - uses: actions/setup-node@v3
97+
+ with:
98+
+ node-version: '18'
99+
+ registry-url: https://registry.npmjs.org/
100+
+ - run: npm publish
101+
+ env:
102+
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
103+
```
66104

67105
See the [change log][] for more details and other changes in the v2 release.
68106

107+
[publishing-nodejs-packages]: https://docs.github.com/actions/publishing-packages/publishing-nodejs-packages
69108
[change log]: https://github.com/JS-DevTools/npm-publish/releases
70109

71110
## GitHub Action
@@ -99,16 +138,17 @@ jobs:
99138

100139
You can set any or all of the following input parameters using `with`:
101140

102-
| Name | Type | Default | Description |
103-
| --------------- | ---------------------- | ----------------------------- | -------------------------------------------------------------------------------- |
104-
| `token` | string | **required** | Authentication token to use with the configured registry. |
105-
| `registry`¹ | string | `https://registry.npmjs.org/` | Registry URL to use. |
106-
| `package` | string | Current working directory | Path to a package directory, a `package.json`, or a packed `.tgz` to publish. |
107-
| `tag`¹ | string | `latest` | [Distribution tag][npm-tag] to publish to. |
108-
| `access`¹ | `public`, `restricted` | [npm defaults][npm-access] | Whether the package should be publicly visible or restricted. |
109-
| `provenance`¹ ² | boolean | `false` | Run `npm publish` with the `--provenance` flag to add [provenance][] statements. |
110-
| `strategy` | `all`, `upgrade` | `all` | Use `all` to publish all unique versions, `upgrade` for only semver upgrades. |
111-
| `dry-run` | boolean | `false` | Run `npm publish` with the `--dry-run` flag to prevent publication. |
141+
| Name | Type | Default | Description |
142+
| ---------------- | ---------------------- | ----------------------------- | -------------------------------------------------------------------------------- |
143+
| `token` | string | **required** | Authentication token to use with the configured registry. |
144+
| `registry`¹ | string | `https://registry.npmjs.org/` | Registry URL to use. |
145+
| `package` | string | Current working directory | Path to a package directory, a `package.json`, or a packed `.tgz` to publish. |
146+
| `tag`¹ | string | `latest` | [Distribution tag][npm-tag] to publish to. |
147+
| `access`¹ | `public`, `restricted` | [npm defaults][npm-access] | Whether the package should be publicly visible or restricted. |
148+
| `provenance`¹ ² | boolean | `false` | Run `npm publish` with the `--provenance` flag to add [provenance][] statements. |
149+
| `strategy` | `all`, `upgrade` | `all` | Use `all` to publish all unique versions, `upgrade` for only semver upgrades. |
150+
| `ignore-scripts` | boolean | `true` | Run `npm publish` with the `--ignore-scripts` flag as a security precaution. |
151+
| `dry-run` | boolean | `false` | Run `npm publish` with the `--dry-run` flag to prevent publication. |
112152

113153
1. May be specified using `publishConfig` in `package.json`.
114154
2. Provenance requires npm `>=9.5.0`.
@@ -147,7 +187,7 @@ steps:
147187

148188
[semver release type]: https://github.com/npm/node-semver#release_types
149189

150-
## JavaScript Function
190+
## JavaScript API
151191

152192
To use npm-package in your JavaScript code, you'll need to install it using [npm][] or other package manager of choice:
153193

@@ -183,6 +223,7 @@ import type { Options } from "@jsdevtools/npm-publish";
183223
| `access`¹ | `public`, `restricted` | [npm defaults][npm-access] | Whether the package should be publicly visible or restricted. |
184224
| `provenance`¹ ² | boolean | `false` | Run `npm publish` with the `--provenance` flag to add [provenance][] statements. |
185225
| `strategy` | `all`, `upgrade` | `all` | Use `all` to publish all unique versions, `upgrade` for only semver upgrades. |
226+
| `ignoreScripts` | boolean | `true` | Run `npm publish` with the `--ignore-scripts` flag as a security precaution. |
186227
| `dryRun` | boolean | `false` | Run `npm publish` with the `--dry-run` flag to prevent publication. |
187228
| `logger` | object | `undefined` | Logging interface with `debug`, `info`, and `error` log methods. |
188229
| `temporaryDirectory` | string | `os.tmpdir()` | Temporary directory to hold a generated `.npmrc` file |
@@ -264,6 +305,9 @@ Options:
264305
--strategy <strategy> Publish strategy, may be "all" or "upgrade".
265306
Defaults to "all", see documentation for details.
266307

308+
--no-ignore-scripts Allow lifecycle scripts, which are disabled by default
309+
as a security precaution. Defaults to false.
310+
267311
--dry-run Do not actually publish anything.
268312
--quiet Only print errors.
269313
--debug Print debug logs.

action.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ inputs:
4242
4343
required: false
4444

45+
ignore-scripts:
46+
description: >
47+
Run npm with the --ignore-scripts flag as a security precaution.
48+
Enabled by default.
49+
required: false
50+
4551
dry-run:
4652
description: Run npm with the --dry-run flag to avoid actually publishing anything.
4753
required: false

dist/main.js

+15-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/main.js.map

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/__tests__/normalize-options.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ describe("normalizeOptions", () => {
7575
tag: { value: "latest", isDefault: true },
7676
access: { value: "public", isDefault: true },
7777
provenance: { value: false, isDefault: true },
78+
ignoreScripts: { value: true, isDefault: true },
7879
dryRun: { value: false, isDefault: true },
7980
strategy: { value: "all", isDefault: true },
8081
});
@@ -100,6 +101,7 @@ describe("normalizeOptions", () => {
100101
tag: "next",
101102
access: "public",
102103
provenance: true,
104+
ignoreScripts: false,
103105
dryRun: true,
104106
strategy: "all",
105107
}
@@ -109,6 +111,7 @@ describe("normalizeOptions", () => {
109111
tag: { value: "next", isDefault: false },
110112
access: { value: "public", isDefault: false },
111113
provenance: { value: true, isDefault: false },
114+
ignoreScripts: { value: false, isDefault: false },
112115
dryRun: { value: true, isDefault: false },
113116
strategy: { value: "all", isDefault: false },
114117
});

src/action/__tests__/main.test.ts

+11-17
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,25 @@ vi.mock("../core", () => imitateEsm("../core"));
1111
describe("run", () => {
1212
beforeEach(() => {
1313
vi.stubEnv("RUNNER_TEMP", "/path/to/temp");
14-
});
15-
16-
afterEach(() => {
17-
vi.unstubAllEnvs();
18-
vi.resetModules();
19-
reset();
20-
});
2114

22-
it("should pass input to options", async () => {
2315
td.when(core.getRequiredSecretInput("token")).thenReturn("abc123");
2416
td.when(core.getInput("package")).thenReturn("./package.json");
2517
td.when(core.getInput("registry")).thenReturn("https://example.com");
2618
td.when(core.getInput("tag")).thenReturn("next");
2719
td.when(core.getInput("access")).thenReturn("restricted");
2820
td.when(core.getBooleanInput("provenance")).thenReturn(true);
2921
td.when(core.getInput("strategy")).thenReturn("all");
22+
td.when(core.getBooleanInput("ignore-scripts")).thenReturn(false);
3023
td.when(core.getBooleanInput("dry-run")).thenReturn(true);
24+
});
3125

26+
afterEach(() => {
27+
vi.unstubAllEnvs();
28+
vi.resetModules();
29+
reset();
30+
});
31+
32+
it("should pass input to options", async () => {
3233
td.when(
3334
npmPublish({
3435
token: "abc123",
@@ -38,6 +39,7 @@ describe("run", () => {
3839
access: "restricted",
3940
provenance: true,
4041
strategy: "all",
42+
ignoreScripts: false,
4143
dryRun: true,
4244
logger: core.logger,
4345
temporaryDirectory: "/path/to/temp",
@@ -72,15 +74,6 @@ describe("run", () => {
7274
it("should fail the action if something raises", async () => {
7375
const error = new Error("oh no");
7476

75-
td.when(core.getRequiredSecretInput("token")).thenReturn("abc123");
76-
td.when(core.getInput("package")).thenReturn("./package.json");
77-
td.when(core.getInput("registry")).thenReturn("https://example.com");
78-
td.when(core.getInput("tag")).thenReturn("next");
79-
td.when(core.getInput("access")).thenReturn("restricted");
80-
td.when(core.getBooleanInput("provenance")).thenReturn(true);
81-
td.when(core.getInput("strategy")).thenReturn("all");
82-
td.when(core.getBooleanInput("dry-run")).thenReturn(true);
83-
8477
td.when(
8578
npmPublish({
8679
token: "abc123",
@@ -90,6 +83,7 @@ describe("run", () => {
9083
access: "restricted",
9184
provenance: true,
9285
strategy: "all",
86+
ignoreScripts: false,
9387
dryRun: true,
9488
logger: core.logger,
9589
temporaryDirectory: "/path/to/temp",

src/action/core.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,14 @@ export function getRequiredSecretInput(name: string): string {
4545
* Get a boolean input by name.
4646
*
4747
* @param name Input name
48-
* @returns True if value is "true", false if not
48+
* @returns True if value is "true", false if "false", undefined if unset
4949
*/
50-
export function getBooleanInput(name: string): boolean {
51-
return ghGetInput(name) === "true";
50+
export function getBooleanInput(name: string): boolean | undefined {
51+
const inputString = ghGetInput(name).toLowerCase();
52+
53+
if (inputString === "true") return true;
54+
if (inputString === "false") return false;
55+
return undefined;
5256
}
5357

5458
/**

src/action/main.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ async function run(): Promise<void> {
1212
access: core.getInput("access"),
1313
provenance: core.getBooleanInput("provenance"),
1414
strategy: core.getInput("strategy"),
15+
ignoreScripts: core.getBooleanInput("ignore-scripts"),
1516
dryRun: core.getBooleanInput("dry-run"),
1617
logger: core.logger,
1718
temporaryDirectory: process.env["RUNNER_TEMP"],

src/cli/__tests__/parse-cli-arguments.test.ts

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe("parseCliArguments", () => {
2828
access: undefined,
2929
provenance: undefined,
3030
strategy: undefined,
31+
ignoreScripts: undefined,
3132
dryRun: undefined,
3233
},
3334
});
@@ -46,6 +47,7 @@ describe("parseCliArguments", () => {
4647
tag: undefined,
4748
access: undefined,
4849
strategy: undefined,
50+
ignoreScripts: undefined,
4951
dryRun: undefined,
5052
},
5153
});
@@ -62,6 +64,7 @@ describe("parseCliArguments", () => {
6264
["--access", "restricted"],
6365
["--strategy", "upgrade"],
6466
["--provenance"],
67+
["--no-ignore-scripts"],
6568
["--dry-run"],
6669
["--quiet"],
6770
["--debug"],
@@ -80,6 +83,7 @@ describe("parseCliArguments", () => {
8083
access: "restricted",
8184
provenance: true,
8285
strategy: "upgrade",
86+
ignoreScripts: false,
8387
dryRun: true,
8488
},
8589
});

src/cli/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ Options:
3131
--strategy <strategy> Publish strategy, may be "all" or "upgrade".
3232
Defaults to "all", see documentation for details.
3333
34+
--ignore-scripts Ignore lifecycle scripts as a security precaution.
35+
Defaults to true.
36+
3437
--dry-run Do not actually publish anything.
3538
--quiet Only print errors.
3639
--debug Print debug logs.

src/cli/parse-cli-arguments.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const ARGUMENTS_OPTIONS = [
1111
{ name: "access", type: String },
1212
{ name: "provenance", type: Boolean },
1313
{ name: "strategy", type: String },
14+
{ name: "no-ignore-scripts", type: Boolean },
1415
{ name: "dry-run", type: Boolean },
1516
{ name: "quiet", type: Boolean },
1617
{ name: "debug", type: Boolean },
@@ -34,11 +35,19 @@ export interface ParsedArguments {
3435
* @returns A parsed object of options.
3536
*/
3637
export function parseCliArguments(argv: string[]): ParsedArguments {
37-
const { help, version, quiet, debug, ...options } = commandLineArgs(
38+
const { help, version, quiet, debug, ...optionFlags } = commandLineArgs(
3839
ARGUMENTS_OPTIONS,
3940
{ argv, camelCase: true }
4041
);
4142

43+
const options = Object.fromEntries(
44+
Object.entries(optionFlags).map(([key, value]) => {
45+
return key === "noIgnoreScripts"
46+
? ["ignoreScripts", !value]
47+
: [key, value];
48+
})
49+
);
50+
4251
return {
4352
help: Boolean(help),
4453
version: Boolean(version),

0 commit comments

Comments
 (0)