Skip to content
Permalink
Browse files
feat(publish): Run npm pack before npm publish
  • Loading branch information
evocateur committed Aug 8, 2018
1 parent 088ea54 commit 8d80b2c0ffc180d8811217ec1026383b6c20e3c4
Showing 5 changed files with 76 additions and 45 deletions.
@@ -38,7 +38,7 @@ describe("licenses", () => {
it("removes all temporary licenses on error", async () => {
const cwd = await initFixture("licenses");

npmPublish.mockImplementationOnce(() => Promise.reject(new Error("boom")));
npmPublish.npmPack.mockImplementationOnce(() => Promise.reject(new Error("boom")));

try {
await lernaPublish(cwd)();
@@ -53,7 +53,7 @@ describe("licenses", () => {
it("does not override original error when removal rejects", async () => {
const cwd = await initFixture("licenses");

npmPublish.mockImplementationOnce(() => Promise.reject(new Error("boom")));
npmPublish.npmPack.mockImplementationOnce(() => Promise.reject(new Error("boom")));
removeTempLicenses.mockImplementationOnce(() => Promise.reject(new Error("shaka-lakka")));

try {
@@ -24,14 +24,17 @@ describe("lifecycle scripts", () => {

await lernaPublish(cwd)();

expect(runLifecycle).toHaveBeenCalledTimes(12);

["prepare", "prepublishOnly", "postpublish"].forEach(script => {
// "lifecycle" is the root manifest name
expect(runLifecycle).toHaveBeenCalledWith(expect.objectContaining({ name: "lifecycle" }), script);
expect(runLifecycle).toHaveBeenCalledWith(expect.objectContaining({ name: "package-1" }), script);
});

// all leaf package lifecycles are called by npm pack
expect(runLifecycle).not.toHaveBeenCalledWith(
expect.objectContaining({ name: "package-1" }),
expect.stringMatching(/(prepare|prepublishOnly|postpublish)/)
);

// package-2 lacks version lifecycle scripts
expect(runLifecycle).not.toHaveBeenCalledWith(
expect.objectContaining({ name: "package-2" }),
@@ -49,9 +52,6 @@ describe("lifecycle scripts", () => {
// publish-specific
["lifecycle", "prepare"],
["lifecycle", "prepublishOnly"],
["package-1", "prepare"],
["package-1", "prepublishOnly"],
["package-1", "postpublish"],
["lifecycle", "postpublish"],
]);
});
@@ -109,7 +109,8 @@ class PublishCommand extends Command {

chain = chain.then(() => this.resolveLocalDependencyLinks());
chain = chain.then(() => this.annotateGitHead());
chain = chain.then(() => this.npmPublish());
chain = chain.then(() => this.packUpdated());
chain = chain.then(() => this.publishPacked());
chain = chain.then(() => this.resetChanges());

if (this.options.tempTag) {
@@ -338,49 +339,70 @@ class PublishCommand extends Command {
});
}

npmPublish() {
const tracker = this.logger.newItem("npmPublish");
// if we skip temp tags we should tag with the proper value immediately
const distTag = this.options.tempTag ? "lerna-temp" : this.getDistTag();
packUpdated() {
const tracker = this.logger.newItem("npm pack");

tracker.addWork(
this.options.requireScripts
? // track completion of prepublish.js on updates as well
this.packagesToPublish.length + this.updates.length
: this.packagesToPublish.length
);

let chain = Promise.resolve();

chain = chain.then(() => createTempLicenses(this.project.licensePath, this.packagesToBeLicensed));

chain = chain.then(() => this.runPrepublishScripts(this.project.manifest));
chain = chain.then(() =>
pMap(this.updates, ({ pkg }) => {
if (this.options.requireScripts) {

if (this.options.requireScripts) {
chain = chain.then(() =>
pMap(this.updates, ({ pkg }) => {
this.execScript(pkg, "prepublish");
}
tracker.completeWork(1);
})
);
}

return this.runPrepublishScripts(pkg);
})
chain = chain.then(() =>
runParallelBatches(this.batchedPackages, this.concurrency, pkg =>
npmPublish.npmPack(pkg).then(() => {
tracker.completeWork(1);
})
)
);

chain = chain.then(() => removeTempLicenses(this.packagesToBeLicensed));

// remove temporary license files if _any_ error occurs _anywhere_ in the promise chain
chain = chain.catch(error => this.removeTempLicensesOnError(error));

return pFinally(chain, () => tracker.finish());
}

publishPacked() {
// if we skip temp tags we should tag with the proper value immediately
const distTag = this.options.tempTag ? "lerna-temp" : this.getDistTag();
const tracker = this.logger.newItem(`${this.npmConfig.npmClient} publish`);

tracker.addWork(this.packagesToPublish.length);

const mapPackage = pkg => {
tracker.verbose("publishing", pkg.name);
let chain = Promise.resolve();

return npmPublish(pkg, distTag, this.npmConfig).then(() => {
tracker.info("published", pkg.name);
tracker.completeWork(1);
chain = chain.then(() =>
runParallelBatches(this.batchedPackages, this.concurrency, pkg =>
npmPublish(pkg, distTag, this.npmConfig).then(() => {
tracker.info("published", pkg.name);

if (this.options.requireScripts) {
this.execScript(pkg, "postpublish");
}
if (this.options.requireScripts) {
this.execScript(pkg, "postpublish");
}

return this.runPackageLifecycle(pkg, "postpublish");
});
};
tracker.completeWork(1);
})
)
);

chain = chain.then(() => runParallelBatches(this.batchedPackages, this.concurrency, mapPackage));
chain = chain.then(() => this.runPackageLifecycle(this.project.manifest, "postpublish"));
chain = chain.then(() => removeTempLicenses(this.packagesToBeLicensed));

// remove temporary license files if _any_ error occurs _anywhere_ in the promise chain
chain = chain.catch(error => this.removeTempLicensesOnError(error));

return pFinally(chain, () => tracker.finish());
}
@@ -21,7 +21,7 @@ describe("npm-publish", () => {

expect(ChildProcessUtilities.exec).lastCalledWith(
"npm",
["publish", "--ignore-scripts", "--tag", "published-tag"],
["publish", "--ignore-scripts", "--tag", "published-tag", "test-1.10.100.tgz"],
{
cwd: pkg.location,
env: {},
@@ -33,19 +33,23 @@ describe("npm-publish", () => {
it("does not pass --tag when none present (npm default)", async () => {
await npmPublish(pkg, undefined, { npmClient: "npm" });

expect(ChildProcessUtilities.exec).lastCalledWith("npm", ["publish", "--ignore-scripts"], {
cwd: pkg.location,
env: {},
pkg,
});
expect(ChildProcessUtilities.exec).lastCalledWith(
"npm",
["publish", "--ignore-scripts", "test-1.10.100.tgz"],
{
cwd: pkg.location,
env: {},
pkg,
}
);
});

it("trims trailing whitespace in tag parameter", async () => {
await npmPublish(pkg, "trailing-tag ", { npmClient: "npm" });

expect(ChildProcessUtilities.exec).lastCalledWith(
"npm",
["publish", "--ignore-scripts", "--tag", "trailing-tag"],
["publish", "--ignore-scripts", "--tag", "trailing-tag", "test-1.10.100.tgz"],
{
cwd: pkg.location,
env: {},
@@ -61,7 +65,7 @@ describe("npm-publish", () => {

expect(ChildProcessUtilities.exec).lastCalledWith(
"npm",
["publish", "--ignore-scripts", "--tag", "custom-registry"],
["publish", "--ignore-scripts", "--tag", "custom-registry", "test-1.10.100.tgz"],
{
cwd: pkg.location,
env: {
@@ -86,6 +90,7 @@ describe("npm-publish", () => {
"--new-version",
pkg.version,
"--non-interactive",
"test-1.10.100.tgz",
],
{
cwd: pkg.location,
@@ -9,7 +9,7 @@ module.exports = npmPublish;
module.exports.npmPack = npmPack;

function npmPublish(pkg, tag, { npmClient, registry }) {
log.silly("npmPublish", tag, pkg.name);
log.verbose("publish", pkg.name);

const distTag = tag && tag.trim();
const opts = getExecOpts(pkg, registry);
@@ -25,6 +25,10 @@ function npmPublish(pkg, tag, { npmClient, registry }) {
args.push("--new-version", pkg.version, "--non-interactive");
}

// always add tarball file, created by npmPack()
args.push(pkg.tarball);

log.silly("exec", npmClient, ...args);
return ChildProcessUtilities.exec(npmClient, args, opts);
}

0 comments on commit 8d80b2c

Please sign in to comment.