Skip to content

Commit 6d4d1c1

Browse files
Merge pull request #1397 from intuit/private-single-npm
skip publish for private npm packages
2 parents 4d44c6e + bfb712b commit 6d4d1c1

File tree

6 files changed

+153
-23
lines changed

6 files changed

+153
-23
lines changed

plugins/npm/__tests__/npm-next.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,49 @@ describe("next", () => {
111111
]);
112112
});
113113

114+
test("skips publish for private package", async () => {
115+
const plugin = new NPMPlugin();
116+
const hooks = makeHooks();
117+
118+
plugin.apply(({
119+
config: { prereleaseBranches: ["next"] },
120+
hooks,
121+
remote: "origin",
122+
baseBranch: "master",
123+
logger: dummyLog(),
124+
getCurrentVersion: () => "1.2.3",
125+
prefixRelease: (v: string) => v,
126+
git: {
127+
getLatestRelease: () => "1.0.0",
128+
getLastTagNotInBaseBranch: () => "1.2.3",
129+
},
130+
} as unknown) as Auto.Auto);
131+
132+
readResult = `
133+
{
134+
"name": "test",
135+
"version": "1.2.4-next.0",
136+
"private": true
137+
}
138+
`;
139+
140+
expect(
141+
await hooks.next.promise([], Auto.SEMVER.patch, {} as any)
142+
).toStrictEqual(["1.2.4-next.0"]);
143+
144+
expect(execPromise).toHaveBeenCalledWith("git", [
145+
"push",
146+
"origin",
147+
"next",
148+
"--tags",
149+
]);
150+
expect(execPromise).not.toHaveBeenCalledWith("npm", [
151+
"publish",
152+
"--tag",
153+
"next",
154+
]);
155+
});
156+
114157
test("works with legacy auth", async () => {
115158
process.env.NPM_TOKEN = "abcd";
116159
const plugin = new NPMPlugin({ legacyAuth: true });

plugins/npm/__tests__/npm.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,14 @@ describe("publish", () => {
811811
`;
812812

813813
await hooks.publish.promise(Auto.SEMVER.patch);
814-
expect(execPromise).toHaveBeenCalledWith("npm", ["publish"]);
814+
expect(execPromise).not.toHaveBeenCalledWith("npm", ["publish"]);
815+
expect(execPromise).toHaveBeenCalledWith("git", [
816+
"push",
817+
"--follow-tags",
818+
"--set-upstream",
819+
"origin",
820+
"master",
821+
]);
815822
});
816823
});
817824

@@ -848,6 +855,37 @@ describe("canary", () => {
848855
expect(execPromise.mock.calls[1][1]).toContain("1.2.4-canary.123.1.0");
849856
});
850857

858+
test("doesn't publish private packages", async () => {
859+
const plugin = new NPMPlugin();
860+
const hooks = makeHooks();
861+
862+
plugin.apply(({
863+
config: { prereleaseBranches: ["next"] },
864+
hooks,
865+
remote: "origin",
866+
baseBranch: "master",
867+
logger: dummyLog(),
868+
getCurrentVersion: () => "1.2.3",
869+
git: {
870+
getLatestRelease: () => "1.2.3",
871+
getLatestTagInBranch: () => Promise.resolve("1.2.3"),
872+
},
873+
} as unknown) as Auto.Auto);
874+
875+
readResult = `
876+
{
877+
"name": "test",
878+
"private": true
879+
}
880+
`;
881+
882+
expect(
883+
await hooks.canary.promise(Auto.SEMVER.patch, "canary.123.1")
884+
).toStrictEqual({
885+
error: "Package private, cannot make canary release to npm.",
886+
});
887+
});
888+
851889
test("finds available canary version", async () => {
852890
const plugin = new NPMPlugin();
853891
const hooks = makeHooks();

plugins/npm/__tests__/set-npm-token.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as utils from "../src/utils";
77
const loadPackageJson = utils.loadPackageJson as jest.Mock;
88
const readFile = utils.readFile as jest.Mock;
99
const writeFile = utils.writeFile as jest.Mock;
10+
const isMonorepo = utils.isMonorepo as jest.Mock;
1011

1112
jest.mock("../src/utils.ts");
1213
jest.mock("env-ci", () => () => ({
@@ -31,6 +32,23 @@ describe("set npm token", () => {
3132
);
3233
});
3334

35+
test("should not write a new npmrc for single private package", async () => {
36+
loadPackageJson.mockReturnValueOnce({ name: "test", private: true });
37+
isMonorepo.mockReturnValueOnce(false);
38+
39+
await setNpmToken(dummyLog());
40+
expect(writeFile).not.toHaveBeenCalled()
41+
});
42+
43+
44+
test("should write a npmrc for monorepo", async () => {
45+
loadPackageJson.mockReturnValueOnce({ name: "test", private: true });
46+
isMonorepo.mockReturnValueOnce(true);
47+
48+
await setNpmToken(dummyLog());
49+
expect(writeFile).toHaveBeenCalled()
50+
});
51+
3452
test("should write a new npmrc w/o name", async () => {
3553
loadPackageJson.mockReturnValueOnce({});
3654
await setNpmToken(dummyLog());
@@ -52,7 +70,7 @@ describe("set npm token", () => {
5270
);
5371
});
5472

55-
test("should use registry for scoped pacakged", async () => {
73+
test("should use registry for scoped packaged", async () => {
5674
loadPackageJson.mockReturnValueOnce({
5775
name: "@scope/test",
5876
});

plugins/npm/src/index.ts

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,11 @@ import { gt, gte, inc, ReleaseType } from "semver";
2626

2727
import getConfigFromPackageJson from "./package-config";
2828
import setTokenOnCI from "./set-npm-token";
29-
import { loadPackageJson, writeFile } from "./utils";
29+
import { loadPackageJson, writeFile, isMonorepo } from "./utils";
3030

3131
const { isCi } = envCi();
3232
const VERSION_COMMIT_MESSAGE = '"Bump version to: %s [skip ci]"';
3333

34-
/** Check if the project is a monorepo */
35-
const isMonorepo = () => fs.existsSync("lerna.json");
36-
3734
/** Get the last published version for a npm package */
3835
async function getPublishedVersion(name: string) {
3936
try {
@@ -527,6 +524,12 @@ export default class NPMPlugin implements IPlugin {
527524
return;
528525
}
529526

527+
const { private: isPrivate } = await loadPackageJson();
528+
529+
if (isPrivate) {
530+
return;
531+
}
532+
530533
auto.checkEnv(this.name, "NPM_TOKEN");
531534
});
532535

@@ -810,7 +813,14 @@ export default class NPMPlugin implements IPlugin {
810813

811814
auto.logger.verbose.info("Detected single npm package");
812815
const current = await auto.getCurrentVersion(lastRelease);
813-
const { name } = await loadPackageJson();
816+
const { name, private: isPrivate } = await loadPackageJson();
817+
818+
if (isPrivate) {
819+
return {
820+
error: "Package private, cannot make canary release to npm.",
821+
};
822+
}
823+
814824
let canaryVersion = determineNextVersion(
815825
lastRelease,
816826
current,
@@ -941,21 +951,27 @@ export default class NPMPlugin implements IPlugin {
941951
...verboseArgs,
942952
]);
943953

944-
const { version } = await loadPackageJson();
954+
const { version, private: isPrivate } = await loadPackageJson();
945955
await execPromise("git", [
946956
"tag",
947957
auto.prefixRelease(version!),
948958
"-m",
949959
`"Update version to ${version}"`,
950960
]);
951961

952-
await execPromise("npm", [
953-
"publish",
954-
"--tag",
955-
prereleaseBranch,
956-
...verboseArgs,
957-
...getLegacyAuthArgs(this.legacyAuth),
958-
]);
962+
if (isPrivate) {
963+
auto.logger.log.info(
964+
`Package private, skipping prerelease publish to npm.`
965+
);
966+
} else {
967+
await execPromise("npm", [
968+
"publish",
969+
"--tag",
970+
prereleaseBranch,
971+
...verboseArgs,
972+
...getLegacyAuthArgs(this.legacyAuth),
973+
]);
974+
}
959975

960976
auto.logger.verbose.info("Successfully published next version");
961977
preReleaseVersions.push(auto.prefixRelease(version!));
@@ -1005,12 +1021,18 @@ export default class NPMPlugin implements IPlugin {
10051021
...getLegacyAuthArgs(this.legacyAuth, { isMonorepo: true }),
10061022
]);
10071023
} else {
1008-
await execPromise("npm", [
1009-
"publish",
1010-
...tag,
1011-
...verboseArgs,
1012-
...getLegacyAuthArgs(this.legacyAuth),
1013-
]);
1024+
const { private: isPrivate } = await loadPackageJson();
1025+
1026+
if (isPrivate) {
1027+
auto.logger.log.info(`Package private, skipping publish to npm.`);
1028+
} else {
1029+
await execPromise("npm", [
1030+
"publish",
1031+
...tag,
1032+
...verboseArgs,
1033+
...getLegacyAuthArgs(this.legacyAuth),
1034+
]);
1035+
}
10141036
}
10151037

10161038
await execPromise("git", [

plugins/npm/src/set-npm-token.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import registryUrl from "registry-url";
55
import urlJoin from "url-join";
66
import userHome from "user-home";
77

8-
import { loadPackageJson, readFile, writeFile } from "./utils";
8+
import { loadPackageJson, readFile, writeFile, isMonorepo } from "./utils";
99

1010
const { isCi } = envCi();
1111

@@ -15,7 +15,13 @@ export default async function setTokenOnCI(logger: ILogger) {
1515
return;
1616
}
1717

18-
const { publishConfig = {}, name } = await loadPackageJson();
18+
const { publishConfig = {}, name, private: isPrivate } = await loadPackageJson();
19+
20+
if (isPrivate && !isMonorepo()) {
21+
logger.verbose.info('NPM token not set for private package.')
22+
return;
23+
}
24+
1925
const rc = path.join(userHome, ".npmrc");
2026
let contents = "";
2127

plugins/npm/src/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ export const writeFile = promisify(fs.writeFile);
99
export async function loadPackageJson(root = "./"): Promise<IPackageJSON> {
1010
return JSON.parse(await readFile(path.join(root, "package.json"), "utf-8"));
1111
}
12+
13+
/** Check if the project is a monorepo */
14+
export const isMonorepo = () => fs.existsSync("lerna.json");

0 commit comments

Comments
 (0)