semantic-release-lerna 0.2.2 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,6 +28,15 @@ As of now the following features from `@semantic-release/npm` is not supported/i
28
28
  | `prepare` | Update the `package.json` version and [create](https://docs.npmjs.com/cli/pack) the npm package tarball. |
29
29
  | `publish` | [Publish the npm package](https://docs.npmjs.com/cli/publish) to the registry. |
30
30
 
31
+ ## Dependencies
32
+
33
+ If a package version is bumped all the packages depending (`dependencies`, `devDependencies` and `peerDependencies`) on it will also have the range updated if the range has one of the following formats:
34
+
35
+ - `1.2.3`
36
+ - `^1.2.3`
37
+ - `^1.2`
38
+ - `^1`
39
+
31
40
  ## Install
32
41
 
33
42
  ```bash
@@ -27,7 +27,7 @@ const { Project } = require("@lerna/project");
27
27
  *
28
28
  * @returns {String} The changelog for all the commits in `context.commits`.
29
29
  */
30
- /* eslint-disable-next-line complexity */
30
+ /* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
31
31
  async function generateNotes(pluginConfig, context) {
32
32
  const { commits, lastRelease, nextRelease, options, cwd, logger } = context;
33
33
  const { generateNotes = false } = pluginConfig;
@@ -58,12 +58,15 @@ async function generateNotes(pluginConfig, context) {
58
58
  }
59
59
 
60
60
  const [match, auth, host, path] =
61
+ /* eslint-disable-next-line security/detect-unsafe-regex */
61
62
  /^(?!.+:\/\/)(?:(?<auth>.*)@)?(?<host>.*?):(?<path>.*)$/.exec(repositoryUrl) || [];
62
- const url = new URL(match ? `ssh://${auth ? `${auth}@` : ""}${host}/${path}` : repositoryUrl);
63
+ const authString = auth ? `${auth}@` : "";
64
+ const url = new URL(match ? `ssh://${authString}${host}/${path}` : repositoryUrl);
63
65
  const { hostname, pathname } = url;
64
66
  let { port, protocol } = url;
65
67
  port = protocol.includes("ssh") ? "" : port;
66
68
  protocol = protocol && /http[^s]/.test(protocol) ? "http" : "https";
69
+ /* eslint-disable-next-line security/detect-unsafe-regex */
67
70
  const [, owner, repository] = /^\/(?<owner>[^/]+)?\/?(?<repository>.+)?$/.exec(pathname);
68
71
 
69
72
  const { issue, commit, referenceActions, issuePrefixes } =
@@ -37,8 +37,9 @@ function parse(stdout, options = {}) {
37
37
  return { refCount, sha, isDirty: Boolean(isDirty) };
38
38
  }
39
39
 
40
- const [, lastTagName, lastVersion, refCount, sha, isDirty] =
41
- /^((?:.*@)?(.*))-(\d+)-g([\da-f]+)(-dirty)?$/.exec(stdout) || [];
40
+ /* eslint-disable-next-line security/detect-unsafe-regex */
41
+ const result = /^((?:.*@)?(.*))-(\d+)-g([\da-f]+)(-dirty)?$/.exec(stdout) || [];
42
+ const [, lastTagName, lastVersion, refCount, sha, isDirty] = result;
42
43
 
43
44
  return { lastTagName, lastVersion, refCount, sha, isDirty: Boolean(isDirty) };
44
45
  }
@@ -55,7 +56,7 @@ function collectUpdates(filteredPackages, packageGraph, execOptions, commandOpti
55
56
 
56
57
  if (hasTags(execOptions)) {
57
58
  // Describe the last annotated tag in the current branch
58
- const { refCount, lastTagName } = describeRefSync(execOptions, false);
59
+ const { refCount, lastTagName } = describeRefSync(execOptions);
59
60
 
60
61
  if (refCount === "0" && !committish) {
61
62
  // No commits since previous release
@@ -109,9 +110,8 @@ async function getChangedPackages(latch, context) {
109
110
  { cwd },
110
111
  { logger, version, latch }
111
112
  );
112
- const changedProjects = updates.map((node) => packages.find((pkg) => pkg.name === node.name));
113
113
 
114
- return changedProjects;
114
+ return updates.map((node) => packages.find((pkg) => pkg.name === node.name));
115
115
  }
116
116
 
117
117
  module.exports = getChangedPackages;
package/lib/prepare.js CHANGED
@@ -1,10 +1,12 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs").promises;
3
+ const { existsSync } = require("fs");
3
4
  const { format } = require("util");
4
5
  const execa = require("execa");
5
6
  const { Project } = require("@lerna/project");
6
7
  const { Package } = require("@lerna/package");
7
8
  const writeJsonFile = require("write-json-file");
9
+ const semverParse = require("semver/functions/parse");
8
10
  const getChangedPackages = require("./get-changed-packages");
9
11
 
10
12
  /**
@@ -12,6 +14,7 @@ const getChangedPackages = require("./get-changed-packages");
12
14
  * @returns {any}
13
15
  **/
14
16
  async function readJson(path) {
17
+ /* eslint-disable-next-line security/detect-non-literal-fs-filename */
15
18
  return JSON.parse(await fs.readFile(path));
16
19
  }
17
20
 
@@ -73,6 +76,39 @@ async function updatePackage(npmrc, pkg, context, currentVersions) {
73
76
  }
74
77
  }
75
78
 
79
+ /**
80
+ * Bump version in a single package "package-lock.json".
81
+ *
82
+ * Noop if "package-lock.json" does not exist.
83
+ *
84
+ * @param {string} npmrc
85
+ * @param {Package} pkg
86
+ * @param {any} context
87
+ * @returns {Promise<void>}
88
+ */
89
+ async function updateLockfile(npmrc, pkg, context) {
90
+ const { env, stdout, stderr, logger } = context;
91
+
92
+ const lockfile = path.join(pkg.location, "package-lock.json");
93
+ if (!existsSync(lockfile)) {
94
+ return;
95
+ }
96
+
97
+ logger.log("Update package-lock.json in %s", pkg.location);
98
+
99
+ const versionResult = execa(
100
+ "npm",
101
+ ["install", "--package-lock-only", "--ignore-scripts", "--no-audit", "--userconfig", npmrc],
102
+ {
103
+ cwd: pkg.location,
104
+ env,
105
+ }
106
+ );
107
+ versionResult.stdout.pipe(stdout, { end: false });
108
+ versionResult.stderr.pipe(stderr, { end: false });
109
+ await versionResult;
110
+ }
111
+
76
112
  /**
77
113
  * @param {Record<string, string>} dependencies
78
114
  * @param {string} newVersion
@@ -80,22 +116,40 @@ async function updatePackage(npmrc, pkg, context, currentVersions) {
80
116
  * @returns {void}
81
117
  */
82
118
  function bumpDependency(dependencies, newVersion, currentVersions) {
119
+ const newParsed = semverParse(newVersion);
120
+ if (!newParsed) {
121
+ return;
122
+ }
83
123
  for (const [dep, range] of Object.entries(dependencies)) {
84
124
  if (!currentVersions[dep]) {
85
125
  continue;
86
126
  }
87
127
 
88
128
  const version = currentVersions[dep];
129
+ const parsed = semverParse(version);
130
+ if (!parsed) {
131
+ continue;
132
+ }
89
133
 
90
134
  /* Exact versions */
91
135
  if (range === version) {
92
136
  dependencies[dep] = newVersion;
93
137
  }
94
138
 
95
- /* Hat ^ */
139
+ /* Hat ^x.y.z */
96
140
  if (range === `^${version}`) {
97
141
  dependencies[dep] = `^${newVersion}`;
98
142
  }
143
+
144
+ /* Hat ^x.y */
145
+ if (range === `^${parsed.major}.${parsed.minor}`) {
146
+ dependencies[dep] = `^${newParsed.major}.${newParsed.minor}`;
147
+ }
148
+
149
+ /* Hat ^x */
150
+ if (range === `^${parsed.major}`) {
151
+ dependencies[dep] = `^${newParsed.major}`;
152
+ }
99
153
  }
100
154
  }
101
155
 
@@ -167,4 +221,7 @@ module.exports = async (npmrc, pluginConfig, context) => {
167
221
  /* Bump version in "lerna.json" */
168
222
  await updateLernaJson(basePath, context);
169
223
  await updatePackage(npmrc, rootPkg, context);
224
+
225
+ /* Bump version in "package-lock.json" */
226
+ await updateLockfile(npmrc, rootPkg, context);
170
227
  };
@@ -0,0 +1,439 @@
1
+ /* eslint-env jest */
2
+ const path = require("path");
3
+ const { outputJson, readJson } = require("fs-extra");
4
+ const tempy = require("tempy");
5
+ const execa = require("execa");
6
+ const { WritableStreamBuffer } = require("stream-buffers");
7
+ const prepare = require("./prepare");
8
+
9
+ let context;
10
+ let mockChangedPackages;
11
+
12
+ jest.mock("../lib/get-changed-packages", () => {
13
+ function getChangedPackagesMock() {
14
+ return mockChangedPackages;
15
+ }
16
+
17
+ return getChangedPackagesMock;
18
+ });
19
+
20
+ async function createProject(cwd, version, pkgData) {
21
+ const lernaPath = path.resolve(cwd, "lerna.json");
22
+ const manifestLocation = path.resolve(cwd, "package.json");
23
+ await outputJson(lernaPath, { version, packages: ["packages/*"] });
24
+ await outputJson(manifestLocation, { name: "root-pkg", version, ...pkgData });
25
+ return {
26
+ lernaPath,
27
+ manifestLocation,
28
+ };
29
+ }
30
+
31
+ /* eslint-disable-next-line max-params */
32
+ async function createPackage(cwd, name, version, options, pkgData) {
33
+ const { changed = false } = options;
34
+ const pkgRoot = `packages/${name}`;
35
+ const location = path.resolve(cwd, pkgRoot);
36
+ const manifestLocation = path.resolve(cwd, pkgRoot, "package.json");
37
+ const pkg = {
38
+ name,
39
+ location,
40
+ manifestLocation,
41
+ shrinkwrapPath: path.resolve(cwd, pkgRoot, "npm-shrinkwrap.json"),
42
+ lockfilePath: path.resolve(cwd, pkgRoot, "package-lock.json"),
43
+ };
44
+ await outputJson(manifestLocation, { name, version, ...pkgData });
45
+ if (changed) {
46
+ mockChangedPackages.push(pkg);
47
+ }
48
+
49
+ return pkg;
50
+ }
51
+
52
+ beforeEach(() => {
53
+ const log = jest.fn();
54
+ context = {
55
+ log,
56
+ logger: { log },
57
+ stdout: new WritableStreamBuffer(),
58
+ stderr: new WritableStreamBuffer(),
59
+ };
60
+ mockChangedPackages = [];
61
+ });
62
+
63
+ it("Update lerna.json and root package.json when no package has changed", async () => {
64
+ expect.assertions(4);
65
+ const cwd = tempy.directory();
66
+ const npmrc = tempy.file({ name: ".npmrc" });
67
+ const project = await createProject(cwd, "0.0.0");
68
+
69
+ await prepare(
70
+ npmrc,
71
+ {},
72
+ {
73
+ cwd,
74
+ env: {},
75
+ stdout: context.stdout,
76
+ stderr: context.stderr,
77
+ nextRelease: { version: "1.0.0" },
78
+ logger: context.logger,
79
+ }
80
+ );
81
+
82
+ // Verify lerna.json has been updated
83
+ expect(await readJson(project.lernaPath)).toEqual(
84
+ expect.objectContaining({
85
+ version: "1.0.0",
86
+ })
87
+ );
88
+
89
+ // Verify root package.json has been updated
90
+ expect(await readJson(project.manifestLocation)).toEqual(
91
+ expect.objectContaining({
92
+ version: "1.0.0",
93
+ })
94
+ );
95
+
96
+ // Verify the logger has been called with the version updated
97
+ expect(context.log).toHaveBeenCalledWith(
98
+ "No packages changed, applying version bump on root package only"
99
+ );
100
+ expect(context.log).toHaveBeenCalledWith("Write version %s to lerna.json in %s", "1.0.0", cwd);
101
+ });
102
+
103
+ it("Update lerna.json and root package.json when one or more package has changed", async () => {
104
+ expect.assertions(5);
105
+ const cwd = tempy.directory();
106
+ const npmrc = tempy.file({ name: ".npmrc" });
107
+ const project = await createProject(cwd, "0.0.0");
108
+ const pkg = await createPackage(cwd, "foo", "0.0.0", {
109
+ changed: true,
110
+ });
111
+
112
+ await prepare(
113
+ npmrc,
114
+ {},
115
+ {
116
+ cwd,
117
+ env: {},
118
+ stdout: context.stdout,
119
+ stderr: context.stderr,
120
+ nextRelease: { version: "1.0.0" },
121
+ logger: context.logger,
122
+ }
123
+ );
124
+
125
+ // Verify lerna.json has been updated
126
+ expect(await readJson(project.lernaPath)).toEqual(
127
+ expect.objectContaining({
128
+ version: "1.0.0",
129
+ })
130
+ );
131
+
132
+ // Verify package.json has been updated
133
+ expect(await readJson(project.manifestLocation)).toEqual(
134
+ expect.objectContaining({
135
+ version: "1.0.0",
136
+ })
137
+ );
138
+
139
+ // Verify the logger has been called with the version updated
140
+ expect(context.log).toHaveBeenCalledWith("1 package need version bump: [ 'foo' ]");
141
+ expect(context.log).toHaveBeenCalledWith(
142
+ "Write version %s to package.json in %s",
143
+ "1.0.0",
144
+ pkg.location
145
+ );
146
+ expect(context.log).toHaveBeenCalledWith("Write version %s to lerna.json in %s", "1.0.0", cwd);
147
+ });
148
+
149
+ it("Update package.json in changed packages", async () => {
150
+ expect.assertions(2);
151
+ const cwd = tempy.directory();
152
+ const npmrc = tempy.file({ name: ".npmrc" });
153
+ await createProject(cwd, "0.0.0");
154
+ const foo = await createPackage(cwd, "foo", "0.0.0", {
155
+ changed: true,
156
+ });
157
+ const bar = await createPackage(cwd, "bar", "0.0.0", {
158
+ changed: false,
159
+ });
160
+
161
+ await prepare(
162
+ npmrc,
163
+ {},
164
+ {
165
+ cwd,
166
+ env: {},
167
+ stdout: context.stdout,
168
+ stderr: context.stderr,
169
+ nextRelease: { version: "1.0.0" },
170
+ logger: context.logger,
171
+ }
172
+ );
173
+
174
+ // Verify foo/package.json has been updated
175
+ expect(await readJson(foo.manifestLocation)).toEqual({
176
+ name: "foo",
177
+ version: "1.0.0",
178
+ });
179
+
180
+ // Verify bar/package.json has not been updated
181
+ expect(await readJson(bar.manifestLocation)).toEqual({
182
+ name: "bar",
183
+ version: "0.0.0",
184
+ });
185
+ });
186
+
187
+ it("Update npm-shrinkwrap.json if present", async () => {
188
+ expect.assertions(2);
189
+ const cwd = tempy.directory();
190
+ const npmrc = tempy.file({ name: ".npmrc" });
191
+ await createProject(cwd, "0.0.0");
192
+ const pkg = await createPackage(cwd, "foo", "0.0.0", {
193
+ changed: true,
194
+ });
195
+ // Create a npm-shrinkwrap.json file
196
+ await execa("npm", ["shrinkwrap"], { cwd: pkg.location });
197
+
198
+ await prepare(
199
+ npmrc,
200
+ {},
201
+ {
202
+ cwd,
203
+ env: {},
204
+ stdout: context.stdout,
205
+ stderr: context.stderr,
206
+ nextRelease: { version: "1.0.0" },
207
+ logger: context.logger,
208
+ }
209
+ );
210
+
211
+ // Verify foo/package.json has been updated
212
+ expect(await readJson(pkg.manifestLocation)).toEqual({
213
+ name: "foo",
214
+ version: "1.0.0",
215
+ });
216
+
217
+ // Verify foo/npm-shrinkwrap.json has been updated
218
+ expect(await readJson(pkg.shrinkwrapPath)).toEqual(
219
+ expect.objectContaining({
220
+ lockfileVersion: expect.anything(),
221
+ name: "foo",
222
+ version: "1.0.0",
223
+ })
224
+ );
225
+ });
226
+
227
+ it("Update package-lock.json if present", async () => {
228
+ expect.assertions(2);
229
+ const cwd = tempy.directory();
230
+ const npmrc = tempy.file({ name: ".npmrc" });
231
+ await createProject(cwd, "0.0.0");
232
+ const pkg = await createPackage(cwd, "foo", "0.0.0", {
233
+ changed: true,
234
+ });
235
+ // Create a package-lock.json file
236
+ await execa("npm", ["install"], { cwd: pkg.location });
237
+
238
+ await prepare(
239
+ npmrc,
240
+ {},
241
+ {
242
+ cwd,
243
+ env: {},
244
+ stdout: context.stdout,
245
+ stderr: context.stderr,
246
+ nextRelease: { version: "1.0.0" },
247
+ logger: context.logger,
248
+ }
249
+ );
250
+
251
+ // Verify foo/package.json has been updated
252
+ expect(await readJson(pkg.manifestLocation)).toEqual({
253
+ name: "foo",
254
+ version: "1.0.0",
255
+ });
256
+
257
+ // Verify foo/package-lock.json has been updated
258
+ expect(await readJson(pkg.lockfilePath)).toEqual(
259
+ expect.objectContaining({
260
+ lockfileVersion: expect.anything(),
261
+ name: "foo",
262
+ version: "1.0.0",
263
+ })
264
+ );
265
+ });
266
+
267
+ it("Update package.json dependency when using exact version", async () => {
268
+ expect.assertions(1);
269
+ const cwd = tempy.directory();
270
+ const npmrc = tempy.file({ name: ".npmrc" });
271
+ await createProject(cwd, "0.0.0");
272
+ const foo = await createPackage(
273
+ cwd,
274
+ "foo",
275
+ "0.0.0",
276
+ {
277
+ changed: true,
278
+ },
279
+ {
280
+ dependencies: {
281
+ a: "0.0.0",
282
+ },
283
+ devDependencies: {
284
+ b: "0.0.0",
285
+ },
286
+ peerDependencies: {
287
+ c: "0.0.0",
288
+ },
289
+ }
290
+ );
291
+ await createPackage(cwd, "a", "0.0.0", { changed: true });
292
+ await createPackage(cwd, "b", "0.0.0", { changed: true });
293
+ await createPackage(cwd, "c", "0.0.0", { changed: true });
294
+
295
+ await prepare(
296
+ npmrc,
297
+ {},
298
+ {
299
+ cwd,
300
+ env: {},
301
+ stdout: context.stdout,
302
+ stderr: context.stderr,
303
+ nextRelease: { version: "0.0.1" },
304
+ logger: context.logger,
305
+ }
306
+ );
307
+
308
+ // Verify dependency has been updated
309
+ expect(await readJson(foo.manifestLocation)).toEqual({
310
+ name: "foo",
311
+ version: "0.0.1",
312
+ dependencies: {
313
+ a: "0.0.1",
314
+ },
315
+ devDependencies: {
316
+ b: "0.0.1",
317
+ },
318
+ peerDependencies: {
319
+ c: "0.0.1",
320
+ },
321
+ });
322
+ });
323
+
324
+ it("Update package.json dependency when using hat", async () => {
325
+ expect.assertions(1);
326
+ const cwd = tempy.directory();
327
+ const npmrc = tempy.file({ name: ".npmrc" });
328
+ await createProject(cwd, "0.1.2");
329
+ const foo = await createPackage(
330
+ cwd,
331
+ "foo",
332
+ "0.1.2",
333
+ {
334
+ changed: true,
335
+ },
336
+ {
337
+ dependencies: {
338
+ a: "^0.1.2",
339
+ b: "^0.1",
340
+ c: "^0",
341
+ },
342
+ }
343
+ );
344
+
345
+ await createPackage(cwd, "a", "0.1.2", { changed: true });
346
+ await createPackage(cwd, "b", "0.1.2", { changed: true });
347
+ await createPackage(cwd, "c", "0.1.2", { changed: true });
348
+
349
+ await prepare(
350
+ npmrc,
351
+ {},
352
+ {
353
+ cwd,
354
+ env: {},
355
+ stdout: context.stdout,
356
+ stderr: context.stderr,
357
+ nextRelease: { version: "1.0.0" },
358
+ logger: context.logger,
359
+ }
360
+ );
361
+
362
+ // Verify dependency has been updated
363
+ expect(await readJson(foo.manifestLocation)).toEqual({
364
+ name: "foo",
365
+ version: "1.0.0",
366
+ dependencies: {
367
+ a: "^1.0.0",
368
+ b: "^1.0",
369
+ c: "^1",
370
+ },
371
+ });
372
+ });
373
+
374
+ it("Should not update other dependencies", async () => {
375
+ expect.assertions(1);
376
+ const cwd = tempy.directory();
377
+ const npmrc = tempy.file({ name: ".npmrc" });
378
+ await createProject(cwd, "0.0.0");
379
+ const foo = await createPackage(
380
+ cwd,
381
+ "foo",
382
+ "0.0.0",
383
+ {
384
+ changed: true,
385
+ },
386
+ {
387
+ dependencies: {
388
+ a: "0.0.0",
389
+ },
390
+ }
391
+ );
392
+
393
+ await prepare(
394
+ npmrc,
395
+ {},
396
+ {
397
+ cwd,
398
+ env: {},
399
+ stdout: context.stdout,
400
+ stderr: context.stderr,
401
+ nextRelease: { version: "0.0.1" },
402
+ logger: context.logger,
403
+ }
404
+ );
405
+
406
+ // Verify dependency has been updated
407
+ expect(await readJson(foo.manifestLocation)).toEqual({
408
+ name: "foo",
409
+ version: "0.0.1",
410
+ dependencies: {
411
+ a: "0.0.0",
412
+ },
413
+ });
414
+ });
415
+
416
+ it("Handle dependencies from root package", async () => {
417
+ expect.assertions(1);
418
+ const cwd = tempy.directory();
419
+ const npmrc = tempy.file({ name: ".npmrc" });
420
+ await createProject(cwd, "0.0.1", {
421
+ devDependencies: {
422
+ "external-dependency": "1.2.3",
423
+ },
424
+ });
425
+ expect(async () => {
426
+ await prepare(
427
+ npmrc,
428
+ {},
429
+ {
430
+ cwd,
431
+ env: {},
432
+ stdout: context.stdout,
433
+ stderr: context.stderr,
434
+ nextRelease: { version: "0.0.2" },
435
+ logger: context.logger,
436
+ }
437
+ );
438
+ }).not.toThrow();
439
+ });
package/lib/publish.js CHANGED
@@ -39,12 +39,13 @@ module.exports = async (npmrc, config, pkg, context) => {
39
39
  "node",
40
40
  // prettier-ignore
41
41
  [
42
- lerna, 'publish', 'from-package',
43
- '--loglevel', 'verbose',
44
- '--yes',
45
- '--dist-tag', distTag,
46
- '--registry', registry,
47
- ...legacyAuth,
42
+ lerna, 'publish', 'from-package',
43
+ '--loglevel', 'verbose',
44
+ '--yes',
45
+ '--no-verify-access', // prepare step has already verify access and lerna doesn't properly pass authentication
46
+ '--dist-tag', distTag,
47
+ '--registry', registry,
48
+ ...legacyAuth,
48
49
  ],
49
50
  {
50
51
  cwd,
@@ -0,0 +1,58 @@
1
+ /* eslint-env jest */
2
+
3
+ const semver = require("semver");
4
+ const shouldLatch = require("./should-latch");
5
+
6
+ const version = "1.0.0";
7
+
8
+ describe("latch none", () => {
9
+ it.each`
10
+ bump | result | version | next
11
+ ${"major"} | ${false} | ${version} | ${semver.inc(version, "major")}
12
+ ${"minor"} | ${false} | ${version} | ${semver.inc(version, "minor")}
13
+ ${"patch"} | ${false} | ${version} | ${semver.inc(version, "patch")}
14
+ ${"prerelease"} | ${false} | ${version} | ${semver.inc(version, "prerelease")}
15
+ `("should return $result when version bump is $bump ($version -> $next)", ({ result, next }) => {
16
+ expect.assertions(1);
17
+ expect(shouldLatch(next, "none")).toBe(result);
18
+ });
19
+ });
20
+
21
+ describe("latch major", () => {
22
+ it.each`
23
+ bump | result | version | next
24
+ ${"major"} | ${true} | ${version} | ${semver.inc(version, "major")}
25
+ ${"minor"} | ${false} | ${version} | ${semver.inc(version, "minor")}
26
+ ${"patch"} | ${false} | ${version} | ${semver.inc(version, "patch")}
27
+ ${"prerelease"} | ${false} | ${version} | ${semver.inc(version, "prerelease")}
28
+ `("should return $result when version bump is $bump ($version -> $next)", ({ result, next }) => {
29
+ expect.assertions(1);
30
+ expect(shouldLatch(next, "major")).toBe(result);
31
+ });
32
+ });
33
+
34
+ describe("latch minor", () => {
35
+ it.each`
36
+ bump | result | version | next
37
+ ${"major"} | ${true} | ${version} | ${semver.inc(version, "major")}
38
+ ${"minor"} | ${true} | ${version} | ${semver.inc(version, "minor")}
39
+ ${"patch"} | ${false} | ${version} | ${semver.inc(version, "patch")}
40
+ ${"prerelease"} | ${false} | ${version} | ${semver.inc(version, "prerelease")}
41
+ `("should return $result when version bump is $bump ($version -> $next)", ({ result, next }) => {
42
+ expect.assertions(1);
43
+ expect(shouldLatch(next, "minor")).toBe(result);
44
+ });
45
+ });
46
+
47
+ describe("latch patch", () => {
48
+ it.each`
49
+ bump | result | version | next
50
+ ${"major"} | ${true} | ${version} | ${semver.inc(version, "major")}
51
+ ${"minor"} | ${true} | ${version} | ${semver.inc(version, "minor")}
52
+ ${"patch"} | ${true} | ${version} | ${semver.inc(version, "patch")}
53
+ ${"prerelease"} | ${false} | ${version} | ${semver.inc(version, "prerelease")}
54
+ `("should return $result when version bump is $bump ($version -> $next)", ({ result, next }) => {
55
+ expect.assertions(1);
56
+ expect(shouldLatch(next, "patch")).toBe(result);
57
+ });
58
+ });
@@ -0,0 +1,49 @@
1
+ /* eslint-env jest */
2
+
3
+ jest.mock("execa");
4
+
5
+ const execa = require("execa");
6
+ const verifyGit = require("./verify-git");
7
+
8
+ it("should return error if working copy is dirty", async () => {
9
+ expect.assertions(2);
10
+ execa.mockImplementation(() => ({ stdout: " M file.js\n" }));
11
+ const errors = await verifyGit({});
12
+ expect(errors).toHaveLength(1);
13
+ expect(errors[0]).toMatchObject({
14
+ code: "EDIRTYWC",
15
+ details: `The git working copy must be clean before releasing:
16
+
17
+ M file.js
18
+ `,
19
+ });
20
+ });
21
+
22
+ it("should return error when working copy has mixed dirty and untracked", async () => {
23
+ expect.assertions(2);
24
+ execa.mockImplementation(() => ({ stdout: " M file.js\n?? file.c" }));
25
+ const errors = await verifyGit({});
26
+ expect(errors).toHaveLength(1);
27
+ expect(errors[0]).toMatchObject({
28
+ code: "EDIRTYWC",
29
+ details: `The git working copy must be clean before releasing:
30
+
31
+ M file.js
32
+ ?? file.c
33
+ `,
34
+ });
35
+ });
36
+
37
+ it("should ignore untracked files", async () => {
38
+ expect.assertions(1);
39
+ execa.mockImplementation(() => ({ stdout: "?? file.js\n" }));
40
+ const errors = await verifyGit({});
41
+ expect(errors).toHaveLength(0);
42
+ });
43
+
44
+ it("should not return error if working copy is clean", async () => {
45
+ expect.assertions(1);
46
+ execa.mockImplementation(() => ({ stdout: "" }));
47
+ const errors = await verifyGit({});
48
+ expect(errors).toHaveLength(0);
49
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semantic-release-lerna",
3
- "version": "0.2.2",
3
+ "version": "0.4.2",
4
4
  "description": "semantic-release plugin to publish lerna monorepo packages to npm",
5
5
  "keywords": [
6
6
  "npm",
@@ -51,42 +51,41 @@
51
51
  "@lerna/package": "^4.0.0",
52
52
  "@lerna/package-graph": "^4.0.0",
53
53
  "@lerna/project": "^4.0.0",
54
- "@semantic-release/error": "^2.2.0",
55
- "@semantic-release/release-notes-generator": "^9.0.0",
56
- "aggregate-error": "^3.0.0",
57
- "conventional-changelog-writer": "^4.1.0",
54
+ "@semantic-release/error": "^3.0.0",
55
+ "@semantic-release/release-notes-generator": "^10.0.0",
56
+ "aggregate-error": "^3.1.0",
57
+ "conventional-changelog-writer": "^5.0.0",
58
58
  "conventional-commits-filter": "^2.0.0",
59
59
  "conventional-commits-parser": "^3.2.0",
60
60
  "debug": "^4.3.0",
61
61
  "execa": "^5.0.0",
62
62
  "get-stream": "^6.0.0",
63
- "into-stream": "^5.1.0",
63
+ "into-stream": "^6.0.0",
64
64
  "read-pkg-up": "^7.0.0",
65
+ "semver": "^7.0.0",
65
66
  "tempy": "^1.0.0",
66
67
  "write-json-file": "^4.0.0"
67
68
  },
68
69
  "devDependencies": {
69
- "@html-validate/eslint-config": "^4.1.0",
70
- "@html-validate/eslint-config-jest": "^4.0.0",
71
- "@html-validate/prettier-config": "^1.1.0",
72
- "@semantic-release/npm": "7.0.10",
73
- "@types/jest": "26.0.20",
74
- "codecov": "3.8.1",
75
- "eslint": "^7.22.0",
76
- "fs-extra": "9.1.0",
77
- "got": "11.8.2",
78
- "jest": "26.6.3",
70
+ "@html-validate/eslint-config": "5.3.1",
71
+ "@html-validate/eslint-config-jest": "5.3.1",
72
+ "@html-validate/prettier-config": "2.0.0",
73
+ "@semantic-release/npm": "9.0.0",
74
+ "@types/jest": "27.4.0",
75
+ "codecov": "3.8.3",
76
+ "fs-extra": "10.0.0",
77
+ "got": "11.8.3",
78
+ "jest": "27.4.7",
79
79
  "lerna": "4.0.0",
80
- "prettier": "^2.2.1",
81
- "semantic-release": "17.4.2",
82
- "semver": "7.3.4",
80
+ "prettier": "2.5.1",
81
+ "semantic-release": "19.0.2",
83
82
  "stream-buffers": "3.0.2",
84
- "verdaccio": "4.11.3"
83
+ "verdaccio": "5.4.0"
85
84
  },
86
85
  "peerDependencies": {
87
86
  "@semantic-release/npm": ">= 7",
88
87
  "lerna": "^3.2 || ^4",
89
- "semantic-release": ">=15.0.0 <18.0.0"
88
+ "semantic-release": ">=15.0.0 <20.0.0"
90
89
  },
91
90
  "engines": {
92
91
  "node": ">= 12.10"
@@ -96,7 +95,7 @@
96
95
  },
97
96
  "renovate": {
98
97
  "extends": [
99
- "@html-validate"
98
+ "gitlab>html-validate/renovate-config"
100
99
  ]
101
100
  }
102
101
  }