From e4b74a63b87cab618d1ae9256d1186104977d6ac Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Wed, 22 Mar 2017 10:57:02 -0700 Subject: [PATCH] Do not reject detached HEAD when publishing a canary release (#711) * assertStubbedCalls: cache objectMethodName and use it for helpful diagnostics --- src/GitUtilities.js | 21 ++-- src/commands/PublishCommand.js | 34 +++--- test/GitUtilities.js | 39 ++++++- test/PublishCommand.js | 160 +++++++++++++++++++++-------- test/helpers/assertStubbedCalls.js | 7 +- 5 files changed, 185 insertions(+), 76 deletions(-) diff --git a/src/GitUtilities.js b/src/GitUtilities.js index 6ce7414db2..f0a09a505b 100644 --- a/src/GitUtilities.js +++ b/src/GitUtilities.js @@ -3,6 +3,12 @@ import logger from "./logger"; import escapeArgs from "command-join"; export default class GitUtilities { + @logger.logifySync() + static isDetachedHead() { + const branchName = GitUtilities.getCurrentBranch(); + return branchName === "HEAD"; + } + @logger.logifySync() static isInitialized() { try { @@ -77,6 +83,11 @@ export default class GitUtilities { return ChildProcessUtilities.execSync("git diff --name-only " + since + " -- " + escapeArgs(location)); } + @logger.logifySync() + static getCurrentBranch() { + return ChildProcessUtilities.execSync("git rev-parse --abbrev-ref HEAD"); + } + @logger.logifySync() static getCurrentSHA() { return ChildProcessUtilities.execSync("git rev-parse HEAD"); @@ -92,16 +103,6 @@ export default class GitUtilities { ChildProcessUtilities.execSync("git checkout -- " + changes); } - @logger.logifySync() - static getCurrentBranch() { - return ChildProcessUtilities.execSync("git symbolic-ref --short HEAD"); - } - - @logger.logifySync() - static getCurrentBranchDescription() { - return ChildProcessUtilities.execSync("git symbolic-ref --short -q HEAD"); - } - @logger.logifySync() static init() { return ChildProcessUtilities.execSync("git init"); diff --git a/src/commands/PublishCommand.js b/src/commands/PublishCommand.js index aae22f650f..ebccb6ecf0 100644 --- a/src/commands/PublishCommand.js +++ b/src/commands/PublishCommand.js @@ -14,16 +14,12 @@ import { EOL } from "os"; export default class PublishCommand extends Command { initialize(callback) { + this.gitEnabled = !(this.flags.canary || this.flags.skipGit); if (this.flags.canary) { this.logger.info("Publishing canary build"); } - const currentBranch = GitUtilities.getCurrentBranchDescription(); - if (currentBranch === "") { - callback("You are working on detached branch, create new branch to publish changes."); - } - if (!this.repository.isIndependent()) { this.globalVersion = this.repository.version; this.logger.info("Current version: " + this.globalVersion); @@ -94,12 +90,17 @@ export default class PublishCommand extends Command { execute(callback) { try { + if (this.gitEnabled && GitUtilities.isDetachedHead()) { + throw new Error("Detached git HEAD, please checkout a branch to publish changes."); + } + if (!this.repository.isIndependent() && !this.flags.canary) { this.updateVersionInLernaJson(); } this.updateUpdatedPackages(); - if (!this.flags.skipGit) { + + if (this.gitEnabled) { this.commitAndTagUpdates(); } } catch (err) { @@ -137,7 +138,7 @@ export default class PublishCommand extends Command { return; } - if (!(this.flags.canary || this.flags.skipGit)) { + if (this.gitEnabled) { this.logger.info("Pushing tags to git..."); this.logger.newLine(); GitUtilities.pushWithTags(this.getOptions().gitRemote || "origin", this.tags); @@ -220,12 +221,12 @@ export default class PublishCommand extends Command { }); }); callback(null, { versions }); - // Independent Non-Canary Mode + + // Independent Non-Canary Mode } else { async.mapLimit(this.updates, 1, (update, cb) => { this.promptVersion(update.package.name, update.package.version, cb); }, (err, versions) => { - if (err) { return callback(err); } @@ -370,7 +371,7 @@ export default class PublishCommand extends Command { changedFiles.push(packageJsonLocation); }); - if (!(this.flags.canary || this.flags.skipGit)) { + if (this.gitEnabled) { changedFiles.forEach(GitUtilities.addFile); } } @@ -392,12 +393,10 @@ export default class PublishCommand extends Command { } commitAndTagUpdates() { - if (!this.flags.canary) { - if (this.repository.isIndependent()) { - this.tags = this.gitCommitAndTagVersionForUpdates(); - } else { - this.tags = [this.gitCommitAndTagVersion(this.masterVersion)]; - } + if (this.repository.isIndependent()) { + this.tags = this.gitCommitAndTagVersionForUpdates(); + } else { + this.tags = [this.gitCommitAndTagVersion(this.masterVersion)]; } } @@ -407,14 +406,17 @@ export default class PublishCommand extends Command { GitUtilities.commit(message); tags.forEach(GitUtilities.addTag); + return tags; } gitCommitAndTagVersion(version) { const tag = "v" + version; const message = this.flags.message || tag; + GitUtilities.commit(message); GitUtilities.addTag(tag); + return tag; } diff --git a/test/GitUtilities.js b/test/GitUtilities.js index a3cfb33657..5e602430e3 100644 --- a/test/GitUtilities.js +++ b/test/GitUtilities.js @@ -1,5 +1,6 @@ import assert from "assert"; +import ChildProcessUtilities from "../src/ChildProcessUtilities"; import GitUtilities from "../src/GitUtilities"; /** @@ -8,6 +9,33 @@ import GitUtilities from "../src/GitUtilities"; */ describe("GitUtilities", () => { + const cpuExecSync = ChildProcessUtilities.execSync; + + beforeEach(() => { + ChildProcessUtilities.execSync = jest.fn(); + }); + + afterEach(() => { + ChildProcessUtilities.execSync = cpuExecSync; + }); + + describe(".isDetachedHead()", () => { + it("calls getCurrentBranch()", () => { + expect(() => GitUtilities.isDetachedHead()).not.toThrow(); + expect(ChildProcessUtilities.execSync).lastCalledWith("git rev-parse --abbrev-ref HEAD"); + }); + + it("returns true when branchName is HEAD", () => { + ChildProcessUtilities.execSync.mockImplementation(() => "HEAD"); + expect(GitUtilities.isDetachedHead()).toBe(true); + }); + + it("returns false when branchName is not HEAD", () => { + ChildProcessUtilities.execSync.mockImplementation(() => "master"); + expect(GitUtilities.isDetachedHead()).toBe(false); + }); + }); + describe(".isInitialized()", () => { it("should exist", () => { assert.ok(GitUtilities.isInitialized); @@ -86,15 +114,16 @@ describe("GitUtilities", () => { }); }); - describe(".getCurrentSHA()", () => { - it("should exist", () => { - assert.ok(GitUtilities.getCurrentSHA); + describe(".getCurrentBranch()", () => { + it("calls `git rev-parse --abbrev-ref HEAD`", () => { + expect(() => GitUtilities.getCurrentBranch()).not.toThrow(); + expect(ChildProcessUtilities.execSync).lastCalledWith("git rev-parse --abbrev-ref HEAD"); }); }); - describe(".getCurrentBranchDescription()", () => { + describe(".getCurrentSHA()", () => { it("should exist", () => { - assert.ok(GitUtilities.getCurrentBranchDescription); + assert.ok(GitUtilities.getCurrentSHA); }); }); diff --git a/test/PublishCommand.js b/test/PublishCommand.js index 0362e31f60..db2cbe1fe1 100644 --- a/test/PublishCommand.js +++ b/test/PublishCommand.js @@ -35,7 +35,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -45,6 +44,7 @@ describe("PublishCommand", () => { { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -80,7 +80,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -133,7 +133,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -146,6 +145,7 @@ describe("PublishCommand", () => { { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-3/package.json"))] }, @@ -179,7 +179,7 @@ describe("PublishCommand", () => { { args: ["npm dist-tag rm package-2 lerna-temp"] }, { args: ["npm dist-tag add package-2@1.1.0 latest"] }, - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin package-1@1.0.1 package-2@1.1.0 package-3@2.0.0 package-4@1.1.0"] }, ]] @@ -229,7 +229,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] }, { args: ["git rev-parse HEAD"], returns: "81e3b44339e1403fe3d762e9435b7c9a155fdef7" }, @@ -262,10 +261,6 @@ describe("PublishCommand", () => { { args: ["npm dist-tag ls package-2"], returns: "lerna-temp: 1.0.0-alpha.81e3b443" + EOL + "stable: 1.0.0" }, { args: ["npm dist-tag rm package-2 lerna-temp"] }, { args: ["npm dist-tag add package-2@1.0.0-alpha.81e3b443 canary"] }, - - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, - { args: ["git push origin master"] }, - { args: ["git push origin v1.0.1"] } ]] ]); @@ -319,7 +314,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] }, { args: ["git rev-parse HEAD"], returns: "81e3b44339e1403fe3d762e9435b7c9a155fdef7" }, @@ -352,10 +346,6 @@ describe("PublishCommand", () => { { args: ["npm dist-tag ls package-2"], returns: "lerna-temp: 2.0.0-alpha.81e3b443" + EOL + "stable: 1.0.0" }, { args: ["npm dist-tag rm package-2 lerna-temp"] }, { args: ["npm dist-tag add package-2@2.0.0-alpha.81e3b443 canary"] }, - - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, - { args: ["git push origin master"] }, - { args: ["git push origin v1.0.1"] } ]] ]); @@ -407,7 +397,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -486,7 +475,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -496,6 +484,7 @@ describe("PublishCommand", () => { { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -555,7 +544,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -612,7 +600,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -622,6 +609,7 @@ describe("PublishCommand", () => { { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -641,7 +629,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -672,7 +660,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -682,6 +669,7 @@ describe("PublishCommand", () => { { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -717,7 +705,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -797,13 +785,13 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "confirm", { valueCallback: true }, [ { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -839,7 +827,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -908,13 +896,13 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "confirm", { valueCallback: true }, [ { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git commit -m \"$(echo \"v1.0.1\")\""] }, @@ -930,7 +918,7 @@ describe("PublishCommand", () => { { args: ["npm dist-tag rm package-1 lerna-temp", { env, cwd: path.join(testDir,"packages/package-1") }] }, { args: ["npm dist-tag add package-1@1.0.1 latest", { env, cwd: path.join(testDir,"packages/package-1") }] }, - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -961,13 +949,13 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "confirm", { valueCallback: true }, [ { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -1003,7 +991,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -1057,13 +1045,13 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "confirm", { valueCallback: true }, [ { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -1099,7 +1087,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -1155,13 +1143,13 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "confirm", { valueCallback: true }, [ { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -1197,7 +1185,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -1254,13 +1242,13 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "confirm", { valueCallback: true }, [ { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -1293,7 +1281,7 @@ describe("PublishCommand", () => { { args: ["npm dist-tag rm package-2 lerna-temp", { cwd: path.join(testDir,"packages/package-2") }] }, { args: ["npm dist-tag add package-2@1.1.0 latest", { cwd: path.join(testDir,"packages/package-2") }] }, - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.1.0"] } ]], @@ -1346,13 +1334,13 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "confirm", { valueCallback: true }, [ { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-3/package.json"))] }, @@ -1386,7 +1374,7 @@ describe("PublishCommand", () => { { args: ["npm dist-tag rm package-2 lerna-temp"] }, { args: ["npm dist-tag add package-2@2.0.1 latest"] }, - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin package-1@1.0.1 package-2@2.0.1 package-3@3.0.1 package-4@4.0.1"] } ]], @@ -1437,7 +1425,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -1447,6 +1434,7 @@ describe("PublishCommand", () => { { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -1482,7 +1470,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push upstream master"] }, { args: ["git push upstream v1.0.1"] } ]], @@ -1535,7 +1523,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -1545,6 +1532,7 @@ describe("PublishCommand", () => { { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "lerna.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, @@ -1580,7 +1568,7 @@ describe("PublishCommand", () => { // No package-5. It's private. - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin v1.0.1"] } ]], @@ -1634,7 +1622,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [PromptUtilities, "select", { valueCallback: true }, [ @@ -1647,6 +1634,7 @@ describe("PublishCommand", () => { { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-1/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-2/package.json"))] }, { args: ["git add " + escapeArgs(path.join(testDir, "packages/package-3/package.json"))] }, @@ -1680,7 +1668,7 @@ describe("PublishCommand", () => { { args: ["npm dist-tag rm package-2 lerna-temp"] }, { args: ["npm dist-tag add package-2@1.1.0 latest"] }, - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin package-1@1.0.1 package-2@1.1.0 package-3@2.0.0 package-4@1.1.0"] }, ]] @@ -1732,7 +1720,6 @@ describe("PublishCommand", () => { assertStubbedCalls([ [ChildProcessUtilities, "execSync", {}, [ - { args: ["git symbolic-ref --short -q HEAD"] }, { args: ["git tag"] } ]], [ConventionalCommitUtilities, "recommendVersion", {}, [ @@ -1744,6 +1731,9 @@ describe("PublishCommand", () => { [PromptUtilities, "confirm", { valueCallback: true }, [ { args: ["Are you sure you want to publish the above changes?"], returns: true } ]], + [ChildProcessUtilities, "execSync", {}, [ + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, + ]], [ConventionalCommitUtilities, "updateChangelog", {}, [ { args: [{ name: "package-1", location: path.join(testDir, "packages/package-1") }] }, { args: [{ name: "package-2", location: path.join(testDir, "packages/package-2") }] }, @@ -1788,7 +1778,7 @@ describe("PublishCommand", () => { { args: ["npm dist-tag rm package-2 lerna-temp"] }, { args: ["npm dist-tag add package-2@1.1.0 latest"] }, - { args: ["git symbolic-ref --short HEAD"], returns: "master" }, + { args: ["git rev-parse --abbrev-ref HEAD"], returns: "master" }, { args: ["git push origin master"] }, { args: ["git push origin package-1@1.0.1 package-2@1.1.0 package-3@2.0.0 package-4@1.1.0"] }, ]] @@ -1816,4 +1806,88 @@ describe("PublishCommand", () => { })); }); }); + + /** ========================================================================= + * INDEPENDENT - CANARY + NPMTAG + YES + EXACT + * ======================================================================= */ + + describe("independent mode --canary --npm-tag=next --yes --exact", () => { + let testDir; + + beforeEach(() => initFixture("PublishCommand/independent").then((dir) => { + testDir = dir; + })); + + it("should publish the changed packages", (done) => { + const publishCommand = new PublishCommand([], { + independent: true, + canary: true, + npmTag: "next", + yes: true, + exact: true, + }); + + publishCommand.runValidations(); + publishCommand.runPreparations(); + + assertStubbedCalls([ + [ChildProcessUtilities, "execSync", {}, [ + { args: ["git tag"] }, + + { args: ["git rev-parse HEAD"], returns: "81e3b44339e1403fe3d762e9435b7c9a155fdef7" }, + { args: ["git rev-parse HEAD"], returns: "81e3b44339e1403fe3d762e9435b7c9a155fdef7" } + ]], + [ChildProcessUtilities, "exec", { nodeCallback: true }, [ + { args: ["npm publish --tag lerna-temp"] }, + { args: ["npm publish --tag lerna-temp"] }, + { args: ["npm publish --tag lerna-temp"] }, + { args: ["npm publish --tag lerna-temp"] }, + ]], + [ChildProcessUtilities, "execSync", {}, [ + { args: ["git checkout -- packages/*/package.json"] }, + + { args: ["npm dist-tag ls package-1"], returns: "lerna-temp: 1.0.0-alpha.81e3b443" + EOL + "latest: 1.0.0" }, + { args: ["npm dist-tag rm package-1 lerna-temp"] }, + { args: ["npm dist-tag add package-1@1.0.0-alpha.81e3b443 next"] }, + + { args: ["npm dist-tag ls package-3"], returns: "lerna-temp: 3.0.0-alpha.81e3b443" + EOL + "latest: 1.0.0" }, + { args: ["npm dist-tag rm package-3 lerna-temp"] }, + { args: ["npm dist-tag add package-3@3.0.0-alpha.81e3b443 next"] }, + + { args: ["npm dist-tag ls package-4"], returns: "lerna-temp: 4.0.0-alpha.81e3b443" + EOL + "latest: 1.0.0" }, + { args: ["npm dist-tag rm package-4 lerna-temp"] }, + { args: ["npm dist-tag add package-4@4.0.0-alpha.81e3b443 next"] }, + + { args: ["npm dist-tag ls package-2"], returns: "lerna-temp: 2.0.0-alpha.81e3b443" + EOL + "latest: 1.0.0" }, + { args: ["npm dist-tag rm package-2 lerna-temp"] }, + { args: ["npm dist-tag add package-2@2.0.0-alpha.81e3b443 next"] }, + ]] + ]); + + publishCommand.runCommand(exitWithCode(0, (err) => { + if (err) return done.fail(err); + + try { + assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log"))); + + // The following wouldn't be the actual results of a canary release + // because `git checkout --` would have removed the file changes. + // However, this is what would've been published to npm so it's + // useful to test. + assert.equal(require(path.join(testDir, "packages/package-1/package.json")).version, "1.0.0-alpha.81e3b443"); + assert.equal(require(path.join(testDir, "packages/package-2/package.json")).version, "2.0.0-alpha.81e3b443"); + assert.equal(require(path.join(testDir, "packages/package-3/package.json")).version, "3.0.0-alpha.81e3b443"); + assert.equal(require(path.join(testDir, "packages/package-4/package.json")).version, "4.0.0-alpha.81e3b443"); + + assert.equal(require(path.join(testDir, "packages/package-2/package.json")).dependencies["package-1"], "1.0.0-alpha.81e3b443"); + assert.equal(require(path.join(testDir, "packages/package-3/package.json")).devDependencies["package-2"], "2.0.0-alpha.81e3b443"); + assert.equal(require(path.join(testDir, "packages/package-4/package.json")).dependencies["package-1"], "^0.0.0"); + + done(); + } catch (ex) { + done.fail(ex); + } + })); + }); + }); }); diff --git a/test/helpers/assertStubbedCalls.js b/test/helpers/assertStubbedCalls.js index f67a37e28f..0de9b3a1a3 100644 --- a/test/helpers/assertStubbedCalls.js +++ b/test/helpers/assertStubbedCalls.js @@ -12,6 +12,8 @@ export default function assertStubbedCalls(definitions) { let callCount = 0; function stubMethod(object, method) { + const objectMethodName = `${object.name}.${method}`; + stub(object, method, function(...actualArgs) { const currentCount = callCount++; const current = expected[currentCount]; @@ -23,14 +25,15 @@ export default function assertStubbedCalls(definitions) { let {args, returns, throws} = current.call; try { - expect(object[method]).toBe(current.object[current.method]); + expect(`${current.object.name}.${current.method}`).toBe(objectMethodName); + expect(current.object[current.method]).toBe(object[method]); for (let a = 0; a < args.length; a++) { expect(actualArgs[a]).toEqual(args[a]); } } catch (err) { // prepend JestAssertionError message with useful metadata before re-throwing - err.message = `Call ${currentCount} of ${object.name}.${method}() has unexpected args!\n\n${err.message}`; + err.message = `Call ${currentCount} of ${objectMethodName}() has unexpected args!\n\n${err.message}`; throw err; }