From fde364e7f53445b84cdfe44e26bf2512dc4df0e2 Mon Sep 17 00:00:00 2001 From: Hamish Buckmaster Date: Tue, 25 May 2021 21:55:51 +1000 Subject: [PATCH 1/7] feat(publish/index.js): Added OutputJson Command Outputs the packages and versions from publishing to a JSON file. --- commands/publish/README.md | 22 +++++++++++++++ .../publish/__tests__/publish-command.test.js | 27 +++++++++++++++++++ commands/publish/command.js | 5 ++++ commands/publish/index.js | 24 +++++++++++++++-- 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/commands/publish/README.md b/commands/publish/README.md index f86b27c781..b5c282c6e0 100644 --- a/commands/publish/README.md +++ b/commands/publish/README.md @@ -65,6 +65,7 @@ This is useful when a previous `lerna publish` failed to publish all packages to - [`--tag-version-prefix`](#--tag-version-prefix) - [`--temp-tag`](#--temp-tag) - [`--yes`](#--yes) +- [`--summary-file `](#--summary-file) ### `--canary` @@ -291,6 +292,27 @@ lerna publish --canary --yes When run with this flag, `lerna publish` will skip all confirmation prompts. Useful in [Continuous integration (CI)](https://en.wikipedia.org/wiki/Continuous_integration) to automatically answer the publish confirmation prompt. +### `--summary-file` + +```sh +lerna publish --canary --yes --summary-file ./output.json +``` + +When run with this flag, once a successfully publish it will create a json summary report(see below for an example). + +```json +[ + { + "packageName": "package1", + "version": "v1.0.1-alpha" + }, + { + "packageName": "package2", + "version": "v2.0.1-alpha" + } +] +``` + ## Deprecated Options ### `--skip-npm` diff --git a/commands/publish/__tests__/publish-command.test.js b/commands/publish/__tests__/publish-command.test.js index 75a50df3e9..a50e79df5a 100644 --- a/commands/publish/__tests__/publish-command.test.js +++ b/commands/publish/__tests__/publish-command.test.js @@ -34,6 +34,8 @@ const initFixture = require("@lerna-test/init-fixture")(__dirname); const path = require("path"); const fs = require("fs-extra"); +const fsmain = require("fs"); + // file under test const lernaPublish = require("@lerna-test/command-runner")(require("../command")); @@ -306,6 +308,31 @@ Map { }); }); + describe("--summary-file", () => { + it("skips creating the summary file", async () => { + const cwd = await initFixture("normal"); + const fsSpy = jest.spyOn(fs, "writeFileSync"); + await lernaPublish(cwd); + + expect(fsSpy).not.toHaveBeenCalled(); + }); + + it("creates the summary file", async () => { + const cwd = await initFixture("normal"); + const fsSpy = jest.spyOn(fsmain, "writeFileSync"); + await lernaPublish(cwd)("--summary-file", "./output.json"); + + const expectedJsonResponse = [ + { packageName: "package-1", version: "1.0.1" }, + { packageName: "package-2", version: "1.0.1" }, + { packageName: "package-3", version: "1.0.1" }, + { packageName: "package-4", version: "1.0.1" }, + ]; + expect(fsSpy).toHaveBeenCalled(); + expect(fsSpy).toHaveBeenCalledWith("./output.json", JSON.stringify(expectedJsonResponse)); + }); + }); + describe("--no-verify-access", () => { it("skips package access verification", async () => { const cwd = await initFixture("normal"); diff --git a/commands/publish/command.js b/commands/publish/command.js index ec2d3e9b0d..39f2b6a50b 100644 --- a/commands/publish/command.js +++ b/commands/publish/command.js @@ -110,6 +110,11 @@ exports.builder = (yargs) => { hidden: true, type: "boolean", }, + "summary-file": { + // Json output. + hidden: true, + type: "string", + }, // y: { // describe: "Skip all confirmation prompts.", // alias: "yes", diff --git a/commands/publish/index.js b/commands/publish/index.js index 16d12a2c88..dd5851838d 100644 --- a/commands/publish/index.js +++ b/commands/publish/index.js @@ -1,6 +1,7 @@ "use strict"; const os = require("os"); +const fs = require("fs"); const path = require("path"); const crypto = require("crypto"); const pMap = require("p-map"); @@ -237,10 +238,29 @@ class PublishCommand extends Command { return chain.then(() => { const count = this.packagesToPublish.length; - const message = this.packagesToPublish.map((pkg) => ` - ${pkg.name}@${pkg.version}`); output("Successfully published:"); - output(message.join(os.EOL)); + + if (this.options.summaryFile) { + // create a json object and output it to a file location. + const filePath = this.options.summaryFile || "./output.json"; + const jsonObject = this.packagesToPublish.map((pkg) => { + return { + packageName: pkg.name, + version: pkg.version, + }; + }); + output(jsonObject); + try { + fs.writeFileSync(filePath, JSON.stringify(jsonObject)); + output("Locate Summary Report Here: ", filePath); + } catch (error) { + output("Failed to create the summary report", error); + } + } else { + const message = this.packagesToPublish.map((pkg) => ` - ${pkg.name}@${pkg.version}`); + output(message.join(os.EOL)); + } this.logger.success("published", "%d %s", count, count === 1 ? "package" : "packages"); }); From 2d963e079de167ee36dd763aa43b357e2431c555 Mon Sep 17 00:00:00 2001 From: Hamish Buckmaster Date: Wed, 9 Nov 2022 19:08:49 +1100 Subject: [PATCH 2/7] Apply suggestions from code review Co-authored-by: James Henry --- commands/publish/README.md | 2 +- commands/publish/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/publish/README.md b/commands/publish/README.md index 054a6bf831..ae5a871024 100644 --- a/commands/publish/README.md +++ b/commands/publish/README.md @@ -302,7 +302,7 @@ Useful in [Continuous integration (CI)](https://en.wikipedia.org/wiki/Continuous lerna publish --canary --yes --summary-file ./output.json ``` -When run with this flag, once a successfully publish it will create a json summary report(see below for an example). +When run with this flag, a json summary report will be generated after all packages have been successfully published (see below for an example). ```json [ diff --git a/commands/publish/index.js b/commands/publish/index.js index dd5851838d..8656820cde 100644 --- a/commands/publish/index.js +++ b/commands/publish/index.js @@ -253,7 +253,7 @@ class PublishCommand extends Command { output(jsonObject); try { fs.writeFileSync(filePath, JSON.stringify(jsonObject)); - output("Locate Summary Report Here: ", filePath); + output("Publish summary created: ", filePath); } catch (error) { output("Failed to create the summary report", error); } From 6b208e78942cde7d75efb3f2510a8873aa81e9cc Mon Sep 17 00:00:00 2001 From: Hamish Buckmaster Date: Wed, 9 Nov 2022 19:47:27 +1100 Subject: [PATCH 3/7] chore: pr feedback --- commands/publish/README.md | 11 +++++++---- commands/publish/__tests__/publish-command.test.js | 7 +++++-- commands/publish/command.js | 3 +-- commands/publish/index.js | 4 +++- core/lerna/schemas/lerna-schema.json | 7 +++++++ 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/commands/publish/README.md b/commands/publish/README.md index 52fbde62ff..dd8105b833 100644 --- a/commands/publish/README.md +++ b/commands/publish/README.md @@ -65,7 +65,7 @@ This is useful when a previous `lerna publish` failed to publish all packages to - [`--tag-version-prefix`](#--tag-version-prefix) - [`--temp-tag`](#--temp-tag) - [`--yes`](#--yes) -- [`--summary-file `](#--summary-file) +- [`--summary-file `](#--summary-file) ### `--canary` @@ -119,11 +119,10 @@ This option can be used to publish a [`prerelease`](http://carrot.is/coding/npm_ > Note: the `latest` tag is the one that is used when a user runs `npm install my-package`. > To install a different tag, a user can run `npm install my-package@prerelease`. -> ### `--force-publish` -To be used with [`--canary`](#--canary) to publish a canary version of all packages in your monorepo. This flag can be helpful when you need to make canary releases of packages beyond what was changed in the most recent commit. +To be used with [`--canary`](#--canary) to publish a canary version of all packages in your monorepo. This flag can be helpful when you need to make canary releases of packages beyond what was changed in the most recent commit. ``` lerna publish --canary --force-publish @@ -307,7 +306,11 @@ Useful in [Continuous integration (CI)](https://en.wikipedia.org/wiki/Continuous ### `--summary-file` ```sh -lerna publish --canary --yes --summary-file ./output.json +# Will create a summary file in the root directory ./lerna-publish-summary.json +lerna publish --canary --yes --summary-file +# Will create a summary file in the root directory ./some/other/dir/lerna-publish-summary.json +lerna publish --canary --yes --summary-file ./some/other/dir + ``` When run with this flag, a json summary report will be generated after all packages have been successfully published (see below for an example). diff --git a/commands/publish/__tests__/publish-command.test.js b/commands/publish/__tests__/publish-command.test.js index 8293c8a0a3..f94a0baacb 100644 --- a/commands/publish/__tests__/publish-command.test.js +++ b/commands/publish/__tests__/publish-command.test.js @@ -374,7 +374,7 @@ Map { it("creates the summary file", async () => { const cwd = await initFixture("normal"); const fsSpy = jest.spyOn(fsmain, "writeFileSync"); - await lernaPublish(cwd)("--summary-file", "./output.json"); + await lernaPublish(cwd)("--summary-file", "./outputs"); const expectedJsonResponse = [ { packageName: "package-1", version: "1.0.1" }, @@ -383,7 +383,10 @@ Map { { packageName: "package-4", version: "1.0.1" }, ]; expect(fsSpy).toHaveBeenCalled(); - expect(fsSpy).toHaveBeenCalledWith("./output.json", JSON.stringify(expectedJsonResponse)); + expect(fsSpy).toHaveBeenCalledWith( + "./outputs/lerna-publish-summary.json", + JSON.stringify(expectedJsonResponse) + ); }); }); describe("--verify-access", () => { diff --git a/commands/publish/command.js b/commands/publish/command.js index 9bb5eb39bb..6739066124 100644 --- a/commands/publish/command.js +++ b/commands/publish/command.js @@ -111,8 +111,7 @@ exports.builder = (yargs) => { type: "boolean", }, "summary-file": { - // Json output. - hidden: true, + // generate lerna publish json output. type: "string", }, // y: { diff --git a/commands/publish/index.js b/commands/publish/index.js index f9fe53a50d..6fac149990 100644 --- a/commands/publish/index.js +++ b/commands/publish/index.js @@ -262,7 +262,9 @@ class PublishCommand extends Command { if (this.options.summaryFile) { // create a json object and output it to a file location. - const filePath = this.options.summaryFile || "./output.json"; + const filePath = this.options.summaryFile + ? `${this.options.summaryFile}/lerna-publish-summary.json` + : "./lerna-publish-summary.json"; const jsonObject = this.packagesToPublish.map((pkg) => { return { packageName: pkg.name, diff --git a/core/lerna/schemas/lerna-schema.json b/core/lerna/schemas/lerna-schema.json index f052890fe1..15f6e91535 100644 --- a/core/lerna/schemas/lerna-schema.json +++ b/core/lerna/schemas/lerna-schema.json @@ -874,6 +874,9 @@ }, "continueIfNoMatch": { "$ref": "#/$defs/filters/continueIfNoMatch" + }, + "summaryFile": { + "$ref": "#/$defs/filters/summaryFile" } } }, @@ -1487,6 +1490,10 @@ "continueIfNoMatch": { "type": "boolean", "description": "Don't fail if no package is matched." + }, + "summaryFile": { + "type": "string", + "description": "A json summary report will be generated after all packages have been successfully published" } }, "commandOptions": { From b0a9cc71bfae09ccc1f5bc3d6b95bde1d6be859e Mon Sep 17 00:00:00 2001 From: Hamish Buckmaster Date: Tue, 13 Dec 2022 19:57:50 +1100 Subject: [PATCH 4/7] chore: pr feedback --- commands/publish/command.js | 6 +----- core/lerna/schemas/lerna-schema.json | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/commands/publish/command.js b/commands/publish/command.js index 6739066124..ae67aa90c8 100644 --- a/commands/publish/command.js +++ b/commands/publish/command.js @@ -112,13 +112,9 @@ exports.builder = (yargs) => { }, "summary-file": { // generate lerna publish json output. + describe: "A json summary report will be generated after all packages have been successfully published", type: "string", }, - // y: { - // describe: "Skip all confirmation prompts.", - // alias: "yes", - // type: "boolean", - // }, }; composeVersionOptions(yargs); diff --git a/core/lerna/schemas/lerna-schema.json b/core/lerna/schemas/lerna-schema.json index 15f6e91535..9b6723ce8a 100644 --- a/core/lerna/schemas/lerna-schema.json +++ b/core/lerna/schemas/lerna-schema.json @@ -1493,7 +1493,7 @@ }, "summaryFile": { "type": "string", - "description": "A json summary report will be generated after all packages have been successfully published" + "description": "A json summary report will be generated after all packages have been successfully published." } }, "commandOptions": { From c764285c31c1e626fcde54cc690f2bbbd93a8803 Mon Sep 17 00:00:00 2001 From: Hamish Buckmaster Date: Tue, 13 Dec 2022 20:20:40 +1100 Subject: [PATCH 5/7] chore: pr feedback --- .../publish/__tests__/publish-command.test.js | 20 ++++++++++++++++++- commands/publish/command.js | 3 ++- commands/publish/index.js | 18 ++++++++--------- core/lerna/schemas/lerna-schema.json | 2 +- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/commands/publish/__tests__/publish-command.test.js b/commands/publish/__tests__/publish-command.test.js index f94a0baacb..1a8badf6e1 100644 --- a/commands/publish/__tests__/publish-command.test.js +++ b/commands/publish/__tests__/publish-command.test.js @@ -371,7 +371,7 @@ Map { expect(fsSpy).not.toHaveBeenCalled(); }); - it("creates the summary file", async () => { + it("creates the summary file with file path provided", async () => { const cwd = await initFixture("normal"); const fsSpy = jest.spyOn(fsmain, "writeFileSync"); await lernaPublish(cwd)("--summary-file", "./outputs"); @@ -388,6 +388,24 @@ Map { JSON.stringify(expectedJsonResponse) ); }); + + it("creates the summary file with no path provided", async () => { + const cwd = await initFixture("normal"); + const fsSpy = jest.spyOn(fsmain, "writeFileSync"); + await lernaPublish(cwd)("--summary-file"); + + const expectedJsonResponse = [ + { packageName: "package-1", version: "1.0.1" }, + { packageName: "package-2", version: "1.0.1" }, + { packageName: "package-3", version: "1.0.1" }, + { packageName: "package-4", version: "1.0.1" }, + ]; + expect(fsSpy).toHaveBeenCalled(); + expect(fsSpy).toHaveBeenCalledWith( + "./lerna-publish-summary.json", + JSON.stringify(expectedJsonResponse) + ); + }); }); describe("--verify-access", () => { it("publishes packages after verifying the user's access to each package", async () => { diff --git a/commands/publish/command.js b/commands/publish/command.js index ae67aa90c8..586c774f71 100644 --- a/commands/publish/command.js +++ b/commands/publish/command.js @@ -112,7 +112,8 @@ exports.builder = (yargs) => { }, "summary-file": { // generate lerna publish json output. - describe: "A json summary report will be generated after all packages have been successfully published", + describe: + "Generate a json summary report after all packages have been successfully published, you can pass an optional path for where to save the file.", type: "string", }, }; diff --git a/commands/publish/index.js b/commands/publish/index.js index 5b394f7895..5983279efc 100644 --- a/commands/publish/index.js +++ b/commands/publish/index.js @@ -260,7 +260,7 @@ class PublishCommand extends Command { output("Successfully published:"); - if (this.options.summaryFile) { + if (this.options.summaryFile !== undefined) { // create a json object and output it to a file location. const filePath = this.options.summaryFile ? `${this.options.summaryFile}/lerna-publish-summary.json` @@ -396,16 +396,14 @@ class PublishCommand extends Command { }).filter((node) => !node.pkg.private) ); - const makeVersion = - (fallback) => - ({ lastVersion = fallback, refCount, sha }) => { - // the next version is bumped without concern for preid or current index - const nextVersion = semver.inc(lastVersion.replace(this.tagPrefix, ""), release.replace("pre", "")); + const makeVersion = (fallback) => ({ lastVersion = fallback, refCount, sha }) => { + // the next version is bumped without concern for preid or current index + const nextVersion = semver.inc(lastVersion.replace(this.tagPrefix, ""), release.replace("pre", "")); - // semver.inc() starts a new prerelease at .0, git describe starts at .1 - // and build metadata is always ignored when comparing dependency ranges - return `${nextVersion}-${preid}.${Math.max(0, refCount - 1)}+${sha}`; - }; + // semver.inc() starts a new prerelease at .0, git describe starts at .1 + // and build metadata is always ignored when comparing dependency ranges + return `${nextVersion}-${preid}.${Math.max(0, refCount - 1)}+${sha}`; + }; if (this.project.isIndependent()) { // each package is described against its tags only diff --git a/core/lerna/schemas/lerna-schema.json b/core/lerna/schemas/lerna-schema.json index 9b6723ce8a..3d04eeda29 100644 --- a/core/lerna/schemas/lerna-schema.json +++ b/core/lerna/schemas/lerna-schema.json @@ -1493,7 +1493,7 @@ }, "summaryFile": { "type": "string", - "description": "A json summary report will be generated after all packages have been successfully published." + "description": "Generate a json summary report after all packages have been successfully published, you can pass an optional path for where to save the file." } }, "commandOptions": { From 6a9d038c2d4a578cab3407e5807a7880005de60d Mon Sep 17 00:00:00 2001 From: James Henry Date: Tue, 13 Dec 2022 14:17:16 +0400 Subject: [PATCH 6/7] chore: apply suggestions from code review --- commands/publish/README.md | 4 ++-- commands/publish/__tests__/publish-command.test.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/publish/README.md b/commands/publish/README.md index dd8105b833..ceb7b9fd35 100644 --- a/commands/publish/README.md +++ b/commands/publish/README.md @@ -306,9 +306,9 @@ Useful in [Continuous integration (CI)](https://en.wikipedia.org/wiki/Continuous ### `--summary-file` ```sh -# Will create a summary file in the root directory ./lerna-publish-summary.json +# Will create a summary file in the root directory, i.e. `./lerna-publish-summary.json` lerna publish --canary --yes --summary-file -# Will create a summary file in the root directory ./some/other/dir/lerna-publish-summary.json +# Will create a summary file in the provided directory, i.e. `./some/other/dir/lerna-publish-summary.json` lerna publish --canary --yes --summary-file ./some/other/dir ``` diff --git a/commands/publish/__tests__/publish-command.test.js b/commands/publish/__tests__/publish-command.test.js index 1a8badf6e1..372185edba 100644 --- a/commands/publish/__tests__/publish-command.test.js +++ b/commands/publish/__tests__/publish-command.test.js @@ -371,7 +371,7 @@ Map { expect(fsSpy).not.toHaveBeenCalled(); }); - it("creates the summary file with file path provided", async () => { + it("creates the summary file within the provided directory", async () => { const cwd = await initFixture("normal"); const fsSpy = jest.spyOn(fsmain, "writeFileSync"); await lernaPublish(cwd)("--summary-file", "./outputs"); @@ -389,7 +389,7 @@ Map { ); }); - it("creates the summary file with no path provided", async () => { + it("creates the summary file at the root when no custom directory is provided", async () => { const cwd = await initFixture("normal"); const fsSpy = jest.spyOn(fsmain, "writeFileSync"); await lernaPublish(cwd)("--summary-file"); From 2a8954a3924ad8bd20434a388e1c4043ad935c18 Mon Sep 17 00:00:00 2001 From: Hamish Buckmaster Date: Tue, 13 Dec 2022 21:50:48 +1100 Subject: [PATCH 7/7] chore: pr feedback --- commands/publish/index.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/commands/publish/index.js b/commands/publish/index.js index 5983279efc..c36fff5531 100644 --- a/commands/publish/index.js +++ b/commands/publish/index.js @@ -396,14 +396,16 @@ class PublishCommand extends Command { }).filter((node) => !node.pkg.private) ); - const makeVersion = (fallback) => ({ lastVersion = fallback, refCount, sha }) => { - // the next version is bumped without concern for preid or current index - const nextVersion = semver.inc(lastVersion.replace(this.tagPrefix, ""), release.replace("pre", "")); - - // semver.inc() starts a new prerelease at .0, git describe starts at .1 - // and build metadata is always ignored when comparing dependency ranges - return `${nextVersion}-${preid}.${Math.max(0, refCount - 1)}+${sha}`; - }; + const makeVersion = + (fallback) => + ({ lastVersion = fallback, refCount, sha }) => { + // the next version is bumped without concern for preid or current index + const nextVersion = semver.inc(lastVersion.replace(this.tagPrefix, ""), release.replace("pre", "")); + + // semver.inc() starts a new prerelease at .0, git describe starts at .1 + // and build metadata is always ignored when comparing dependency ranges + return `${nextVersion}-${preid}.${Math.max(0, refCount - 1)}+${sha}`; + }; if (this.project.isIndependent()) { // each package is described against its tags only