Skip to content

Commit

Permalink
feat: allow --ignore-scripts to be disabled, if needed (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
mcous authored Jun 2, 2023
1 parent 569541e commit cb083ce
Show file tree
Hide file tree
Showing 18 changed files with 228 additions and 60 deletions.
72 changes: 58 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,45 @@ This package can be used three different ways:

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

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

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

## v2 Migration Guide

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

- The `type` output is now an empty string instead of `none` when no release occurs
- The `type` output is now an empty string instead of `'none'` when no release occurs
```diff
- run: echo "Version changed!"
- if: ${{ steps.publish.outputs.type != 'none' }}
+ if: ${{ steps.publish.outputs.type }}
```
- 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`.
```diff
with:
token: ${{ secrets.NPM_TOKEN }}
+ ignore-scripts: false
```
- 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.

```diff
- uses: actions/setup-node@v3
with:
node-version: '18'
registry-url: https://registry.npmjs.org/

- uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}

- name: Do some more stuff with npm
run: npm whoami
env:
- INPUT_TOKEN: ${{ secrets.NPM_TOKEN }}
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```

- The `check-version` and `greater-version-only` options have been removed and replaced with `strategy`.
- Use `strategy: all` (default) to publish all versions that do not yet exist in the registry.
```diff
Expand All @@ -62,10 +87,24 @@ The v1 to v2 upgrade brought a few notable **breaking changes**. To migrate, mak
- greater-version-only: true
+ strategy: upgrade
```
- `check-version: false` has been removed. You don't need this action if you're not checking already published versions; use `npm` directly, instead.
- `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.
```diff
- - uses: JS-DevTools/npm-publish@v1
- with:
- token: ${{ secrets.NPM_TOKEN }}
- check-version: false
+ - uses: actions/setup-node@v3
+ with:
+ node-version: '18'
+ registry-url: https://registry.npmjs.org/
+ - run: npm publish
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```

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

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

## GitHub Action
Expand Down Expand Up @@ -99,16 +138,17 @@ jobs:

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

| Name | Type | Default | Description |
| --------------- | ---------------------- | ----------------------------- | -------------------------------------------------------------------------------- |
| `token` | string | **required** | Authentication token to use with the configured registry. |
| `registry`¹ | string | `https://registry.npmjs.org/` | Registry URL to use. |
| `package` | string | Current working directory | Path to a package directory, a `package.json`, or a packed `.tgz` to publish. |
| `tag`¹ | string | `latest` | [Distribution tag][npm-tag] to publish to. |
| `access`¹ | `public`, `restricted` | [npm defaults][npm-access] | Whether the package should be publicly visible or restricted. |
| `provenance`¹ ² | boolean | `false` | Run `npm publish` with the `--provenance` flag to add [provenance][] statements. |
| `strategy` | `all`, `upgrade` | `all` | Use `all` to publish all unique versions, `upgrade` for only semver upgrades. |
| `dry-run` | boolean | `false` | Run `npm publish` with the `--dry-run` flag to prevent publication. |
| Name | Type | Default | Description |
| ---------------- | ---------------------- | ----------------------------- | -------------------------------------------------------------------------------- |
| `token` | string | **required** | Authentication token to use with the configured registry. |
| `registry`¹ | string | `https://registry.npmjs.org/` | Registry URL to use. |
| `package` | string | Current working directory | Path to a package directory, a `package.json`, or a packed `.tgz` to publish. |
| `tag`¹ | string | `latest` | [Distribution tag][npm-tag] to publish to. |
| `access`¹ | `public`, `restricted` | [npm defaults][npm-access] | Whether the package should be publicly visible or restricted. |
| `provenance`¹ ² | boolean | `false` | Run `npm publish` with the `--provenance` flag to add [provenance][] statements. |
| `strategy` | `all`, `upgrade` | `all` | Use `all` to publish all unique versions, `upgrade` for only semver upgrades. |
| `ignore-scripts` | boolean | `true` | Run `npm publish` with the `--ignore-scripts` flag as a security precaution. |
| `dry-run` | boolean | `false` | Run `npm publish` with the `--dry-run` flag to prevent publication. |

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

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

## JavaScript Function
## JavaScript API

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

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

--no-ignore-scripts Allow lifecycle scripts, which are disabled by default
as a security precaution. Defaults to false.

--dry-run Do not actually publish anything.
--quiet Only print errors.
--debug Print debug logs.
Expand Down
6 changes: 6 additions & 0 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ inputs:
required: false

ignore-scripts:
description: >
Run npm with the --ignore-scripts flag as a security precaution.
Enabled by default.
required: false

dry-run:
description: Run npm with the --dry-run flag to avoid actually publishing anything.
required: false
Expand Down
18 changes: 15 additions & 3 deletions dist/main.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dist/main.js.map

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/__tests__/normalize-options.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ describe("normalizeOptions", () => {
tag: { value: "latest", isDefault: true },
access: { value: "public", isDefault: true },
provenance: { value: false, isDefault: true },
ignoreScripts: { value: true, isDefault: true },
dryRun: { value: false, isDefault: true },
strategy: { value: "all", isDefault: true },
});
Expand All @@ -100,6 +101,7 @@ describe("normalizeOptions", () => {
tag: "next",
access: "public",
provenance: true,
ignoreScripts: false,
dryRun: true,
strategy: "all",
}
Expand All @@ -109,6 +111,7 @@ describe("normalizeOptions", () => {
tag: { value: "next", isDefault: false },
access: { value: "public", isDefault: false },
provenance: { value: true, isDefault: false },
ignoreScripts: { value: false, isDefault: false },
dryRun: { value: true, isDefault: false },
strategy: { value: "all", isDefault: false },
});
Expand Down
28 changes: 11 additions & 17 deletions src/action/__tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,25 @@ vi.mock("../core", () => imitateEsm("../core"));
describe("run", () => {
beforeEach(() => {
vi.stubEnv("RUNNER_TEMP", "/path/to/temp");
});

afterEach(() => {
vi.unstubAllEnvs();
vi.resetModules();
reset();
});

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

afterEach(() => {
vi.unstubAllEnvs();
vi.resetModules();
reset();
});

it("should pass input to options", async () => {
td.when(
npmPublish({
token: "abc123",
Expand All @@ -38,6 +39,7 @@ describe("run", () => {
access: "restricted",
provenance: true,
strategy: "all",
ignoreScripts: false,
dryRun: true,
logger: core.logger,
temporaryDirectory: "/path/to/temp",
Expand Down Expand Up @@ -72,15 +74,6 @@ describe("run", () => {
it("should fail the action if something raises", async () => {
const error = new Error("oh no");

td.when(core.getRequiredSecretInput("token")).thenReturn("abc123");
td.when(core.getInput("package")).thenReturn("./package.json");
td.when(core.getInput("registry")).thenReturn("https://example.com");
td.when(core.getInput("tag")).thenReturn("next");
td.when(core.getInput("access")).thenReturn("restricted");
td.when(core.getBooleanInput("provenance")).thenReturn(true);
td.when(core.getInput("strategy")).thenReturn("all");
td.when(core.getBooleanInput("dry-run")).thenReturn(true);

td.when(
npmPublish({
token: "abc123",
Expand All @@ -90,6 +83,7 @@ describe("run", () => {
access: "restricted",
provenance: true,
strategy: "all",
ignoreScripts: false,
dryRun: true,
logger: core.logger,
temporaryDirectory: "/path/to/temp",
Expand Down
10 changes: 7 additions & 3 deletions src/action/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@ export function getRequiredSecretInput(name: string): string {
* Get a boolean input by name.
*
* @param name Input name
* @returns True if value is "true", false if not
* @returns True if value is "true", false if "false", undefined if unset
*/
export function getBooleanInput(name: string): boolean {
return ghGetInput(name) === "true";
export function getBooleanInput(name: string): boolean | undefined {
const inputString = ghGetInput(name).toLowerCase();

if (inputString === "true") return true;
if (inputString === "false") return false;
return undefined;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/action/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ async function run(): Promise<void> {
access: core.getInput("access"),
provenance: core.getBooleanInput("provenance"),
strategy: core.getInput("strategy"),
ignoreScripts: core.getBooleanInput("ignore-scripts"),
dryRun: core.getBooleanInput("dry-run"),
logger: core.logger,
temporaryDirectory: process.env["RUNNER_TEMP"],
Expand Down
4 changes: 4 additions & 0 deletions src/cli/__tests__/parse-cli-arguments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe("parseCliArguments", () => {
access: undefined,
provenance: undefined,
strategy: undefined,
ignoreScripts: undefined,
dryRun: undefined,
},
});
Expand All @@ -46,6 +47,7 @@ describe("parseCliArguments", () => {
tag: undefined,
access: undefined,
strategy: undefined,
ignoreScripts: undefined,
dryRun: undefined,
},
});
Expand All @@ -62,6 +64,7 @@ describe("parseCliArguments", () => {
["--access", "restricted"],
["--strategy", "upgrade"],
["--provenance"],
["--no-ignore-scripts"],
["--dry-run"],
["--quiet"],
["--debug"],
Expand All @@ -80,6 +83,7 @@ describe("parseCliArguments", () => {
access: "restricted",
provenance: true,
strategy: "upgrade",
ignoreScripts: false,
dryRun: true,
},
});
Expand Down
3 changes: 3 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ Options:
--strategy <strategy> Publish strategy, may be "all" or "upgrade".
Defaults to "all", see documentation for details.
--ignore-scripts Ignore lifecycle scripts as a security precaution.
Defaults to true.
--dry-run Do not actually publish anything.
--quiet Only print errors.
--debug Print debug logs.
Expand Down
11 changes: 10 additions & 1 deletion src/cli/parse-cli-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const ARGUMENTS_OPTIONS = [
{ name: "access", type: String },
{ name: "provenance", type: Boolean },
{ name: "strategy", type: String },
{ name: "no-ignore-scripts", type: Boolean },
{ name: "dry-run", type: Boolean },
{ name: "quiet", type: Boolean },
{ name: "debug", type: Boolean },
Expand All @@ -34,11 +35,19 @@ export interface ParsedArguments {
* @returns A parsed object of options.
*/
export function parseCliArguments(argv: string[]): ParsedArguments {
const { help, version, quiet, debug, ...options } = commandLineArgs(
const { help, version, quiet, debug, ...optionFlags } = commandLineArgs(
ARGUMENTS_OPTIONS,
{ argv, camelCase: true }
);

const options = Object.fromEntries(
Object.entries(optionFlags).map(([key, value]) => {
return key === "noIgnoreScripts"
? ["ignoreScripts", !value]
: [key, value];
})
);

return {
help: Boolean(help),
version: Boolean(version),
Expand Down
Loading

0 comments on commit cb083ce

Please sign in to comment.