Skip to content

Commit 354b1dc

Browse files
authored
feat: allow plugins to skip merging in-scope pull requests (#1550)
* test: add failing test for combining workspace and linked-versions plugin * feat: allow configuring workspace and linked-versions plugins to skip merging * chore: update schemas and remove logging * docs: add documentation on using linked-versions and workspace
1 parent 3ce7cb7 commit 354b1dc

13 files changed

Lines changed: 550 additions & 25 deletions

__snapshots__/cargo-workspace.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,58 @@ Release notes for path: packages/rustA, releaseType: rust
3131
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
3232
`
3333

34+
exports['CargoWorkspace plugin run can skip merging rust packages 1'] = `
35+
:robot: I have created a release *beep* *boop*
36+
---
37+
38+
39+
Release notes for path: packages/rustA, releaseType: rust
40+
41+
---
42+
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
43+
`
44+
45+
exports['CargoWorkspace plugin run can skip merging rust packages 2'] = `
46+
:robot: I have created a release *beep* *boop*
47+
---
48+
49+
50+
### Dependencies
51+
52+
* The following workspace dependencies were updated
53+
* dependencies
54+
* pkgA bumped from 1.1.1 to 1.1.2
55+
56+
---
57+
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
58+
`
59+
60+
exports['CargoWorkspace plugin run can skip merging rust packages 3'] = `
61+
:robot: I have created a release *beep* *boop*
62+
---
63+
64+
65+
### Dependencies
66+
67+
* The following workspace dependencies were updated
68+
* dependencies
69+
* pkgB bumped from 2.2.2 to 2.2.3
70+
71+
---
72+
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
73+
`
74+
75+
exports['CargoWorkspace plugin run can skip merging rust packages 4'] = `
76+
:robot: I have created a release *beep* *boop*
77+
---
78+
79+
80+
Release notes for path: packages/rustD, releaseType: rust
81+
82+
---
83+
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
84+
`
85+
3486
exports['CargoWorkspace plugin run combines rust packages 1'] = `
3587
:robot: I have created a release *beep* *boop*
3688
---
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
exports['Plugin compatibility linked-versions and workspace should version bump dependencies together 1'] = `
2+
:robot: I have created a release *beep* *boop*
3+
---
4+
5+
6+
<details><summary>pkgA: 1.1.0</summary>
7+
8+
## [1.1.0](https://github.com/fake-owner/fake-repo/compare/pkgA-v1.0.0...pkgA-v1.1.0) (1983-10-10)
9+
10+
11+
### Features
12+
13+
* some feature ([aaaaaa](https://github.com/fake-owner/fake-repo/commit/aaaaaa))
14+
</details>
15+
16+
<details><summary>pkgB: 1.1.0</summary>
17+
18+
## [1.1.0](https://github.com/fake-owner/fake-repo/compare/pkgB-v1.0.0...pkgB-v1.1.0) (1983-10-10)
19+
20+
21+
### Miscellaneous Chores
22+
23+
* **pkgB:** Synchronize my group versions
24+
25+
26+
### Dependencies
27+
28+
* The following workspace dependencies were updated
29+
* dependencies
30+
* pkgA bumped from 1.0.0 to 1.1.0
31+
</details>
32+
33+
---
34+
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
35+
`

__snapshots__/linked-versions.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,48 @@
1+
exports['LinkedVersions plugin can skip grouping pull requests 1'] = `
2+
:robot: I have created a release *beep* *boop*
3+
---
4+
5+
6+
## [1.0.1](https://github.com/fake-owner/fake-repo/compare/pkg1-v1.0.0...pkg1-v1.0.1) (1983-10-10)
7+
8+
9+
### Bug Fixes
10+
11+
* some bugfix ([aaaaaa](https://github.com/fake-owner/fake-repo/commit/aaaaaa))
12+
13+
---
14+
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
15+
`
16+
17+
exports['LinkedVersions plugin can skip grouping pull requests 2'] = `
18+
:robot: I have created a release *beep* *boop*
19+
---
20+
21+
22+
<details><summary>pkg2: 0.2.4</summary>
23+
24+
## [0.2.4](https://github.com/fake-owner/fake-repo/compare/pkg2-v0.2.3...pkg2-v0.2.4) (1983-10-10)
25+
26+
27+
### Bug Fixes
28+
29+
* some bugfix ([bbbbbb](https://github.com/fake-owner/fake-repo/commit/bbbbbb))
30+
</details>
31+
32+
<details><summary>pkg3: 0.2.4</summary>
33+
34+
## [0.2.4](https://github.com/fake-owner/fake-repo/compare/pkg3-v0.2.3...pkg3-v0.2.4) (1983-10-10)
35+
36+
37+
### Miscellaneous Chores
38+
39+
* **pkg3:** Synchronize group name versions
40+
</details>
41+
42+
---
43+
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
44+
`
45+
146
exports['LinkedVersions plugin should group pull requests 1'] = `
247
:robot: I have created a release *beep* *boop*
348
---

docs/manifest-releaser.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,43 @@ The `maven-workspace` plugin operates similarly to the `node-workspace` plugin,
477477
but on a multi-artifact Maven workspace. It builds a dependency graph of all
478478
discovered `pom.xml` files that are configured in the manifest config and updates
479479
any packages that were directly bumped by release-please.
480+
481+
### linked-versions
482+
483+
The `linked-versions` plugin allows you to "link" the versions of multiple
484+
components in your monorepo. When any component in the specified group is
485+
updated, we pick the highest version amongst the components and update all
486+
group components to the same version (keeping them in sync).
487+
488+
Note: when combining the `linked-versions` plugin with a `workspace` plugin,
489+
you will need to tell the `workspace` plugin to skip its own internal merge.
490+
See #1457 for context.
491+
492+
Example:
493+
494+
```json
495+
{
496+
"release-type": "rust",
497+
"packages": {
498+
"packages/rustA": {
499+
"component": "pkgA"
500+
},
501+
"packages/rustB": {
502+
"component": "pkgB"
503+
}
504+
},
505+
"plugins": [
506+
{
507+
"type": "cargo-workspace",
508+
"merge": false
509+
},
510+
{
511+
"type": "linked-versions",
512+
"group-name": "my group",
513+
"components": [
514+
"pkgA", "pkgB"
515+
]
516+
}
517+
]
518+
}
519+
```

schemas/config.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,33 @@
228228
"items": {
229229
"type": "string"
230230
}
231+
},
232+
"merge": {
233+
"description": "Whether to merge in-scope pull requests into a combined release pull request. Defaults to `true`.",
234+
"type": "boolean"
231235
}
232236
},
233237
"required": ["type", "groupName", "components"]
234238
},
239+
{
240+
"description": "Configuration for various `workspace` plugins.",
241+
"type": "object",
242+
"properties": {
243+
"type": {
244+
"description": "The name of the plugin.",
245+
"type": "string",
246+
"enum": ["cargo-workspace", "maven-workspace", "node-workspace"]
247+
},
248+
"updateAllPackages": {
249+
"description": "Whether to force updating all packages regardless of the dependency tree. Defaults to `false`.",
250+
"type": "boolean"
251+
},
252+
"merge": {
253+
"description": "Whether to merge in-scope pull requests into a combined release pull request. Defaults to `true`.",
254+
"type": "boolean"
255+
}
256+
}
257+
},
235258
{
236259
"description": "Other plugins",
237260
"type": "object",

src/factories/plugin-factory.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ export function buildPlugin(options: PluginFactoryOptions): ManifestPlugin {
7878
if (typeof options.type === 'object') {
7979
const builder = pluginFactories[options.type.type];
8080
if (builder) {
81-
return builder(options);
81+
return builder({
82+
...options.type,
83+
...options,
84+
});
8285
}
8386
throw new ConfigurationError(
8487
`Unknown plugin type: ${options.type.type}`,

src/manifest.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,16 @@ export interface LinkedVersionPluginConfig extends ConfigurablePluginType {
180180
type: 'linked-versions';
181181
groupName: string;
182182
components: string[];
183+
merge?: boolean;
184+
}
185+
export interface WorkspacePluginConfig extends ConfigurablePluginType {
186+
merge?: boolean;
183187
}
184188
export type PluginType =
185189
| DirectPluginType
186190
| ConfigurablePluginType
187-
| LinkedVersionPluginConfig;
191+
| LinkedVersionPluginConfig
192+
| WorkspacePluginConfig;
188193

189194
/**
190195
* This is the schema of the manifest config json
@@ -691,6 +696,7 @@ export class Manifest {
691696
}
692697

693698
for (const plugin of plugins) {
699+
logger.debug(`running plugin: ${plugin.constructor.name}`);
694700
newReleasePullRequests = await plugin.run(newReleasePullRequests);
695701
}
696702

src/plugins/cargo-workspace.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,17 @@ export class CargoWorkspace extends WorkspacePlugin<CrateInfo> {
271271
candidates: CandidateReleasePullRequest[],
272272
updatedVersions: VersionsMap
273273
): CandidateReleasePullRequest[] {
274-
const rootCandidate = candidates.find(c => c.path === ROOT_PROJECT_PATH);
274+
let rootCandidate = candidates.find(c => c.path === ROOT_PROJECT_PATH);
275275
if (!rootCandidate) {
276-
throw Error('Unable to find root candidate pull request');
276+
logger.warn('Unable to find root candidate pull request');
277+
rootCandidate = candidates.find(c => c.config.releaseType === 'rust');
278+
}
279+
if (!rootCandidate) {
280+
logger.warn('Unable to find a rust candidate pull request');
281+
return candidates;
277282
}
278283

284+
// Update the root Cargo.lock if it exists
279285
rootCandidate.pullRequest.updates.push({
280286
path: 'Cargo.lock',
281287
createIfMissing: false,

src/plugins/linked-versions.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ import {Version} from '../version';
2323
import {buildStrategy} from '../factory';
2424
import {Merge} from './merge';
2525

26+
interface LinkedVersionsPluginOptions {
27+
merge?: boolean;
28+
}
29+
2630
/**
2731
* This plugin reconfigures strategies by linking multiple components
2832
* together.
@@ -32,17 +36,20 @@ import {Merge} from './merge';
3236
export class LinkedVersions extends ManifestPlugin {
3337
private groupName: string;
3438
private components: Set<string>;
39+
private merge: boolean;
3540

3641
constructor(
3742
github: GitHub,
3843
targetBranch: string,
3944
repositoryConfig: RepositoryConfig,
4045
groupName: string,
41-
components: string[]
46+
components: string[],
47+
options: LinkedVersionsPluginOptions = {}
4248
) {
4349
super(github, targetBranch, repositoryConfig);
4450
this.groupName = groupName;
4551
this.components = new Set(components);
52+
this.merge = options.merge ?? true;
4653
}
4754

4855
/**
@@ -138,6 +145,10 @@ export class LinkedVersions extends ManifestPlugin {
138145
async run(
139146
candidates: CandidateReleasePullRequest[]
140147
): Promise<CandidateReleasePullRequest[]> {
148+
if (!this.merge) {
149+
return candidates;
150+
}
151+
141152
const [inScopeCandidates, outOfScopeCandidates] = candidates.reduce(
142153
(collection, candidate) => {
143154
if (!candidate.pullRequest.version) {

src/plugins/workspace.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export interface DependencyNode<T> {
3434
export interface WorkspacePluginOptions {
3535
manifestPath?: string;
3636
updateAllPackages?: boolean;
37+
merge?: boolean;
3738
}
3839

3940
export interface AllPackages<T> {
@@ -55,6 +56,7 @@ export interface AllPackages<T> {
5556
export abstract class WorkspacePlugin<T> extends ManifestPlugin {
5657
private updateAllPackages: boolean;
5758
private manifestPath: string;
59+
private merge: boolean;
5860
constructor(
5961
github: GitHub,
6062
targetBranch: string,
@@ -64,6 +66,7 @@ export abstract class WorkspacePlugin<T> extends ManifestPlugin {
6466
super(github, targetBranch, repositoryConfig);
6567
this.manifestPath = options.manifestPath ?? DEFAULT_RELEASE_PLEASE_MANIFEST;
6668
this.updateAllPackages = options.updateAllPackages ?? false;
69+
this.merge = options.merge ?? true;
6770
}
6871
async run(
6972
candidates: CandidateReleasePullRequest[]
@@ -151,28 +154,26 @@ export abstract class WorkspacePlugin<T> extends ManifestPlugin {
151154
}
152155
}
153156

154-
logger.info(`Merging ${newCandidates.length} in-scope candidates`);
155-
const mergePlugin = new Merge(
156-
this.github,
157-
this.targetBranch,
158-
this.repositoryConfig
159-
);
160-
newCandidates = await mergePlugin.run(newCandidates);
161-
162-
if (newCandidates.length === 1) {
163-
const newUpdates = newCandidates[0].pullRequest.updates;
164-
newUpdates.push({
165-
path: this.manifestPath,
166-
createIfMissing: false,
167-
updater: new ReleasePleaseManifest({
168-
version: newCandidates[0].pullRequest.version!,
169-
versionsMap: updatedPathVersions,
170-
}),
171-
});
172-
} else {
173-
logger.warn(`Expected 1 merged candidate, got ${newCandidates.length}`);
157+
if (this.merge) {
158+
logger.info(`Merging ${newCandidates.length} in-scope candidates`);
159+
const mergePlugin = new Merge(
160+
this.github,
161+
this.targetBranch,
162+
this.repositoryConfig
163+
);
164+
newCandidates = await mergePlugin.run(newCandidates);
174165
}
175166

167+
const newUpdates = newCandidates[0].pullRequest.updates;
168+
newUpdates.push({
169+
path: this.manifestPath,
170+
createIfMissing: false,
171+
updater: new ReleasePleaseManifest({
172+
version: newCandidates[0].pullRequest.version!,
173+
versionsMap: updatedPathVersions,
174+
}),
175+
});
176+
176177
logger.info(`Post-processing ${newCandidates.length} in-scope candidates`);
177178
newCandidates = this.postProcessCandidates(newCandidates, updatedVersions);
178179

0 commit comments

Comments
 (0)