Skip to content

Commit

Permalink
feat: always-update (#2420)
Browse files Browse the repository at this point in the history
* feat: add "always-update" config option

Fixes #1459

This patch adds a new option "always-update" that forces existing pull
requests to be updated even when there are no changes to the
release notes. This is useful, for example, when the repository
enforces pull requests to be up-to-date with the main branch before
they can be merged. Without this option set to `true`, that is, with the
default behavior of release-please, if the last commit made to the
main branch does not appear in the release notes, the existing pull
request branch would not be rebased.

* feat: apply suggested changes

---------

Co-authored-by: Fabian Meyer <[email protected]>
Co-authored-by: Jeff Ching <[email protected]>
  • Loading branch information
3 people authored Nov 12, 2024
1 parent b79f423 commit 72d40df
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 19 deletions.
7 changes: 7 additions & 0 deletions docs/manifest-releaser.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ defaults (those are documented in comments)
// absence defaults to false and one pull request will be raised
"separate-pull-requests": false,

// if true, always update existing pull requests when changes are added,
// instead of only when the release notes change.
// This option may increase the number of API calls used, but can be useful
// if pull requests must not be out-of-date with the base branch.
// absence defaults to false
"always-update": true,

// sets the manifest pull request title for when releasing multiple packages
// grouped together in the one pull request.
// This option has no effect when `separate-pull-requests` is `true`.
Expand Down
5 changes: 5 additions & 0 deletions schemas/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@
"description": "Open a separate release pull request for each component. Defaults to `false`.",
"type": "boolean"
},
"always-update": {
"description": "Always update the pull request with the latest changes. Defaults to `false`.",
"type": "boolean"
},
"tag-separator": {
"description": "Customize the separator between the component and version in the GitHub tag.",
"type": "string"
Expand Down Expand Up @@ -470,6 +474,7 @@
"pull-request-header": true,
"pull-request-footer": true,
"separate-pull-requests": true,
"always-update": true,
"tag-separator": true,
"extra-files": true,
"version-file": true,
Expand Down
50 changes: 31 additions & 19 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ export interface ManifestOptions {
draft?: boolean;
prerelease?: boolean;
draftPullRequest?: boolean;
alwaysUpdate?: boolean;
groupPullRequestTitlePattern?: string;
releaseSearchDepth?: number;
commitSearchDepth?: number;
Expand Down Expand Up @@ -260,6 +261,7 @@ export interface ManifestConfig extends ReleaserConfigJson {
'release-search-depth'?: number;
'commit-search-depth'?: number;
'sequential-calls'?: boolean;
'always-update'?: boolean;
}
// path => version
export type ReleasedVersions = Record<string, Version>;
Expand Down Expand Up @@ -295,6 +297,7 @@ export class Manifest {
readonly releasedVersions: ReleasedVersions;
private targetBranch: string;
private separatePullRequests: boolean;
private alwaysUpdate: boolean;
readonly fork: boolean;
private signoffUser?: string;
private labels: string[];
Expand Down Expand Up @@ -334,6 +337,8 @@ export class Manifest {
* plugin
* @param {boolean} manifestOptions.separatePullRequests If true, create separate pull
* requests instead of a single manifest release pull request
* @param {boolean} manifestOptions.alwaysUpdate If true, always updates pull requests instead of
* only when the release notes change
* @param {PluginType[]} manifestOptions.plugins Any plugins to use for this repository
* @param {boolean} manifestOptions.fork If true, create pull requests from a fork. Defaults
* to `false`
Expand Down Expand Up @@ -361,6 +366,7 @@ export class Manifest {
this.separatePullRequests =
manifestOptions?.separatePullRequests ??
Object.keys(repositoryConfig).length === 1;
this.alwaysUpdate = manifestOptions?.alwaysUpdate || false;
this.fork = manifestOptions?.fork || false;
this.signoffUser = manifestOptions?.signoff;
this.releaseLabels =
Expand Down Expand Up @@ -1016,7 +1022,9 @@ export class Manifest {
openPullRequest.headBranchName === pullRequest.headRefName
);
if (existing) {
return await this.maybeUpdateExistingPullRequest(existing, pullRequest);
return this.alwaysUpdate
? await this.updateExistingPullRequest(existing, pullRequest)
: await this.maybeUpdateExistingPullRequest(existing, pullRequest);
}

// look for closed, snoozed pull request
Expand All @@ -1025,7 +1033,9 @@ export class Manifest {
openPullRequest.headBranchName === pullRequest.headRefName
);
if (snoozed) {
return await this.maybeUpdateSnoozedPullRequest(snoozed, pullRequest);
return this.alwaysUpdate
? await this.updateExistingPullRequest(snoozed, pullRequest)
: await this.maybeUpdateSnoozedPullRequest(snoozed, pullRequest);
}

const body = await this.pullRequestOverflowHandler.handleOverflow(
Expand Down Expand Up @@ -1068,20 +1078,10 @@ export class Manifest {
);
return undefined;
}
const updatedPullRequest = await this.github.updatePullRequest(
existing.number,
pullRequest,
this.targetBranch,
{
fork: this.fork,
signoffUser: this.signoffUser,
pullRequestOverflowHandler: this.pullRequestOverflowHandler,
}
);
return updatedPullRequest;
return await this.updateExistingPullRequest(existing, pullRequest);
}

/// only update an snoozed pull request if it has release note changes
/// only update a snoozed pull request if it has release note changes
private async maybeUpdateSnoozedPullRequest(
snoozed: PullRequest,
pullRequest: ReleasePullRequest
Expand All @@ -1093,8 +1093,22 @@ export class Manifest {
);
return undefined;
}
const updatedPullRequest = await this.github.updatePullRequest(
snoozed.number,
const updatedPullRequest = await this.updateExistingPullRequest(
snoozed,
pullRequest
);
// TODO: consider leaving the snooze label
await this.github.removeIssueLabels([SNOOZE_LABEL], snoozed.number);
return updatedPullRequest;
}

/// force an update to an existing pull request
private async updateExistingPullRequest(
existing: PullRequest,
pullRequest: ReleasePullRequest
): Promise<PullRequest> {
return await this.github.updatePullRequest(
existing.number,
pullRequest,
this.targetBranch,
{
Expand All @@ -1103,9 +1117,6 @@ export class Manifest {
pullRequestOverflowHandler: this.pullRequestOverflowHandler,
}
);
// TODO: consider leaving the snooze label
await this.github.removeIssueLabels([SNOOZE_LABEL], snoozed.number);
return updatedPullRequest;
}

private async *findMergedReleasePullRequests() {
Expand Down Expand Up @@ -1428,6 +1439,7 @@ async function parseConfig(
lastReleaseSha: config['last-release-sha'],
alwaysLinkLocal: config['always-link-local'],
separatePullRequests: config['separate-pull-requests'],
alwaysUpdate: config['always-update'],
groupPullRequestTitlePattern: config['group-pull-request-title-pattern'],
plugins: config['plugins'],
signoff: config['signoff'],
Expand Down
83 changes: 83 additions & 0 deletions test/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4330,6 +4330,89 @@ describe('Manifest', () => {
const pullRequestNumbers = await manifest.createPullRequests();
expect(pullRequestNumbers).lengthOf(0);
});

it('updates an existing pull request without changes if set to always update', async function () {
sandbox
.stub(github, 'getFileContentsOnBranch')
.withArgs('README.md', 'main')
.resolves(buildGitHubFileRaw('some-content'))
.withArgs('release-notes.md', 'my-head-branch--release-notes')
.resolves(buildGitHubFileRaw(body.toString()));
stubSuggesterWithSnapshot(sandbox, this.test!.fullTitle());
mockPullRequests(
github,
[
{
number: 22,
title: 'pr title1',
body: pullRequestBody('release-notes/overflow.txt'),
headBranchName: 'release-please/branches/main',
baseBranchName: 'main',
labels: ['autorelease: pending'],
files: [],
},
],
[]
);
sandbox
.stub(github, 'updatePullRequest')
.withArgs(
22,
sinon.match.any,
sinon.match.any,
sinon.match.has('pullRequestOverflowHandler', sinon.match.truthy)
)
.resolves({
number: 22,
title: 'pr title1',
body: 'pr body1',
headBranchName: 'release-please/branches/main',
baseBranchName: 'main',
labels: [],
files: [],
});
const manifest = new Manifest(
github,
'main',
{
'path/a': {
releaseType: 'node',
component: 'pkg1',
},
'path/b': {
releaseType: 'node',
component: 'pkg2',
},
},
{
'path/a': Version.parse('1.0.0'),
'path/b': Version.parse('0.2.3'),
},
{
separatePullRequests: true,
alwaysUpdate: true,
plugins: ['node-workspace'],
}
);
sandbox.stub(manifest, 'buildPullRequests').resolves([
{
title: PullRequestTitle.ofTargetBranch('main'),
body,
updates: [
{
path: 'README.md',
createIfMissing: false,
updater: new RawContent('some raw content'),
},
],
labels: [],
headRefName: 'release-please/branches/main',
draft: false,
},
]);
const pullRequestNumbers = await manifest.createPullRequests();
expect(pullRequestNumbers).lengthOf(1);
});
});

it('updates an existing snapshot pull request', async function () {
Expand Down

0 comments on commit 72d40df

Please sign in to comment.