release-please 13.5.0 → 13.7.1

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/CHANGELOG.md CHANGED
@@ -4,6 +4,29 @@
4
4
 
5
5
  [1]: https://www.npmjs.com/package/release-please?activeTab=versions
6
6
 
7
+ ### [13.7.1](https://github.com/googleapis/release-please/compare/v13.7.0...v13.7.1) (2022-03-30)
8
+
9
+
10
+ ### Bug Fixes
11
+
12
+ * use lerna typings provided by DefinitelyTyped project ([#1351](https://github.com/googleapis/release-please/issues/1351)) ([86dccdf](https://github.com/googleapis/release-please/commit/86dccdf9dd2fbd8c517a88f4422412c18a944e07))
13
+
14
+ ## [13.7.0](https://github.com/googleapis/release-please/compare/v13.6.0...v13.7.0) (2022-03-24)
15
+
16
+
17
+ ### Features
18
+
19
+ * allow snoozing release pull requests ([#1345](https://github.com/googleapis/release-please/issues/1345)) ([ea69708](https://github.com/googleapis/release-please/commit/ea6970813a11cebe2394c19d8d006d352c8fb306)), closes [#1339](https://github.com/googleapis/release-please/issues/1339)
20
+
21
+ ## [13.6.0](https://github.com/googleapis/release-please/compare/v13.5.0...v13.6.0) (2022-03-16)
22
+
23
+
24
+ ### Features
25
+
26
+ * add linked-versions plugin for grouping components ([#1327](https://github.com/googleapis/release-please/issues/1327)) ([f398bdf](https://github.com/googleapis/release-please/commit/f398bdffdae69772c61a82cd7158cca3478c2110))
27
+ * introduce generic json updater ([#1332](https://github.com/googleapis/release-please/issues/1332)) ([ecbfcf0](https://github.com/googleapis/release-please/commit/ecbfcf03f7854ced3ace5eafd95f7872ddee1d14))
28
+ * introduce generic xml updater ([#1337](https://github.com/googleapis/release-please/issues/1337)) ([02ef78b](https://github.com/googleapis/release-please/commit/02ef78b4d6a855236ff80fc139fd00a46f88d445))
29
+
7
30
  ## [13.5.0](https://github.com/googleapis/release-please/compare/v13.4.15...v13.5.0) (2022-03-08)
8
31
 
9
32
 
@@ -1,4 +1,4 @@
1
- import { ConventionalCommit } from './commit';
1
+ import { Commit, ConventionalCommit } from './commit';
2
2
  export interface BuildNotesOptions {
3
3
  host?: string;
4
4
  owner: string;
@@ -8,6 +8,7 @@ export interface BuildNotesOptions {
8
8
  currentTag: string;
9
9
  targetBranch: string;
10
10
  changelogSections?: ChangelogSection[];
11
+ commits?: Commit[];
11
12
  }
12
13
  export interface ChangelogNotes {
13
14
  buildNotes(commits: ConventionalCommit[], options: BuildNotesOptions): Promise<string>;
@@ -39,6 +39,7 @@ const node_workspace_1 = require("./plugins/node-workspace");
39
39
  const cargo_workspace_1 = require("./plugins/cargo-workspace");
40
40
  const github_1 = require("./changelog-notes/github");
41
41
  const default_2 = require("./changelog-notes/default");
42
+ const linked_versions_1 = require("./plugins/linked-versions");
42
43
  // Factory shared by GitHub Action and CLI for creating Release PRs
43
44
  // and GitHub Releases:
44
45
  // add any new releasers you create to this type as well as the `releasers`
@@ -191,6 +192,14 @@ function buildVersioningStrategy(options) {
191
192
  }
192
193
  }
193
194
  function buildPlugin(options) {
195
+ if (typeof options.type === 'object') {
196
+ switch (options.type.type) {
197
+ case 'linked-versions':
198
+ return new linked_versions_1.LinkedVersions(options.github, options.targetBranch, options.repositoryConfig, options.type.groupName, options.type.components);
199
+ default:
200
+ throw new Error(`Unknown plugin type: ${options.type}`);
201
+ }
202
+ }
194
203
  switch (options.type) {
195
204
  case 'cargo-workspace':
196
205
  return new cargo_workspace_1.CargoWorkspace(options.github, options.targetBranch, options.repositoryConfig, options);
@@ -5,6 +5,17 @@ import { PullRequest } from './pull-request';
5
5
  import { ReleasePullRequest } from './release-pull-request';
6
6
  import { ReleaseType, VersioningStrategyType, ChangelogNotesType } from './factory';
7
7
  import { Release } from './release';
8
+ declare type ExtraJsonFile = {
9
+ type: 'json';
10
+ path: string;
11
+ jsonpath: string;
12
+ };
13
+ declare type ExtraXmlFile = {
14
+ type: 'xml';
15
+ path: string;
16
+ xpath: string;
17
+ };
18
+ export declare type ExtraFile = string | ExtraJsonFile | ExtraXmlFile;
8
19
  /**
9
20
  * These are configurations provided to each strategy per-path.
10
21
  */
@@ -27,7 +38,7 @@ export interface ReleaserConfig {
27
38
  changelogPath?: string;
28
39
  changelogType?: ChangelogNotesType;
29
40
  versionFile?: string;
30
- extraFiles?: string[];
41
+ extraFiles?: ExtraFile[];
31
42
  }
32
43
  export interface CandidateReleasePullRequest {
33
44
  path: string;
@@ -80,7 +91,13 @@ interface ReleaserPackageConfig extends ReleaserConfigJson {
80
91
  component?: string;
81
92
  'changelog-path'?: string;
82
93
  }
83
- export declare type PluginType = 'node-workspace' | 'cargo-workspace';
94
+ declare type DirectPluginType = 'node-workspace' | 'cargo-workspace';
95
+ interface LinkedVersionPluginConfig {
96
+ type: 'linked-versions';
97
+ groupName: string;
98
+ components: string[];
99
+ }
100
+ export declare type PluginType = DirectPluginType | LinkedVersionPluginConfig;
84
101
  /**
85
102
  * This is the schema of the manifest config json
86
103
  */
@@ -206,7 +223,11 @@ export declare class Manifest {
206
223
  * @returns {number[]} Pull request numbers of release pull requests
207
224
  */
208
225
  createPullRequests(): Promise<(PullRequest | undefined)[]>;
226
+ private findOpenReleasePullRequests;
227
+ private findSnoozedReleasePullRequests;
209
228
  private createOrUpdatePullRequest;
229
+ private maybeUpdateExistingPullRequest;
230
+ private maybeUpdateSnoozedPullRequest;
210
231
  private findMergedReleasePullRequests;
211
232
  /**
212
233
  * Find merged, untagged releases and build candidate releases to tag.
@@ -31,6 +31,7 @@ exports.ROOT_PROJECT_PATH = '.';
31
31
  const DEFAULT_COMPONENT_NAME = '';
32
32
  const DEFAULT_LABELS = ['autorelease: pending'];
33
33
  const DEFAULT_RELEASE_LABELS = ['autorelease: tagged'];
34
+ const SNOOZE_LABEL = 'autorelease: snooze';
34
35
  exports.MANIFEST_PULL_REQUEST_TITLE_PATTERN = 'chore: release ${branch}';
35
36
  class Manifest {
36
37
  /**
@@ -270,33 +271,54 @@ class Manifest {
270
271
  includeEmpty: true,
271
272
  packagePaths: Object.keys(this.repositoryConfig),
272
273
  });
273
- const commitsPerPath = cs.split(commits);
274
- let newReleasePullRequests = [];
274
+ const splitCommits = cs.split(commits);
275
+ // limit paths to ones since the last release
276
+ const commitsPerPath = {};
275
277
  for (const path in this.repositoryConfig) {
276
- const config = this.repositoryConfig[path];
277
- logger_1.logger.info(`Building candidate release pull request for path: ${path}`);
278
- logger_1.logger.debug(`type: ${config.releaseType}`);
279
- logger_1.logger.debug(`targetBranch: ${this.targetBranch}`);
280
- const pathCommits = commitsAfterSha(path === exports.ROOT_PROJECT_PATH ? commits : commitsPerPath[path], releaseShasByPath[path]);
281
- logger_1.logger.debug(`commits: ${pathCommits.length}`);
282
- const latestReleasePullRequest = releasePullRequestsBySha[releaseShasByPath[path]];
283
- if (!latestReleasePullRequest) {
284
- logger_1.logger.warn('No latest release pull request found.');
285
- }
286
- const strategy = strategiesByPath[path];
287
- let latestRelease = releasesByPath[path];
278
+ commitsPerPath[path] = commitsAfterSha(path === exports.ROOT_PROJECT_PATH ? commits : splitCommits[path], releaseShasByPath[path]);
279
+ }
280
+ // backfill latest release tags from manifest
281
+ for (const path in this.repositoryConfig) {
282
+ const latestRelease = releasesByPath[path];
288
283
  if (!latestRelease &&
289
284
  this.releasedVersions[path] &&
290
285
  this.releasedVersions[path].toString() !== '0.0.0') {
291
286
  const version = this.releasedVersions[path];
287
+ const strategy = strategiesByPath[path];
292
288
  const component = await strategy.getComponent();
293
289
  logger_1.logger.info(`No latest release found for path: ${path}, component: ${component}, but a previous version (${version.toString()}) was specified in the manifest.`);
294
- latestRelease = {
290
+ releasesByPath[path] = {
295
291
  tag: new tag_name_1.TagName(version, component),
296
292
  sha: '',
297
293
  notes: '',
298
294
  };
299
295
  }
296
+ }
297
+ // Build plugins
298
+ const plugins = this.plugins.map(pluginType => factory_1.buildPlugin({
299
+ type: pluginType,
300
+ github: this.github,
301
+ targetBranch: this.targetBranch,
302
+ repositoryConfig: this.repositoryConfig,
303
+ }));
304
+ let strategies = strategiesByPath;
305
+ for (const plugin of plugins) {
306
+ strategies = await plugin.preconfigure(strategies, commitsPerPath, releasesByPath);
307
+ }
308
+ let newReleasePullRequests = [];
309
+ for (const path in this.repositoryConfig) {
310
+ const config = this.repositoryConfig[path];
311
+ logger_1.logger.info(`Building candidate release pull request for path: ${path}`);
312
+ logger_1.logger.debug(`type: ${config.releaseType}`);
313
+ logger_1.logger.debug(`targetBranch: ${this.targetBranch}`);
314
+ const pathCommits = commitsPerPath[path];
315
+ logger_1.logger.debug(`commits: ${pathCommits.length}`);
316
+ const latestReleasePullRequest = releasePullRequestsBySha[releaseShasByPath[path]];
317
+ if (!latestReleasePullRequest) {
318
+ logger_1.logger.warn('No latest release pull request found.');
319
+ }
320
+ const strategy = strategies[path];
321
+ const latestRelease = releasesByPath[path];
300
322
  const releasePullRequest = await strategy.buildReleasePullRequest(pathCommits, latestRelease, (_a = config.draftPullRequest) !== null && _a !== void 0 ? _a : this.draftPullRequest, this.labels);
301
323
  if (releasePullRequest) {
302
324
  if (releasePullRequest.version) {
@@ -318,13 +340,6 @@ class Manifest {
318
340
  });
319
341
  }
320
342
  }
321
- // Build plugins
322
- const plugins = this.plugins.map(pluginType => factory_1.buildPlugin({
323
- type: pluginType,
324
- github: this.github,
325
- targetBranch: this.targetBranch,
326
- repositoryConfig: this.repositoryConfig,
327
- }));
328
343
  // Combine pull requests into 1 unless configured for separate
329
344
  // pull requests
330
345
  if (!this.separatePullRequests) {
@@ -384,7 +399,18 @@ class Manifest {
384
399
  logger_1.logger.warn('There are untagged, merged release PRs outstanding - aborting');
385
400
  return [];
386
401
  }
387
- // collect open release pull requests
402
+ // collect open and snoozed release pull requests
403
+ const openPullRequests = await this.findOpenReleasePullRequests();
404
+ const snoozedPullRequests = await this.findSnoozedReleasePullRequests();
405
+ const promises = [];
406
+ for (const pullRequest of candidatePullRequests) {
407
+ promises.push(this.createOrUpdatePullRequest(pullRequest, openPullRequests, snoozedPullRequests));
408
+ }
409
+ const pullNumbers = await Promise.all(promises);
410
+ // reject any pull numbers that were not created or updated
411
+ return pullNumbers.filter(number => !!number);
412
+ }
413
+ async findOpenReleasePullRequests() {
388
414
  logger_1.logger.info('Looking for open release pull requests');
389
415
  const openPullRequests = [];
390
416
  const generator = this.github.pullRequestIterator(this.targetBranch, 'OPEN');
@@ -396,34 +422,66 @@ class Manifest {
396
422
  }
397
423
  }
398
424
  logger_1.logger.info(`found ${openPullRequests.length} open release pull requests.`);
399
- const promises = [];
400
- for (const pullRequest of candidatePullRequests) {
401
- promises.push(this.createOrUpdatePullRequest(pullRequest, openPullRequests));
425
+ return openPullRequests;
426
+ }
427
+ async findSnoozedReleasePullRequests() {
428
+ logger_1.logger.info('Looking for snoozed release pull requests');
429
+ const snoozedPullRequests = [];
430
+ const closedGenerator = this.github.pullRequestIterator(this.targetBranch, 'CLOSED');
431
+ for await (const closedPullRequest of closedGenerator) {
432
+ if (hasAllLabels([SNOOZE_LABEL], closedPullRequest.labels) &&
433
+ branch_name_1.BranchName.parse(closedPullRequest.headBranchName) &&
434
+ pull_request_body_1.PullRequestBody.parse(closedPullRequest.body)) {
435
+ snoozedPullRequests.push(closedPullRequest);
436
+ }
402
437
  }
403
- return await Promise.all(promises);
438
+ logger_1.logger.info(`found ${snoozedPullRequests.length} snoozed release pull requests.`);
439
+ return snoozedPullRequests;
404
440
  }
405
- async createOrUpdatePullRequest(pullRequest, openPullRequests) {
406
- // look for existing, open pull rquest
441
+ async createOrUpdatePullRequest(pullRequest, openPullRequests, snoozedPullRequests) {
442
+ // look for existing, open pull request
407
443
  const existing = openPullRequests.find(openPullRequest => openPullRequest.headBranchName === pullRequest.headRefName);
408
444
  if (existing) {
409
- // If unchanged, no need to push updates
410
- if (existing.body === pullRequest.body.toString()) {
411
- logger_1.logger.info(`PR https://github.com/${this.repository.owner}/${this.repository.repo}/pull/${existing.number} remained the same`);
412
- return undefined;
413
- }
414
- const updatedPullRequest = await this.github.updatePullRequest(existing.number, pullRequest, this.targetBranch, {
415
- fork: this.fork,
416
- signoffUser: this.signoffUser,
417
- });
418
- return updatedPullRequest;
445
+ return await this.maybeUpdateExistingPullRequest(existing, pullRequest);
419
446
  }
420
- else {
421
- const newPullRequest = await this.github.createReleasePullRequest(pullRequest, this.targetBranch, {
422
- fork: this.fork,
423
- signoffUser: this.signoffUser,
424
- });
425
- return newPullRequest;
447
+ // look for closed, snoozed pull request
448
+ const snoozed = snoozedPullRequests.find(openPullRequest => openPullRequest.headBranchName === pullRequest.headRefName);
449
+ if (snoozed) {
450
+ return await this.maybeUpdateSnoozedPullRequest(snoozed, pullRequest);
426
451
  }
452
+ const newPullRequest = await this.github.createReleasePullRequest(pullRequest, this.targetBranch, {
453
+ fork: this.fork,
454
+ signoffUser: this.signoffUser,
455
+ });
456
+ return newPullRequest;
457
+ }
458
+ /// only update an existing pull request if it has release note changes
459
+ async maybeUpdateExistingPullRequest(existing, pullRequest) {
460
+ // If unchanged, no need to push updates
461
+ if (existing.body === pullRequest.body.toString()) {
462
+ logger_1.logger.info(`PR https://github.com/${this.repository.owner}/${this.repository.repo}/pull/${existing.number} remained the same`);
463
+ return undefined;
464
+ }
465
+ const updatedPullRequest = await this.github.updatePullRequest(existing.number, pullRequest, this.targetBranch, {
466
+ fork: this.fork,
467
+ signoffUser: this.signoffUser,
468
+ });
469
+ return updatedPullRequest;
470
+ }
471
+ /// only update an snoozed pull request if it has release note changes
472
+ async maybeUpdateSnoozedPullRequest(snoozed, pullRequest) {
473
+ // If unchanged, no need to push updates
474
+ if (snoozed.body === pullRequest.body.toString()) {
475
+ logger_1.logger.info(`PR https://github.com/${this.repository.owner}/${this.repository.repo}/pull/${snoozed.number} remained the same`);
476
+ return undefined;
477
+ }
478
+ const updatedPullRequest = await this.github.updatePullRequest(snoozed.number, pullRequest, this.targetBranch, {
479
+ fork: this.fork,
480
+ signoffUser: this.signoffUser,
481
+ });
482
+ // TODO: consider leaving the snooze label
483
+ await this.github.removeIssueLabels([SNOOZE_LABEL], snoozed.number);
484
+ return updatedPullRequest;
427
485
  }
428
486
  async *findMergedReleasePullRequests() {
429
487
  // Find merged release pull requests
@@ -1,5 +1,8 @@
1
1
  import { GitHub } from './github';
2
2
  import { CandidateReleasePullRequest, RepositoryConfig } from './manifest';
3
+ import { Strategy } from './strategy';
4
+ import { Commit } from './commit';
5
+ import { Release } from './release';
3
6
  /**
4
7
  * A plugin runs after a repository manifest has built candidate
5
8
  * pull requests and can make updates that span across multiple
@@ -16,5 +19,11 @@ export declare abstract class ManifestPlugin {
16
19
  * @param {CandidateReleasePullRequest[]} pullRequests Candidate pull requests
17
20
  * @returns {CandidateReleasePullRequest[]} Updated pull requests
18
21
  */
19
- abstract run(pullRequests: CandidateReleasePullRequest[]): Promise<CandidateReleasePullRequest[]>;
22
+ run(pullRequests: CandidateReleasePullRequest[]): Promise<CandidateReleasePullRequest[]>;
23
+ /**
24
+ * Pre-configure strategies.
25
+ * @param {Record<string, Strategy>} strategiesByPath Strategies indexed by path
26
+ * @returns {Record<string, Strategy>} Updated strategies indexed by path
27
+ */
28
+ preconfigure(strategiesByPath: Record<string, Strategy>, _commitsByPath: Record<string, Commit[]>, _releasesByPath: Record<string, Release>): Promise<Record<string, Strategy>>;
20
29
  }
@@ -26,6 +26,22 @@ class ManifestPlugin {
26
26
  this.targetBranch = targetBranch;
27
27
  this.repositoryConfig = repositoryConfig;
28
28
  }
29
+ /**
30
+ * Post-process candidate pull requests.
31
+ * @param {CandidateReleasePullRequest[]} pullRequests Candidate pull requests
32
+ * @returns {CandidateReleasePullRequest[]} Updated pull requests
33
+ */
34
+ async run(pullRequests) {
35
+ return pullRequests;
36
+ }
37
+ /**
38
+ * Pre-configure strategies.
39
+ * @param {Record<string, Strategy>} strategiesByPath Strategies indexed by path
40
+ * @returns {Record<string, Strategy>} Updated strategies indexed by path
41
+ */
42
+ async preconfigure(strategiesByPath, _commitsByPath, _releasesByPath) {
43
+ return strategiesByPath;
44
+ }
29
45
  }
30
46
  exports.ManifestPlugin = ManifestPlugin;
31
47
  //# sourceMappingURL=plugin.js.map
@@ -0,0 +1,29 @@
1
+ import { ManifestPlugin } from '../plugin';
2
+ import { RepositoryConfig, CandidateReleasePullRequest } from '../manifest';
3
+ import { GitHub } from '../github';
4
+ import { Strategy } from '../strategy';
5
+ import { Commit } from '../commit';
6
+ import { Release } from '../release';
7
+ /**
8
+ * This plugin reconfigures strategies by linking multiple components
9
+ * together.
10
+ *
11
+ * Release notes are broken up using `<summary>`/`<details>` blocks.
12
+ */
13
+ export declare class LinkedVersions extends ManifestPlugin {
14
+ private groupName;
15
+ private components;
16
+ constructor(github: GitHub, targetBranch: string, repositoryConfig: RepositoryConfig, groupName: string, components: string[]);
17
+ /**
18
+ * Pre-configure strategies.
19
+ * @param {Record<string, Strategy>} strategiesByPath Strategies indexed by path
20
+ * @returns {Record<string, Strategy>} Updated strategies indexed by path
21
+ */
22
+ preconfigure(strategiesByPath: Record<string, Strategy>, commitsByPath: Record<string, Commit[]>, releasesByPath: Record<string, Release>): Promise<Record<string, Strategy>>;
23
+ /**
24
+ * Post-process candidate pull requests.
25
+ * @param {CandidateReleasePullRequest[]} pullRequests Candidate pull requests
26
+ * @returns {CandidateReleasePullRequest[]} Updated pull requests
27
+ */
28
+ run(candidates: CandidateReleasePullRequest[]): Promise<CandidateReleasePullRequest[]>;
29
+ }
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ // Copyright 2022 Google LLC
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.LinkedVersions = void 0;
17
+ const plugin_1 = require("../plugin");
18
+ const logger_1 = require("../util/logger");
19
+ const factory_1 = require("../factory");
20
+ const merge_1 = require("./merge");
21
+ /**
22
+ * This plugin reconfigures strategies by linking multiple components
23
+ * together.
24
+ *
25
+ * Release notes are broken up using `<summary>`/`<details>` blocks.
26
+ */
27
+ class LinkedVersions extends plugin_1.ManifestPlugin {
28
+ constructor(github, targetBranch, repositoryConfig, groupName, components) {
29
+ super(github, targetBranch, repositoryConfig);
30
+ this.groupName = groupName;
31
+ this.components = new Set(components);
32
+ }
33
+ /**
34
+ * Pre-configure strategies.
35
+ * @param {Record<string, Strategy>} strategiesByPath Strategies indexed by path
36
+ * @returns {Record<string, Strategy>} Updated strategies indexed by path
37
+ */
38
+ async preconfigure(strategiesByPath, commitsByPath, releasesByPath) {
39
+ // Find all strategies in the group
40
+ const groupStrategies = {};
41
+ for (const path in strategiesByPath) {
42
+ const strategy = strategiesByPath[path];
43
+ const component = await strategy.getComponent();
44
+ if (!component) {
45
+ continue;
46
+ }
47
+ if (this.components.has(component)) {
48
+ groupStrategies[path] = strategy;
49
+ }
50
+ }
51
+ logger_1.logger.info(`Found ${Object.keys(groupStrategies).length} group components for ${this.groupName}`);
52
+ const groupVersions = {};
53
+ const missingReleasePaths = new Set();
54
+ for (const path in groupStrategies) {
55
+ const strategy = groupStrategies[path];
56
+ const latestRelease = releasesByPath[path];
57
+ const releasePullRequest = await strategy.buildReleasePullRequest(commitsByPath[path], latestRelease);
58
+ if (releasePullRequest === null || releasePullRequest === void 0 ? void 0 : releasePullRequest.version) {
59
+ groupVersions[path] = releasePullRequest.version;
60
+ }
61
+ else {
62
+ missingReleasePaths.add(path);
63
+ }
64
+ }
65
+ const versions = Object.values(groupVersions);
66
+ if (versions.length === 0) {
67
+ return strategiesByPath;
68
+ }
69
+ const primaryVersion = versions.reduce((collector, version) => collector.compare(version) > 0 ? collector : version, versions[0]);
70
+ const newStrategies = {};
71
+ for (const path in strategiesByPath) {
72
+ if (path in groupStrategies) {
73
+ const component = await strategiesByPath[path].getComponent();
74
+ logger_1.logger.info(`Replacing strategy for path ${path} with forced version: ${primaryVersion}`);
75
+ newStrategies[path] = await factory_1.buildStrategy({
76
+ ...this.repositoryConfig[path],
77
+ github: this.github,
78
+ path,
79
+ targetBranch: this.targetBranch,
80
+ releaseAs: primaryVersion.toString(),
81
+ });
82
+ if (missingReleasePaths.has(path)) {
83
+ logger_1.logger.debug(`Appending fake commit for path: ${path}`);
84
+ commitsByPath[path].push({
85
+ sha: '',
86
+ message: `chore(${component}): Synchronize ${this.groupName} versions\n\nRelease-As: ${primaryVersion.toString()}`,
87
+ });
88
+ }
89
+ }
90
+ else {
91
+ newStrategies[path] = strategiesByPath[path];
92
+ }
93
+ }
94
+ return newStrategies;
95
+ }
96
+ /**
97
+ * Post-process candidate pull requests.
98
+ * @param {CandidateReleasePullRequest[]} pullRequests Candidate pull requests
99
+ * @returns {CandidateReleasePullRequest[]} Updated pull requests
100
+ */
101
+ async run(candidates) {
102
+ const [inScopeCandidates, outOfScopeCandidates] = candidates.reduce((collection, candidate) => {
103
+ if (!candidate.pullRequest.version) {
104
+ logger_1.logger.warn('pull request missing version', candidate);
105
+ return collection;
106
+ }
107
+ if (this.components.has(candidate.config.component || '')) {
108
+ collection[0].push(candidate);
109
+ }
110
+ else {
111
+ collection[1].push(candidate);
112
+ }
113
+ return collection;
114
+ }, [[], []]);
115
+ // delegate to the merge plugin and add merged pull request
116
+ if (inScopeCandidates.length > 0) {
117
+ const merge = new merge_1.Merge(this.github, this.targetBranch, this.repositoryConfig, `chore\${branch}: release ${this.groupName} libraries`);
118
+ const merged = await merge.run(inScopeCandidates);
119
+ outOfScopeCandidates.push(...merged);
120
+ }
121
+ return outOfScopeCandidates;
122
+ }
123
+ }
124
+ exports.LinkedVersions = LinkedVersions;
125
+ //# sourceMappingURL=linked-versions.js.map
@@ -1,4 +1,4 @@
1
- import { Package as LernaPackage, PackageJson } from '@lerna/package';
1
+ import { Package as LernaPackage, RawManifest as PackageJson } from '@lerna/package';
2
2
  import { GitHub } from '../github';
3
3
  import { CandidateReleasePullRequest, RepositoryConfig } from '../manifest';
4
4
  import { Version, VersionsMap } from '../version';
@@ -3,6 +3,7 @@ import { GitHub } from '../github';
3
3
  import { VersioningStrategy } from '../versioning-strategy';
4
4
  import { Repository } from '../repository';
5
5
  import { ChangelogNotes, ChangelogSection } from '../changelog-notes';
6
+ import { ExtraFile } from '../manifest';
6
7
  import { Update } from '../update';
7
8
  import { ConventionalCommit, Commit } from '../commit';
8
9
  import { Version, VersionsMap } from '../version';
@@ -37,7 +38,7 @@ export interface BaseStrategyOptions {
37
38
  changelogNotes?: ChangelogNotes;
38
39
  includeComponentInTag?: boolean;
39
40
  pullRequestTitlePattern?: string;
40
- extraFiles?: string[];
41
+ extraFiles?: ExtraFile[];
41
42
  }
42
43
  /**
43
44
  * A strategy is responsible for determining which files are
@@ -57,7 +58,7 @@ export declare abstract class BaseStrategy implements Strategy {
57
58
  private releaseAs?;
58
59
  protected includeComponentInTag: boolean;
59
60
  private pullRequestTitlePattern?;
60
- readonly extraFiles: string[];
61
+ readonly extraFiles: ExtraFile[];
61
62
  readonly changelogNotes: ChangelogNotes;
62
63
  protected changelogSections?: ChangelogSection[];
63
64
  constructor(options: BaseStrategyOptions);
@@ -81,7 +82,7 @@ export declare abstract class BaseStrategy implements Strategy {
81
82
  * @returns {ConventionalCommit[]} modified commits
82
83
  */
83
84
  protected postProcessCommits(commits: ConventionalCommit[]): Promise<ConventionalCommit[]>;
84
- protected buildReleaseNotes(conventionalCommits: ConventionalCommit[], newVersion: Version, newVersionTag: TagName, latestRelease?: Release): Promise<string>;
85
+ protected buildReleaseNotes(conventionalCommits: ConventionalCommit[], newVersion: Version, newVersionTag: TagName, latestRelease?: Release, commits?: Commit[]): Promise<string>;
85
86
  protected buildPullRequestBody(component: string | undefined, newVersion: Version, releaseNotesBody: string, _conventionalCommits: ConventionalCommit[], _latestRelease?: Release): Promise<PullRequestBody>;
86
87
  /**
87
88
  * Builds a candidate release pull request
@@ -26,6 +26,8 @@ const branch_name_1 = require("../util/branch-name");
26
26
  const pull_request_body_1 = require("../util/pull-request-body");
27
27
  const composite_1 = require("../updaters/composite");
28
28
  const generic_1 = require("../updaters/generic");
29
+ const generic_json_1 = require("../updaters/generic-json");
30
+ const generic_xml_1 = require("../updaters/generic-xml");
29
31
  const DEFAULT_CHANGELOG_PATH = 'CHANGELOG.md';
30
32
  /**
31
33
  * A strategy is responsible for determining which files are
@@ -90,7 +92,7 @@ class BaseStrategy {
90
92
  async postProcessCommits(commits) {
91
93
  return commits;
92
94
  }
93
- async buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease) {
95
+ async buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease, commits) {
94
96
  var _a;
95
97
  return await this.changelogNotes.buildNotes(conventionalCommits, {
96
98
  owner: this.repository.owner,
@@ -100,6 +102,7 @@ class BaseStrategy {
100
102
  currentTag: newVersionTag.toString(),
101
103
  targetBranch: this.targetBranch,
102
104
  changelogSections: this.changelogSections,
105
+ commits: commits,
103
106
  });
104
107
  }
105
108
  async buildPullRequestBody(component, newVersion, releaseNotesBody, _conventionalCommits, _latestRelease) {
@@ -139,7 +142,7 @@ class BaseStrategy {
139
142
  const branchName = component
140
143
  ? branch_name_1.BranchName.ofComponentTargetBranch(component, this.targetBranch)
141
144
  : branch_name_1.BranchName.ofTargetBranch(this.targetBranch);
142
- const releaseNotesBody = await this.buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease);
145
+ const releaseNotesBody = await this.buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease, commits);
143
146
  if (this.changelogEmpty(releaseNotesBody)) {
144
147
  logger_1.logger.info(`No user facing commits found since ${latestRelease ? latestRelease.sha : 'beginning of time'} - skipping`);
145
148
  return undefined;
@@ -163,12 +166,31 @@ class BaseStrategy {
163
166
  };
164
167
  }
165
168
  extraFileUpdates(version) {
166
- const genericUpdater = new generic_1.Generic({ version });
167
- return this.extraFiles.map(path => ({
168
- path: this.addPath(path),
169
- createIfMissing: false,
170
- updater: genericUpdater,
171
- }));
169
+ return this.extraFiles.map(extraFile => {
170
+ if (typeof extraFile === 'object') {
171
+ switch (extraFile.type) {
172
+ case 'json':
173
+ return {
174
+ path: this.addPath(extraFile.path),
175
+ createIfMissing: false,
176
+ updater: new generic_json_1.GenericJson(extraFile.jsonpath, version),
177
+ };
178
+ case 'xml':
179
+ return {
180
+ path: this.addPath(extraFile.path),
181
+ createIfMissing: false,
182
+ updater: new generic_xml_1.GenericXml(extraFile.xpath, version),
183
+ };
184
+ default:
185
+ throw new Error(`unsupported extraFile type: ${extraFile.type}`);
186
+ }
187
+ }
188
+ return {
189
+ path: this.addPath(extraFile),
190
+ createIfMissing: false,
191
+ updater: new generic_1.Generic({ version }),
192
+ };
193
+ });
172
194
  }
173
195
  changelogEmpty(changelogEntry) {
174
196
  return changelogEntry.split('\n').length <= 1;
@@ -1,6 +1,6 @@
1
1
  import { BaseStrategy, BuildUpdatesOptions, BaseStrategyOptions } from './base';
2
2
  import { Update } from '../update';
3
- import { ConventionalCommit } from '../commit';
3
+ import { Commit, ConventionalCommit } from '../commit';
4
4
  import { Version } from '../version';
5
5
  import { TagName } from '../util/tag-name';
6
6
  import { Release } from '../release';
@@ -9,6 +9,6 @@ export declare class GoYoshi extends BaseStrategy {
9
9
  protected buildUpdates(options: BuildUpdatesOptions): Promise<Update[]>;
10
10
  protected postProcessCommits(commits: ConventionalCommit[]): Promise<ConventionalCommit[]>;
11
11
  getIgnoredSubModules(): Promise<Set<string>>;
12
- protected buildReleaseNotes(conventionalCommits: ConventionalCommit[], newVersion: Version, newVersionTag: TagName, latestRelease?: Release): Promise<string>;
12
+ protected buildReleaseNotes(conventionalCommits: ConventionalCommit[], newVersion: Version, newVersionTag: TagName, latestRelease?: Release, commits?: Commit[]): Promise<string>;
13
13
  protected initialReleaseVersion(): Version;
14
14
  }
@@ -137,8 +137,8 @@ class GoYoshi extends base_1.BaseStrategy {
137
137
  }
138
138
  // "closes" is a little presumptuous, let's just indicate that the
139
139
  // PR references these other commits:
140
- async buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease) {
141
- const releaseNotes = await super.buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease);
140
+ async buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease, commits) {
141
+ const releaseNotes = await super.buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease, commits);
142
142
  return releaseNotes.replace(/, closes /g, ', refs ');
143
143
  }
144
144
  initialReleaseVersion() {
@@ -198,9 +198,12 @@ class JavaYoshi extends base_1.BaseStrategy {
198
198
  }),
199
199
  });
200
200
  });
201
- this.extraFiles.forEach(path => {
201
+ this.extraFiles.forEach(extraFile => {
202
+ if (typeof extraFile === 'object') {
203
+ return;
204
+ }
202
205
  updates.push({
203
- path,
206
+ path: extraFile,
204
207
  createIfMissing: false,
205
208
  updater: new java_update_1.JavaUpdate({
206
209
  version,
@@ -0,0 +1,13 @@
1
+ import { Updater } from '../update';
2
+ import { Version } from '../version';
3
+ export declare class GenericJson implements Updater {
4
+ readonly jsonpath: string;
5
+ readonly version: Version;
6
+ constructor(jsonpath: string, version: Version);
7
+ /**
8
+ * Given initial file contents, return updated contents.
9
+ * @param {string} content The initial content
10
+ * @returns {string} The updated content
11
+ */
12
+ updateContent(content: string): string;
13
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ // Copyright 2022 Google LLC
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.GenericJson = void 0;
17
+ const jp = require("jsonpath");
18
+ const json_stringify_1 = require("../util/json-stringify");
19
+ const logger_1 = require("../util/logger");
20
+ class GenericJson {
21
+ constructor(jsonpath, version) {
22
+ this.jsonpath = jsonpath;
23
+ this.version = version;
24
+ }
25
+ /**
26
+ * Given initial file contents, return updated contents.
27
+ * @param {string} content The initial content
28
+ * @returns {string} The updated content
29
+ */
30
+ updateContent(content) {
31
+ const data = JSON.parse(content);
32
+ const nodes = jp.apply(data, this.jsonpath, _val => {
33
+ return this.version.toString();
34
+ });
35
+ if (!nodes) {
36
+ logger_1.logger.warn(`No entries modified in ${this.jsonpath}`);
37
+ return content;
38
+ }
39
+ return json_stringify_1.jsonStringify(data, content);
40
+ }
41
+ }
42
+ exports.GenericJson = GenericJson;
43
+ //# sourceMappingURL=generic-json.js.map
@@ -0,0 +1,13 @@
1
+ import { Updater } from '../update';
2
+ import { Version } from '../version';
3
+ export declare class GenericXml implements Updater {
4
+ private xpath;
5
+ private version;
6
+ constructor(xpath: string, version: Version);
7
+ /**
8
+ * Given initial file contents, return updated contents.
9
+ * @param {string} content The initial content
10
+ * @returns {string} The updated content
11
+ */
12
+ updateContent(content: string): string;
13
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ // Copyright 2022 Google LLC
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.GenericXml = void 0;
17
+ const xpath = require("xpath");
18
+ const dom = require("xmldom");
19
+ class GenericXml {
20
+ constructor(xpath, version) {
21
+ this.xpath = xpath;
22
+ this.version = version;
23
+ }
24
+ /**
25
+ * Given initial file contents, return updated contents.
26
+ * @param {string} content The initial content
27
+ * @returns {string} The updated content
28
+ */
29
+ updateContent(content) {
30
+ const document = new dom.DOMParser().parseFromString(content);
31
+ const iterator = xpath.evaluate(this.xpath, document, null, 0, null);
32
+ let node;
33
+ let updated = false;
34
+ while ((node = iterator.iterateNext())) {
35
+ node.textContent = this.version.toString();
36
+ updated = true;
37
+ }
38
+ if (updated) {
39
+ return new dom.XMLSerializer().serializeToString(document);
40
+ }
41
+ else {
42
+ return content;
43
+ }
44
+ }
45
+ }
46
+ exports.GenericXml = GenericXml;
47
+ //# sourceMappingURL=generic-xml.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-please",
3
- "version": "13.5.0",
3
+ "version": "13.7.1",
4
4
  "description": "generate release PRs based on the conventionalcommits.org spec",
5
5
  "main": "./build/src/index.js",
6
6
  "bin": "./build/src/bin/release-please.js",
@@ -41,11 +41,17 @@
41
41
  "@types/chai": "^4.1.7",
42
42
  "@types/iarna__toml": "^2.0.1",
43
43
  "@types/js-yaml": "^4.0.0",
44
+ "@types/jsonpath": "^0.2.0",
45
+ "@types/lerna__collect-updates": "^4.0.0",
46
+ "@types/lerna__package": "^4.0.2",
47
+ "@types/lerna__package-graph": "^4.0.1",
48
+ "@types/lerna__run-topologically": "^4.0.0",
44
49
  "@types/mocha": "^9.0.0",
45
50
  "@types/node": "^16.0.0",
46
51
  "@types/pino": "^7.0.0",
47
52
  "@types/semver": "^7.0.0",
48
53
  "@types/sinon": "^10.0.0",
54
+ "@types/xmldom": "^0.1.31",
49
55
  "@types/yargs": "^17.0.0",
50
56
  "c8": "^7.0.0",
51
57
  "chai": "^4.2.0",
@@ -76,6 +82,7 @@
76
82
  "detect-indent": "^6.1.0",
77
83
  "figures": "^3.0.0",
78
84
  "js-yaml": "^4.0.0",
85
+ "jsonpath": "^1.1.1",
79
86
  "node-html-parser": "^5.0.0",
80
87
  "parse-github-repo-url": "^1.4.1",
81
88
  "semver": "^7.0.0",
@@ -83,6 +90,8 @@
83
90
  "typescript": "^3.8.3",
84
91
  "unist-util-visit": "^2.0.3",
85
92
  "unist-util-visit-parents": "^3.1.1",
93
+ "xmldom": "^0.6.0",
94
+ "xpath": "^0.0.32",
86
95
  "yargs": "^17.0.0"
87
96
  },
88
97
  "engines": {