From b7c6f3069d6353801666e0981f10f4e3d4bd636a Mon Sep 17 00:00:00 2001 From: Miles Bardon <milesbardon@gmail.com> Date: Mon, 13 Sep 2021 21:58:40 +0100 Subject: [PATCH 1/7] Adding new option and parsing --- src/action/index.ts | 25 +++++++++++++++---------- src/normalize-options.ts | 13 +++++++++++-- src/options.ts | 8 ++++++++ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/action/index.ts b/src/action/index.ts index d683a2d..4383fc0 100644 --- a/src/action/index.ts +++ b/src/action/index.ts @@ -17,10 +17,12 @@ async function main(): Promise<void> { token: getInput("token", { required: true }), registry: getInput("registry", { required: true }), package: getInput("package", { required: true }), - checkVersion: getInput("check-version", { required: true }).toLowerCase() === "true", + checkVersion: + getInput("check-version", { required: true }).toLowerCase() === "true", tag: getInput("tag"), access: getInput("access") as Access, dryRun: getInput("dry-run").toLowerCase() === "true", + greaterVersion: getInput("greater-version").toLowerCase() === "true", debug: debugHandler, }; @@ -28,13 +30,17 @@ async function main(): Promise<void> { let results = await npmPublish(options); if (results.type === "none") { - console.log(`\n📦 ${results.package} v${results.version} is already published to NPM`); - } - else if (results.dryRun) { - console.log(`\n📦 ${results.package} v${results.version} was NOT actually published to NPM (dry run)`); - } - else { - console.log(`\n📦 Successfully published ${results.package} v${results.version} to NPM`); + console.log( + `\n📦 ${results.package} v${results.version} is already published to NPM` + ); + } else if (results.dryRun) { + console.log( + `\n📦 ${results.package} v${results.version} was NOT actually published to NPM (dry run)` + ); + } else { + console.log( + `\n📦 Successfully published ${results.package} v${results.version} to NPM` + ); } // Set the GitHub Actions output variables @@ -44,8 +50,7 @@ async function main(): Promise<void> { setOutput("tag", results.tag); setOutput("access", results.access); setOutput("dry-run", results.dryRun); - } - catch (error) { + } catch (error) { errorHandler(error as Error); } } diff --git a/src/normalize-options.ts b/src/normalize-options.ts index bedd73e..f372741 100644 --- a/src/normalize-options.ts +++ b/src/normalize-options.ts @@ -13,6 +13,7 @@ export interface NormalizedOptions { access?: Access; dryRun: boolean; checkVersion: boolean; + greaterVersion: boolean; quiet: boolean; debug: Debug; } @@ -22,7 +23,10 @@ export interface NormalizedOptions { * @internal */ export function normalizeOptions(options: Options): NormalizedOptions { - let registryURL = typeof options.registry === "string" ? new URL(options.registry) : options.registry; + let registryURL = + typeof options.registry === "string" + ? new URL(options.registry) + : options.registry; return { token: options.token || "", @@ -31,7 +35,12 @@ export function normalizeOptions(options: Options): NormalizedOptions { tag: options.tag || "latest", access: options.access, dryRun: options.dryRun || false, - checkVersion: options.checkVersion === undefined ? true : Boolean(options.checkVersion), + checkVersion: + options.checkVersion === undefined ? true : Boolean(options.checkVersion), + greaterVersion: + options.greaterVersion === undefined + ? false + : Boolean(options.greaterVersion), quiet: options.quiet || false, debug: options.debug || (() => undefined), }; diff --git a/src/options.ts b/src/options.ts index fee3190..9c645ed 100644 --- a/src/options.ts +++ b/src/options.ts @@ -64,6 +64,14 @@ export interface Options { */ quiet?: boolean; + /** + * Only publish the package if the version number in package.json + * is greater than the latest on NPM. + * + * Defaults to `false` + */ + greaterVersion?: boolean; + /** * A function to call to log debug messages. * From 265fdaf55032877fe2a5ab87bf4cd66fdb746674 Mon Sep 17 00:00:00 2001 From: Miles Bardon <milesbardon@gmail.com> Date: Mon, 13 Sep 2021 22:01:47 +0100 Subject: [PATCH 2/7] Fixing lint issues and adding existing functionality test --- src/action/index.ts | 10 +- test/specs/action/success.spec.js | 264 ++++++++++++++++++++++-------- 2 files changed, 201 insertions(+), 73 deletions(-) diff --git a/src/action/index.ts b/src/action/index.ts index 4383fc0..519d5f4 100644 --- a/src/action/index.ts +++ b/src/action/index.ts @@ -4,6 +4,7 @@ import { Access, Options } from "../options"; /** * The main entry point of the GitHub Action + * * @internal */ async function main(): Promise<void> { @@ -33,11 +34,13 @@ async function main(): Promise<void> { console.log( `\n📦 ${results.package} v${results.version} is already published to NPM` ); - } else if (results.dryRun) { + } + else if (results.dryRun) { console.log( `\n📦 ${results.package} v${results.version} was NOT actually published to NPM (dry run)` ); - } else { + } + else { console.log( `\n📦 Successfully published ${results.package} v${results.version} to NPM` ); @@ -50,7 +53,8 @@ async function main(): Promise<void> { setOutput("tag", results.tag); setOutput("access", results.access); setOutput("dry-run", results.dryRun); - } catch (error) { + } + catch (error) { errorHandler(error as Error); } } diff --git a/test/specs/action/success.spec.js b/test/specs/action/success.spec.js index 6c6e89f..04bc294 100644 --- a/test/specs/action/success.spec.js +++ b/test/specs/action/success.spec.js @@ -9,10 +9,12 @@ const { EOL } = require("os"); const { join } = require("path"); describe("GitHub Action - success tests", () => { - it("should publish a new version to NPM", () => { files.create([ - { path: "workspace/package.json", contents: { name: "my-lib", version: "2.0.0" }}, + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "2.0.0" }, + }, ]); npm.mock({ @@ -39,12 +41,14 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - } + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("my-lib 2.0.0"); - expect(cli).stdout.to.include("Successfully published my-lib v2.0.0 to NPM"); + expect(cli).stdout.to.include( + "Successfully published my-lib v2.0.0 to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::major"); expect(cli).stdout.to.include("::set-output name=version::2.0.0"); expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); @@ -53,9 +57,67 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", + `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + + `registry=https://registry.npmjs.org/${EOL}` + ); + + npm.assert.ran(4); + }); + + it("should publish a new version to NPM if the version is lower", () => { + files.create([ + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "0.1.0" }, + }, + ]); + + npm.mock({ + args: ["config", "get", "userconfig"], + stdout: `${paths.npmrc}${EOL}`, + }); + + npm.mock({ + args: ["view", "my-lib", "version"], + stdout: `1.0.0${EOL}`, + }); + + npm.mock({ + args: ["config", "get", "userconfig"], + stdout: `${paths.npmrc}${EOL}`, + }); + + npm.mock({ + args: ["publish"], + env: { INPUT_TOKEN: "my-secret-token" }, + stdout: `my-lib 0.1.0${EOL}`, + }); + + let cli = exec.action({ + env: { + INPUT_TOKEN: "my-secret-token", + }, + }); + + expect(cli).to.have.stderr(""); + expect(cli).stdout.to.include("my-lib 0.1.0"); + expect(cli).stdout.to.include( + "Successfully published my-lib v0.1.0 to NPM" + ); + expect(cli).stdout.to.include("::set-output name=type::major"); + expect(cli).stdout.to.include("::set-output name=version::0.1.0"); + expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); + expect(cli).stdout.to.include("::set-output name=tag::latest"); + expect(cli).stdout.to.include("::set-output name=access::public"); + expect(cli).stdout.to.include("::set-output name=dry-run::false"); + expect(cli).to.have.exitCode(0); + + files.assert.contents( + "home/.npmrc", `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -63,7 +125,10 @@ describe("GitHub Action - success tests", () => { it("should publish a new version to NPM if no package exists", async () => { files.create([ - { path: "workspace/package.json", contents: { name: "my-lib", version: "1.0.0" }}, + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "1.0.0" }, + }, ]); npm.mock({ @@ -91,12 +156,14 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - } + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("my-lib 1.0.0"); - expect(cli).stdout.to.include("Successfully published my-lib v1.0.0 to NPM"); + expect(cli).stdout.to.include( + "Successfully published my-lib v1.0.0 to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::major"); expect(cli).stdout.to.include("::set-output name=version::1.0.0"); expect(cli).stdout.to.include("::set-output name=old-version::0.0.0"); @@ -105,9 +172,10 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -115,7 +183,10 @@ describe("GitHub Action - success tests", () => { it("should publish a new version to NPM if the tag does not exist", async () => { files.create([ - { path: "workspace/package.json", contents: { name: "my-lib", version: "1.0.0" }}, + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "1.0.0" }, + }, ]); npm.mock({ @@ -142,12 +213,14 @@ describe("GitHub Action - success tests", () => { env: { INPUT_TOKEN: "my-secret-token", INPUT_TAG: "my-tag", - } + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("my-lib 1.0.0"); - expect(cli).stdout.to.include("Successfully published my-lib v1.0.0 to NPM"); + expect(cli).stdout.to.include( + "Successfully published my-lib v1.0.0 to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::major"); expect(cli).stdout.to.include("::set-output name=version::1.0.0"); expect(cli).stdout.to.include("::set-output name=old-version::0.0.0"); @@ -156,9 +229,10 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -166,7 +240,10 @@ describe("GitHub Action - success tests", () => { it("should not publish a new version to NPM if the version number hasn't changed", () => { files.create([ - { path: "workspace/package.json", contents: { name: "my-lib", version: "1.0.0" }}, + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "1.0.0" }, + }, ]); npm.mock({ @@ -182,11 +259,13 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - } + }, }); expect(cli).to.have.stderr(""); - expect(cli).stdout.to.include("📦 my-lib v1.0.0 is already published to NPM"); + expect(cli).stdout.to.include( + "📦 my-lib v1.0.0 is already published to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::none"); expect(cli).stdout.to.include("::set-output name=version::1.0.0"); expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); @@ -195,9 +274,10 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(2); @@ -205,8 +285,15 @@ describe("GitHub Action - success tests", () => { it("should append to an existing .npmrc file", () => { files.create([ - { path: "workspace/package.json", contents: { name: "my-lib", version: "1.1.0" }}, - { path: "home/.npmrc", contents: "This is my NPM config.\nThere are many like it,\nbut this one is mine." }, + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "1.1.0" }, + }, + { + path: "home/.npmrc", + contents: + "This is my NPM config.\nThere are many like it,\nbut this one is mine.", + }, ]); npm.mock({ @@ -233,12 +320,14 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - } + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("my-lib 1.1.0"); - expect(cli).stdout.to.include("📦 Successfully published my-lib v1.1.0 to NPM"); + expect(cli).stdout.to.include( + "📦 Successfully published my-lib v1.1.0 to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::minor"); expect(cli).stdout.to.include("::set-output name=version::1.1.0"); expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); @@ -247,13 +336,14 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `This is my NPM config.${EOL}` + - `There are many like it,${EOL}` + - `but this one is mine.${EOL}` + - `${EOL}` + - `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `There are many like it,${EOL}` + + `but this one is mine.${EOL}` + + `${EOL}` + + `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -261,7 +351,10 @@ describe("GitHub Action - success tests", () => { it("should update an existing .npmrc file's settings", () => { files.create([ - { path: "workspace/package.json", contents: { name: "my-lib", version: "1.0.1" }}, + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "1.0.1" }, + }, { path: "home/.npmrc", contents: @@ -273,7 +366,7 @@ describe("GitHub Action - success tests", () => { "registry=https://registry.npmjs.org/\n" + "\n" + "# Use some other package registry\n" + - "registry=https://registry.example.com/\n" + "registry=https://registry.example.com/\n", }, ]); @@ -301,12 +394,14 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - } + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("my-lib 1.0.1"); - expect(cli).stdout.to.include("📦 Successfully published my-lib v1.0.1 to NPM"); + expect(cli).stdout.to.include( + "📦 Successfully published my-lib v1.0.1 to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::patch"); expect(cli).stdout.to.include("::set-output name=version::1.0.1"); expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); @@ -315,16 +410,17 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `# Use the GitHub package registry${EOL}` + - `${EOL}` + - `# Use the NPM registry with no auth${EOL}` + - `${EOL}` + - `# Use some other package registry${EOL}` + - `${EOL}` + - `${EOL}` + - `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `${EOL}` + + `# Use the NPM registry with no auth${EOL}` + + `${EOL}` + + `# Use some other package registry${EOL}` + + `${EOL}` + + `${EOL}` + + `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -332,7 +428,10 @@ describe("GitHub Action - success tests", () => { it("should publish a package that's not in the root of the workspace directory", () => { files.create([ - { path: "workspace/subdir/my-lib/package.json", contents: { name: "my-lib", version: "1.0.0-beta" }}, + { + path: "workspace/subdir/my-lib/package.json", + contents: { name: "my-lib", version: "1.0.0-beta" }, + }, ]); npm.mock({ @@ -361,12 +460,14 @@ describe("GitHub Action - success tests", () => { env: { INPUT_TOKEN: "my-secret-token", INPUT_PACKAGE: "subdir/my-lib/package.json", - } + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("my-lib 1.0.0-beta"); - expect(cli).stdout.to.include("📦 Successfully published my-lib v1.0.0-beta to NPM"); + expect(cli).stdout.to.include( + "📦 Successfully published my-lib v1.0.0-beta to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::prerelease"); expect(cli).stdout.to.include("::set-output name=version::1.0.0-beta"); expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); @@ -375,9 +476,10 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -385,7 +487,10 @@ describe("GitHub Action - success tests", () => { it("should publish a scoped package", () => { files.create([ - { path: "workspace/package.json", contents: { name: "@my-scope/my-lib", version: "2.0.0" }}, + { + path: "workspace/package.json", + contents: { name: "@my-scope/my-lib", version: "2.0.0" }, + }, ]); npm.mock({ @@ -412,12 +517,14 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - } + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("@my-scope/my-lib 2.0.0"); - expect(cli).stdout.to.include("Successfully published @my-scope/my-lib v2.0.0 to NPM"); + expect(cli).stdout.to.include( + "Successfully published @my-scope/my-lib v2.0.0 to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::major"); expect(cli).stdout.to.include("::set-output name=version::2.0.0"); expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); @@ -426,9 +533,10 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -436,7 +544,10 @@ describe("GitHub Action - success tests", () => { it("should publish to a specific tag", () => { files.create([ - { path: "workspace/package.json", contents: { name: "my-lib", version: "2.0.0" }}, + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "2.0.0" }, + }, ]); npm.mock({ @@ -464,12 +575,14 @@ describe("GitHub Action - success tests", () => { env: { INPUT_TOKEN: "my-secret-token", INPUT_TAG: "next", - } + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("my-lib 2.0.0"); - expect(cli).stdout.to.include("Successfully published my-lib v2.0.0 to NPM"); + expect(cli).stdout.to.include( + "Successfully published my-lib v2.0.0 to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::major"); expect(cli).stdout.to.include("::set-output name=version::2.0.0"); expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); @@ -478,9 +591,10 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -488,7 +602,10 @@ describe("GitHub Action - success tests", () => { it("should publish a scoped package with public access", () => { files.create([ - { path: "workspace/package.json", contents: { name: "@my-scope/my-lib", version: "2.0.0" }}, + { + path: "workspace/package.json", + contents: { name: "@my-scope/my-lib", version: "2.0.0" }, + }, ]); npm.mock({ @@ -515,13 +632,15 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - INPUT_ACCESS: "public" - } + INPUT_ACCESS: "public", + }, }); expect(cli).to.have.stderr(""); expect(cli).stdout.to.include("@my-scope/my-lib 2.0.0"); - expect(cli).stdout.to.include("Successfully published @my-scope/my-lib v2.0.0 to NPM"); + expect(cli).stdout.to.include( + "Successfully published @my-scope/my-lib v2.0.0 to NPM" + ); expect(cli).stdout.to.include("::set-output name=type::major"); expect(cli).stdout.to.include("::set-output name=version::2.0.0"); expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); @@ -530,9 +649,10 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=dry-run::false"); expect(cli).to.have.exitCode(0); - files.assert.contents("home/.npmrc", + files.assert.contents( + "home/.npmrc", `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + - `registry=https://registry.npmjs.org/${EOL}` + `registry=https://registry.npmjs.org/${EOL}` ); npm.assert.ran(4); @@ -540,7 +660,10 @@ describe("GitHub Action - success tests", () => { it("should not publish a package if dryRun is true", () => { files.create([ - { path: "workspace/package.json", contents: { name: "my-lib", version: "1.1.0" }}, + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "1.1.0" }, + }, ]); npm.mock({ @@ -567,8 +690,8 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - "INPUT_DRY-RUN": "true" - } + "INPUT_DRY-RUN": "true", + }, }); expect(cli).to.have.stderr(""); @@ -579,10 +702,11 @@ describe("GitHub Action - success tests", () => { expect(cli).stdout.to.include("::set-output name=access::public"); expect(cli).stdout.to.include("::set-output name=dry-run::true"); expect(cli).stdout.to.include("my-lib 1.1.0"); - expect(cli).stdout.to.include("📦 my-lib v1.1.0 was NOT actually published to NPM (dry run)"); + expect(cli).stdout.to.include( + "📦 my-lib v1.1.0 was NOT actually published to NPM (dry run)" + ); expect(cli).to.have.exitCode(0); npm.assert.ran(4); }); - }); From 63f31bcb99b8a253fe2502f385496323dd24084a Mon Sep 17 00:00:00 2001 From: Miles Bardon <milesbardon@gmail.com> Date: Mon, 13 Sep 2021 22:35:32 +0100 Subject: [PATCH 3/7] Adding comparison check for decrementing version --- src/action/index.ts | 5 +++ src/npm-publish.ts | 20 ++++++++--- src/results.ts | 2 +- test/specs/action/success.spec.js | 58 +++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/action/index.ts b/src/action/index.ts index 519d5f4..87c5a04 100644 --- a/src/action/index.ts +++ b/src/action/index.ts @@ -35,6 +35,11 @@ async function main(): Promise<void> { `\n📦 ${results.package} v${results.version} is already published to NPM` ); } + else if (options.greaterVersion && results.type === "lower") { + console.log( + `\n📦 ${results.package} v${results.version} is lower than the version published to NPM` + ); + } else if (results.dryRun) { console.log( `\n📦 ${results.package} v${results.version} was NOT actually published to NPM (dry run)` diff --git a/src/npm-publish.ts b/src/npm-publish.ts index 8d8bfb5..c492243 100644 --- a/src/npm-publish.ts +++ b/src/npm-publish.ts @@ -18,19 +18,31 @@ export async function npmPublish(opts: Options = {}): Promise<Results> { // Determine if/how the version has changed let diff = semver.diff(manifest.version, publishedVersion); - if (diff || !options.checkVersion) { + // Compare both versions to see if it's changed + let cmp = semver.compare(manifest.version, publishedVersion); + + let shouldPublish = + !options.checkVersion || + // compare returns 1 if manifest is higher than published + (options.greaterVersion && cmp === 1) || + // compare returns 0 if the manifest is the same as published + cmp !== 0; + + if (shouldPublish) { // Publish the new version to NPM await npm.publish(manifest, options); } let results: Results = { package: manifest.name, - type: diff || "none", + type: (cmp === -1 && "lower") || diff || "none", version: manifest.version.raw, oldVersion: publishedVersion.raw, tag: options.tag, - access: options.access || (manifest.name.startsWith("@") ? "restricted" : "public"), - dryRun: options.dryRun + access: + options.access || + (manifest.name.startsWith("@") ? "restricted" : "public"), + dryRun: options.dryRun, }; options.debug("OUTPUT:", results); diff --git a/src/results.ts b/src/results.ts index 8f296df..fb9f7cb 100644 --- a/src/results.ts +++ b/src/results.ts @@ -10,7 +10,7 @@ export interface Results { /** * The type of version change that occurred */ - type: ReleaseType | "none"; + type: ReleaseType | "lower" | "none"; /** * The name of the NPM package that was published diff --git a/test/specs/action/success.spec.js b/test/specs/action/success.spec.js index 04bc294..214cde0 100644 --- a/test/specs/action/success.spec.js +++ b/test/specs/action/success.spec.js @@ -123,6 +123,64 @@ describe("GitHub Action - success tests", () => { npm.assert.ran(4); }); + it("should not publish a new version to NPM if the version is lower and greaterVersion flag is set", () => { + files.create([ + { + path: "workspace/package.json", + contents: { name: "my-lib", version: "0.1.0" }, + }, + ]); + + npm.mock({ + args: ["config", "get", "userconfig"], + stdout: `${paths.npmrc}${EOL}`, + }); + + npm.mock({ + args: ["view", "my-lib", "version"], + stdout: `1.0.0${EOL}`, + }); + + npm.mock({ + args: ["config", "get", "userconfig"], + stdout: `${paths.npmrc}${EOL}`, + }); + + npm.mock({ + args: ["publish"], + env: { INPUT_TOKEN: "my-secret-token" }, + stdout: `my-lib 0.1.0${EOL}`, + }); + + let cli = exec.action({ + env: { + INPUT_TOKEN: "my-secret-token", + "INPUT_GREATER-VERSION": "true", + }, + }); + + expect(cli).to.have.stderr(""); + expect(cli).stdout.to.include("my-lib 0.1.0"); + expect(cli).stdout.to.include( + "my-lib v0.1.0 is lower than the version published to NPM" + ); + expect(cli).stdout.to.include("::set-output name=type::lower"); + expect(cli).stdout.to.include("::set-output name=version::0.1.0"); + expect(cli).stdout.to.include("::set-output name=old-version::1.0.0"); + expect(cli).stdout.to.include("::set-output name=tag::latest"); + expect(cli).stdout.to.include("::set-output name=access::public"); + expect(cli).stdout.to.include("::set-output name=dry-run::false"); + expect(cli).to.have.exitCode(0); + + files.assert.contents( + "home/.npmrc", + `//registry.npmjs.org/:_authToken=\${INPUT_TOKEN}${EOL}` + + `registry=https://registry.npmjs.org/${EOL}` + ); + + npm.assert.ran(4); + }); + it("should publish a new version to NPM if no package exists", async () => { files.create([ { From 6a5b36028e3b40cb35a281bf4475cc6fa853e6f0 Mon Sep 17 00:00:00 2001 From: Miles Bardon <milesbardon@gmail.com> Date: Tue, 14 Sep 2021 08:36:05 +0100 Subject: [PATCH 4/7] Attempting to fix greater-version output --- src/action/index.ts | 2 +- src/npm-publish.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/action/index.ts b/src/action/index.ts index 87c5a04..cfcdb74 100644 --- a/src/action/index.ts +++ b/src/action/index.ts @@ -35,7 +35,7 @@ async function main(): Promise<void> { `\n📦 ${results.package} v${results.version} is already published to NPM` ); } - else if (options.greaterVersion && results.type === "lower") { + if (results.type === "lower") { console.log( `\n📦 ${results.package} v${results.version} is lower than the version published to NPM` ); diff --git a/src/npm-publish.ts b/src/npm-publish.ts index c492243..7dcf0cf 100644 --- a/src/npm-publish.ts +++ b/src/npm-publish.ts @@ -35,7 +35,7 @@ export async function npmPublish(opts: Options = {}): Promise<Results> { let results: Results = { package: manifest.name, - type: (cmp === -1 && "lower") || diff || "none", + type: (options.greaterVersion && cmp === -1 && "lower") || diff || "none", version: manifest.version.raw, oldVersion: publishedVersion.raw, tag: options.tag, From 3f54312689121b90654d91a5838d572fc6b1a610 Mon Sep 17 00:00:00 2001 From: Miles Bardon <milesbardon@gmail.com> Date: Tue, 14 Sep 2021 08:52:01 +0100 Subject: [PATCH 5/7] Tidying up tests --- src/npm-publish.ts | 6 +++++- test/specs/action/success.spec.js | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/npm-publish.ts b/src/npm-publish.ts index 7dcf0cf..5b5f9a2 100644 --- a/src/npm-publish.ts +++ b/src/npm-publish.ts @@ -33,9 +33,13 @@ export async function npmPublish(opts: Options = {}): Promise<Results> { await npm.publish(manifest, options); } + // The version should be marked as lower if we disallow decrementing the version + let type = + (options.greaterVersion && cmp === -1 && "lower") || diff || "none"; + let results: Results = { package: manifest.name, - type: (options.greaterVersion && cmp === -1 && "lower") || diff || "none", + type, version: manifest.version.raw, oldVersion: publishedVersion.raw, tag: options.tag, diff --git a/test/specs/action/success.spec.js b/test/specs/action/success.spec.js index 214cde0..41ddbf1 100644 --- a/test/specs/action/success.spec.js +++ b/test/specs/action/success.spec.js @@ -160,7 +160,6 @@ describe("GitHub Action - success tests", () => { }); expect(cli).to.have.stderr(""); - expect(cli).stdout.to.include("my-lib 0.1.0"); expect(cli).stdout.to.include( "my-lib v0.1.0 is lower than the version published to NPM" ); From 18930aa589ee6b5fc2c7959c9eed5009e04b901d Mon Sep 17 00:00:00 2001 From: Miles Bardon <milesbardon@gmail.com> Date: Tue, 14 Sep 2021 09:00:39 +0100 Subject: [PATCH 6/7] Changing flag to be clearer --- src/action/index.ts | 2 +- src/normalize-options.ts | 8 ++++---- src/npm-publish.ts | 10 ++++------ src/options.ts | 2 +- test/specs/action/success.spec.js | 2 +- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/action/index.ts b/src/action/index.ts index cfcdb74..92f0195 100644 --- a/src/action/index.ts +++ b/src/action/index.ts @@ -23,7 +23,7 @@ async function main(): Promise<void> { tag: getInput("tag"), access: getInput("access") as Access, dryRun: getInput("dry-run").toLowerCase() === "true", - greaterVersion: getInput("greater-version").toLowerCase() === "true", + greaterVersionOnly: getInput("greater-version-only").toLowerCase() === "true", debug: debugHandler, }; diff --git a/src/normalize-options.ts b/src/normalize-options.ts index f372741..23cffaa 100644 --- a/src/normalize-options.ts +++ b/src/normalize-options.ts @@ -13,7 +13,7 @@ export interface NormalizedOptions { access?: Access; dryRun: boolean; checkVersion: boolean; - greaterVersion: boolean; + greaterVersionOnly: boolean; quiet: boolean; debug: Debug; } @@ -37,10 +37,10 @@ export function normalizeOptions(options: Options): NormalizedOptions { dryRun: options.dryRun || false, checkVersion: options.checkVersion === undefined ? true : Boolean(options.checkVersion), - greaterVersion: - options.greaterVersion === undefined + greaterVersionOnly: + options.greaterVersionOnly === undefined ? false - : Boolean(options.greaterVersion), + : Boolean(options.greaterVersionOnly), quiet: options.quiet || false, debug: options.debug || (() => undefined), }; diff --git a/src/npm-publish.ts b/src/npm-publish.ts index 5b5f9a2..bedf07e 100644 --- a/src/npm-publish.ts +++ b/src/npm-publish.ts @@ -24,7 +24,7 @@ export async function npmPublish(opts: Options = {}): Promise<Results> { let shouldPublish = !options.checkVersion || // compare returns 1 if manifest is higher than published - (options.greaterVersion && cmp === 1) || + (options.greaterVersionOnly && cmp === 1) || // compare returns 0 if the manifest is the same as published cmp !== 0; @@ -33,13 +33,11 @@ export async function npmPublish(opts: Options = {}): Promise<Results> { await npm.publish(manifest, options); } - // The version should be marked as lower if we disallow decrementing the version - let type = - (options.greaterVersion && cmp === -1 && "lower") || diff || "none"; - let results: Results = { package: manifest.name, - type, + // The version should be marked as lower if we disallow decrementing the version + type: + (options.greaterVersionOnly && cmp === -1 && "lower") || diff || "none", version: manifest.version.raw, oldVersion: publishedVersion.raw, tag: options.tag, diff --git a/src/options.ts b/src/options.ts index 9c645ed..1ed4ad8 100644 --- a/src/options.ts +++ b/src/options.ts @@ -70,7 +70,7 @@ export interface Options { * * Defaults to `false` */ - greaterVersion?: boolean; + greaterVersionOnly?: boolean; /** * A function to call to log debug messages. diff --git a/test/specs/action/success.spec.js b/test/specs/action/success.spec.js index 41ddbf1..9df0c81 100644 --- a/test/specs/action/success.spec.js +++ b/test/specs/action/success.spec.js @@ -155,7 +155,7 @@ describe("GitHub Action - success tests", () => { let cli = exec.action({ env: { INPUT_TOKEN: "my-secret-token", - "INPUT_GREATER-VERSION": "true", + "INPUT_GREATER-VERSION-ONLY": "true", }, }); From e738dd850f69cec653b4d441fbd646bfafdfc520 Mon Sep 17 00:00:00 2001 From: Miles Bardon <milesbardon@gmail.com> Date: Tue, 14 Sep 2021 09:04:02 +0100 Subject: [PATCH 7/7] Updating README --- README.md | 154 ++++++++++++++++++++++++------------------------------ 1 file changed, 69 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index 94477fe..80dabf2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Fast, easy publishing to NPM -============================================== +# Fast, easy publishing to NPM [](https://github.com/JS-DevTools/npm-publish/actions) [](https://github.com/JS-DevTools/npm-publish/actions) @@ -11,29 +10,25 @@ Fast, easy publishing to NPM [](LICENSE) [](https://plant.treeware.earth/JS-DevTools/npm-publish) +## Features - -Features --------------------------- - 🧠 **Smart**<br> -Only publishes if the version number in `package.json` differs from the latest on NPM + Only publishes if the version number in `package.json` differs from the latest on NPM - 🛠 **Configurable**<br> -Customize the version-checking behavior, the registry URL, and path of your package + Customize the version-checking behavior, the registry URL, and path of your package - 🔐 **Secure**<br> -Keeps your NPM access token secret. Doesn't write it to `~/.npmrc` + Keeps your NPM access token secret. Doesn't write it to `~/.npmrc` - ⚡ **Fast**<br> -100% JavaScript (which is faster than Docker) and bundled to optimize loading time + 100% JavaScript (which is faster than Docker) and bundled to optimize loading time - 📤 **Outputs**<br> -Exposes the old and new version numbers, and the type of change (major, minor, patch, etc.) as variables that you can use in your workflow. - + Exposes the old and new version numbers, and the type of change (major, minor, patch, etc.) as variables that you can use in your workflow. +## Usage -Usage --------------------------- This package can be used three different ways: - 🤖 A [**GitHub Action**](#github-action) as part of your CI/CD process @@ -42,10 +37,8 @@ This package can be used three different ways: - 🖥 A [**CLI**](#command-line-interface) that you run in your terminal +## GitHub Action - -GitHub Action ------------------------------ To use the GitHub Action, you'll need to add it as a step in your [Workflow file](https://help.github.com/en/actions/automating-your-workflow-with-github-actions). By default, the only thing you need to do is set the `token` parameter to your [NPM auth token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens). ```yaml @@ -66,26 +59,23 @@ jobs: token: ${{ secrets.NPM_TOKEN }} ``` +## Input Parameters - -Input Parameters --------------------------- You can set any or all of the following input parameters: -|Name |Type |Required? |Default |Description -|----------------|--------|----------|----------------------------|------------------------------------ -|`token` |string |yes | |The NPM auth token to use for publishing -|`registry` |string |no |https://registry.npmjs.org/ |The NPM registry URL to use -|`package` |string |no |./package.json |The path of your package.json file -|`tag` |string |no |"latest" |The tag to publish to. This allows people to install the package using `npm install <package-name>@<tag>`. -|`access` |string |no |"public" for non-scoped packages. "restricted" for scoped packages.|Determines whether the published package should be publicly visible, or restricted to members of your NPM organization. -|`dry-run` |boolean |no |false |Run NPM publish with the `--dry-run` flag to prevent publication -|`check-version` |boolean |no |true |Only publish to NPM if the version number in `package.json` differs from the latest on NPM +| Name | Type | Required? | Default | Description | +| ---------------------- | ------- | --------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| `token` | string | yes | | The NPM auth token to use for publishing | +| `registry` | string | no | https://registry.npmjs.org/ | The NPM registry URL to use | +| `package` | string | no | ./package.json | The path of your package.json file | +| `tag` | string | no | "latest" | The tag to publish to. This allows people to install the package using `npm install <package-name>@<tag>`. | +| `access` | string | no | "public" for non-scoped packages. "restricted" for scoped packages. | Determines whether the published package should be publicly visible, or restricted to members of your NPM organization. | +| `dry-run` | boolean | no | false | Run NPM publish with the `--dry-run` flag to prevent publication | +| `check-version` | boolean | no | true | Only publish to NPM if the version number in `package.json` differs from the latest on NPM | +| `greater-version-only` | boolean | no | false | Only publish to NPM if the version number in `package.json` is greater than the latest on NPM | +## Output Variables - -Output Variables --------------------------- npm-publish exposes some output variables, which you can use in later steps of your workflow. To access the output variables, you'll need to set an `id` for the npm-publish step. ```yaml @@ -100,20 +90,17 @@ steps: echo "Version changed: ${{ steps.publish.outputs.old-version }} => ${{ steps.publish.outputs.version }}" ``` +| Variable | Type | Description | +| ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `type` | string | The type of version change that occurred ("major", "minor", "patch", etc.). If there was no version change, then type will be "none". If `greater-version-only` is set and the version is lower, then type will be "lower". | +| `version` | string | The version that was published | +| `old-version` | string | The version number that was previously published to NPM | +| `tag` | string | The tag that the package was published to. | +| `access` | string | Indicates whether the published package is publicly visible or restricted to members of your NPM organization. | +| `dry-run` | boolean | Indicates whether NPM was run in "dry run" mode | -|Variable |Type |Description -|--------------|--------|------------------------------------ -|`type` |string |The type of version change that occurred ("major", "minor", "patch", etc.). If there was no version change, then type will be "none". -|`version` |string |The version that was published -|`old-version` |string |The version number that was previously published to NPM -|`tag` |string |The tag that the package was published to. -|`access` |string |Indicates whether the published package is publicly visible or restricted to members of your NPM organization. -|`dry-run` |boolean |Indicates whether NPM was run in "dry run" mode - +## JavaScript Function - -JavaScript Function ------------------------------- To use npm-package in your JavaScript code, you'll need to install it using [NPM](https://docs.npmjs.com/about-npm/): ```bash @@ -131,42 +118,43 @@ await npmPublish(); // Run npm-publish with options await npmPublish({ package: "./path/to/package.json", - token: "YOUR_NPM_AUTH_TOKEN_HERE" + token: "YOUR_NPM_AUTH_TOKEN_HERE", }); ``` ### Options + As shown in the example above, you can pass options to the `npmPublish()` function. Here are the available options: -|Name |Type |Default |Description -|----------------|---------|----------------------------|------------------------------------ -|`token` |string |NPM's default credentials |The NPM auth token to use for publishing. If not set, then NPM will -|`registry` |string |https://registry.npmjs.org/ |The NPM registry URL to use -|`package` |string |./package.json |The path of your package.json file -|`tag` |string |"latest" |The tag to publish to. This allows people to install the package using `npm install <package-name>@<tag>`. -|`access` |string |"public" for non-scoped packages. "restricted" for scoped packages.|Determines whether the published package should be publicly visible, or restricted to members of your NPM organization. -|`dryRun` |boolean |false |Run NPM publish with the `--dry-run` flag to prevent publication -|`checkVersion` |boolean |true |Only publish to NPM if the version number in `package.json` differs from the latest on NPM -|`quiet` |boolean |false |Suppress console output from NPM and npm-publish -|`debug` |function |no-op |A function to log debug messages. You can set this to a custom function to receive debug messages, or just set it to `console.debug` to print debug messages to the console. +| Name | Type | Default | Description | +| -------------------- | -------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `token` | string | NPM's default credentials | The NPM auth token to use for publishing. If not set, then NPM will | +| `registry` | string | https://registry.npmjs.org/ | The NPM registry URL to use | +| `package` | string | ./package.json | The path of your package.json file | +| `tag` | string | "latest" | The tag to publish to. This allows people to install the package using `npm install <package-name>@<tag>`. | +| `access` | string | "public" for non-scoped packages. "restricted" for scoped packages. | Determines whether the published package should be publicly visible, or restricted to members of your NPM organization. | +| `dryRun` | boolean | false | Run NPM publish with the `--dry-run` flag to prevent publication | +| `checkVersion` | boolean | true | Only publish to NPM if the version number in `package.json` differs from the latest on NPM | +| `greaterVersionOnly` | boolean | false | Only publish to NPM if the version number in `package.json` is greater then the latest on NPM | +| `quiet` | boolean | false | Suppress console output from NPM and npm-publish | +| `debug` | function | no-op | A function to log debug messages. You can set this to a custom function to receive debug messages, or just set it to `console.debug` to print debug messages to the console. | ### Return Value -The `npmPublish()` function asynchronously returns an object with the following properties: -|Name |Type |Description -|----------------|---------|------------------------------------ -|`type` |string |The type of version change that occurred ("major", "minor", "patch", etc.) If there was no version change, then the the type is "none". -|`package` |string |The name of the NPM package that was published -|`version` |string |The version number that was published -|`oldVersion` |string |The version number that was previously published to NPM -|`tag` |string |The tag that the package was published to. -|`access` |string |Indicates whether the published package is publicly visible or restricted to members of your NPM organization. -|`dryRun` |boolean |Indicates whether NPM was run in "dry run" mode +The `npmPublish()` function asynchronously returns an object with the following properties: +| Name | Type | Description | +| ------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `type` | string | The type of version change that occurred ("major", "minor", "patch", etc.) If there was no version change, then the the type is "none". If `greater-version-only` is set and the version is lower, then type will be "lower". | +| `package` | string | The name of the NPM package that was published | +| `version` | string | The version number that was published | +| `oldVersion` | string | The version number that was previously published to NPM | +| `tag` | string | The tag that the package was published to. | +| `access` | string | Indicates whether the published package is publicly visible or restricted to members of your NPM organization. | +| `dryRun` | boolean | Indicates whether NPM was run in "dry run" mode | +## Command Line Interface -Command Line Interface ------------------------------- To use npm-package from as a command-line tool in your terminal, you'll need to install it globally using [NPM](https://docs.npmjs.com/about-npm/): ```bash @@ -186,6 +174,7 @@ npm-publish --token=YOUR_NPM_AUTH_TOKEN_HERE ./path/to/package.json ``` ### Options + Run `npm-publish --help` to see the full list of options available. ``` @@ -221,39 +210,34 @@ package_path The absolute or relative path of the NPM package to publis Defaults to the current directory. ``` +## Contributing - -Contributing --------------------------- -Contributions, enhancements, and bug-fixes are welcome! [Open an issue](https://github.com/JS-DevTools/npm-publish/issues) on GitHub and [submit a pull request](https://github.com/JS-DevTools/npm-publish/pulls). +Contributions, enhancements, and bug-fixes are welcome! [Open an issue](https://github.com/JS-DevTools/npm-publish/issues) on GitHub and [submit a pull request](https://github.com/JS-DevTools/npm-publish/pulls). #### Building -To build the project locally on your computer: -1. __Clone this repo__<br> -`git clone https://github.com/JS-DevTools/npm-publish.git` +To build the project locally on your computer: -2. __Install dependencies__<br> -`npm install` +1. **Clone this repo**<br> + `git clone https://github.com/JS-DevTools/npm-publish.git` -3. __Build the code__<br> -`npm run build` +2. **Install dependencies**<br> + `npm install` -4. __Run the tests__<br> -`npm test` +3. **Build the code**<br> + `npm run build` +4. **Run the tests**<br> + `npm test` +## License -License --------------------------- npm-publish is 100% free and open-source, under the [MIT license](LICENSE). Use it however you want. This package is [Treeware](http://treeware.earth). If you use it in production, then we ask that you [**buy the world a tree**](https://plant.treeware.earth/JS-DevTools/npm-publish) to thank us for our work. By contributing to the Treeware forest you’ll be creating employment for local families and restoring wildlife habitats. +## Big Thanks To - -Big Thanks To --------------------------- Thanks to these awesome companies for their support of Open Source developers ❤ [](https://github.com/open-source)