Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/customizing.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,35 @@ If you wish to avoid that, consider using `component-no-space: true`/`--componen
| `${version}` | The version of the component being released |
| `${branch?}` | The target branch of the pull request. If you have multiple release branches, this helps identify which release branch we are working on |

#### Group Pull Request Title

For grouped/merged pull requests (when using plugins like `linked-versions` or when `separate-pull-requests` is `false`), you can use the `group-pull-request-title-pattern` option instead:

```json
{
"group-pull-request-title-pattern": "chore${scope}: release ${component} ${version}"
}
```

This pattern uses the same placeholders as `pull-request-title-pattern`. When used with the `linked-versions` plugin, the `${component}` placeholder will be replaced with the plugin's `groupName`.

For example, with the configuration:

```json
{
"group-pull-request-title-pattern": "chore${scope}: release ${component} ${version}",
"plugins": [
{
"type": "linked-versions",
"groupName": "my-sdk",
"components": ["pkgA", "pkgB"]
}
]
}
```

The resulting pull request title will be: `chore(main): release my-sdk v1.2.3`

### Pull Request Header

If you would like to customize the pull request header, you can use the
Expand Down
48 changes: 47 additions & 1 deletion docs/manifest-releaser.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ defaults (those are documented in comments)
// Template values (i.e. ${scope}, ${component} and ${version}) are inherited
// from the root path's (i.e. '.') package, if present
// absence defaults to "chore: release ${branch}"
// Note: When used with the `linked-versions` plugin, ${component} will be
// replaced with the plugin's groupName.
"group-pull-request-title-pattern": "chore: release ${branch}",

// When searching for the latest release SHAs, only consider the last N releases.
Expand Down Expand Up @@ -568,7 +570,49 @@ Note: when combining the `linked-versions` plugin with a `workspace` plugin,
you will need to tell the `workspace` plugin to skip its own internal merge.
See #1457 for context.

Example:
#### Pull Request Title Configuration

By default, the `linked-versions` plugin generates pull request titles with
the format `chore(scope): release <groupName> libraries`. You can customize
this by using the `group-pull-request-title-pattern` option:

```json
{
"release-type": "rust",
"group-pull-request-title-pattern": "chore${scope}: release ${component} ${version}",
"packages": {
"packages/rustA": {
"component": "pkgA"
},
"packages/rustB": {
"component": "pkgB"
}
},
"plugins": [
{
"type": "cargo-workspace",
"merge": false
},
{
"type": "linked-versions",
"groupName": "my-sdk",
"components": [
"pkgA", "pkgB"
]
}
]
}
```

With this configuration, the pull request title will be
`chore(main): release my-sdk v1.2.3` instead of the default
`chore(main): release my-sdk libraries`.

The `${component}` placeholder in the pattern will be replaced with the
`groupName` from the plugin configuration. Other available placeholders are
`${scope}`, `${version}`, and `${branch}`.

Example without configuration (backward compatibility):

```json
{
Expand Down Expand Up @@ -597,6 +641,8 @@ Example:
}
```

This will use the default title: `chore(main): release my group libraries`.

### sentence-case

Capitalize the leading word in a commit message, taking into account common exceptions, e.g., gRPC.
Expand Down
3 changes: 3 additions & 0 deletions src/factories/plugin-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ export interface PluginFactoryOptions {
updateAllPackages?: boolean;
considerAllArtifacts?: boolean;

// linked-versions options
groupPullRequestTitlePattern?: string;

logger?: Logger;
}

Expand Down
1 change: 1 addition & 0 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ export class Manifest {
repositoryConfig: this.repositoryConfig,
manifestPath: this.manifestPath,
separatePullRequests: this.separatePullRequests,
groupPullRequestTitlePattern: this.groupPullRequestTitlePattern,
})
);
this.pullRequestOverflowHandler = new FilePullRequestOverflowHandler(
Expand Down
16 changes: 15 additions & 1 deletion src/plugins/linked-versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {BranchName} from '../util/branch-name';

interface LinkedVersionsPluginOptions {
merge?: boolean;
groupPullRequestTitlePattern?: string;
logger?: Logger;
}

Expand All @@ -39,6 +40,7 @@ export class LinkedVersions extends ManifestPlugin {
readonly groupName: string;
readonly components: Set<string>;
readonly merge: boolean;
private groupPullRequestTitlePattern?: string;

constructor(
github: GitHub,
Expand All @@ -52,6 +54,7 @@ export class LinkedVersions extends ManifestPlugin {
this.groupName = groupName;
this.components = new Set(components);
this.merge = options.merge ?? true;
this.groupPullRequestTitlePattern = options.groupPullRequestTitlePattern;
}

/**
Expand Down Expand Up @@ -173,12 +176,23 @@ export class LinkedVersions extends ManifestPlugin {

// delegate to the merge plugin and add merged pull request
if (inScopeCandidates.length > 0) {
// Use configured pattern if available, otherwise default to "libraries" for backward compatibility
let pullRequestTitlePattern = this.groupPullRequestTitlePattern
? this.groupPullRequestTitlePattern
: `chore\${scope}: release ${this.groupName} libraries`;

// Replace ${component} placeholder with the actual group name
pullRequestTitlePattern = pullRequestTitlePattern.replace(
'${component}',
this.groupName
);

const merge = new Merge(
this.github,
this.targetBranch,
this.repositoryConfig,
{
pullRequestTitlePattern: `chore\${scope}: release ${this.groupName} \${version}`,
pullRequestTitlePattern,
forceMerge: true,
headBranchName: BranchName.ofGroupTargetBranch(
this.groupName,
Expand Down
143 changes: 143 additions & 0 deletions test/plugins/linked-versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,4 +346,147 @@ describe('LinkedVersions plugin', () => {
expect(groupPullRequest1.headRefName).to.not.include(' ');
expect(groupPullRequest2.headRefName).to.not.include(' ');
});

it('should use "libraries" in title by default for backward compatibility', async () => {
const manifest = new Manifest(
github,
'target-branch',
{
'path/a': {
releaseType: 'simple',
component: 'pkg1',
},
'path/b': {
releaseType: 'simple',
component: 'pkg2',
},
'path/c': {
releaseType: 'simple',
component: 'pkg3',
},
},
{
'path/a': Version.parse('1.0.0'),
'path/b': Version.parse('0.2.3'),
'path/c': Version.parse('0.2.3'),
},
{
separatePullRequests: true,
plugins: [
{
type: 'linked-versions',
groupName: 'my-group',
components: ['pkg2', 'pkg3'],
},
],
}
);
const pullRequests = await manifest.buildPullRequests();
expect(pullRequests).lengthOf(2);
const groupedPullRequest = pullRequests.find(pr =>
pr.title.toString().includes('my-group')
);
expect(groupedPullRequest).to.not.be.undefined;
expect(groupedPullRequest!.title.toString()).to.include('libraries');
expect(groupedPullRequest!.title.toString()).to.equal(
'chore(target-branch): release my-group libraries'
);
});

it('should respect groupPullRequestTitlePattern with version', async () => {
const manifest = new Manifest(
github,
'target-branch',
{
'path/a': {
releaseType: 'simple',
component: 'pkg1',
},
'path/b': {
releaseType: 'simple',
component: 'pkg2',
},
'path/c': {
releaseType: 'simple',
component: 'pkg3',
},
},
{
'path/a': Version.parse('1.0.0'),
'path/b': Version.parse('0.2.3'),
'path/c': Version.parse('0.2.3'),
},
{
separatePullRequests: true,
groupPullRequestTitlePattern:
'chore${scope}: release ${component} ${version}',
plugins: [
{
type: 'linked-versions',
groupName: 'my-sdk',
components: ['pkg2', 'pkg3'],
},
],
}
);
const pullRequests = await manifest.buildPullRequests();
expect(pullRequests).lengthOf(2);
// Find the grouped PR (pkg2+pkg3) - it should have multiple release data entries
const groupedPullRequest = pullRequests.find(
pr => pr.body.releaseData.length > 1
);
expect(groupedPullRequest).to.not.be.undefined;
expect(groupedPullRequest!.title.toString()).to.not.include('libraries');
expect(groupedPullRequest!.title.toString()).to.include('0.2.4');
expect(groupedPullRequest!.title.toString()).to.equal(
'chore(target-branch): release my-sdk 0.2.4'
);
});

it('should respect custom groupPullRequestTitlePattern', async () => {
const manifest = new Manifest(
github,
'target-branch',
{
'path/a': {
releaseType: 'simple',
component: 'pkg1',
},
'path/b': {
releaseType: 'simple',
component: 'pkg2',
},
'path/c': {
releaseType: 'simple',
component: 'pkg3',
},
},
{
'path/a': Version.parse('1.0.0'),
'path/b': Version.parse('0.2.3'),
'path/c': Version.parse('0.2.3'),
},
{
separatePullRequests: true,
groupPullRequestTitlePattern: 'feat${scope}: ${component} v${version}',
plugins: [
{
type: 'linked-versions',
groupName: 'core-libs',
components: ['pkg2', 'pkg3'],
},
],
}
);
const pullRequests = await manifest.buildPullRequests();
expect(pullRequests).lengthOf(2);
// Find the grouped PR (pkg2+pkg3) - it should have multiple release data entries
const groupedPullRequest = pullRequests.find(
pr => pr.body.releaseData.length > 1
);
expect(groupedPullRequest).to.not.be.undefined;
expect(groupedPullRequest!.title.toString()).to.equal(
'feat(target-branch): core-libs v0.2.4'
);
});
});
Loading