Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow --ignore-scripts to be disabled, if needed #102

Merged
merged 2 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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