Skip to content

Commit 50f5c99

Browse files
authored
feat: Support sequential-calls manifest field that disables concurrency when creating multiple pull requests or releases (#1401)
When running release-please on the google-cloud-ruby manifest, we're running afoul of secondary rate limits. Currently release-please runs pull request creation concurrently, and in this case, it was trying to open 33 pull requests at once which seemed to be too much. So we're introducing an option to run them sequentially using blocking calls. This was added to the manifest config rather than the CLI options, because it seems to be a scaling-related knob that should be set per repository, rather than something that depends on how release-please is invoked. I honestly do not know how to write a test for this. I tested manually both the sequential and concurrent cases to make sure the functionality was still correct, but I'm not sure how to test whether something happened concurrently or not.
1 parent dcd1752 commit 50f5c99

3 files changed

Lines changed: 59 additions & 20 deletions

File tree

docs/cli.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ Extra options:
5656
| `--draft-pull-request` | `boolean` | If set, create pull requests as drafts |
5757
| `--label` | `string` | Comma-separated list of labels to apply to the release pull requests. Defaults to `autorelease: pending` |
5858
| `--release-label` | `string` | Comma-separated list of labels to apply to the pull request after the release has been tagged. Defaults to `autorelease: tagged` |
59-
| `--skip-labeling` | `boolean` | If set, labels will not be applied to pull requests |
6059
| `--changelog-path` | `string` | Override the path to the managed CHANGELOG. Defaults to `CHANGELOG.md` |
6160
| `--changelog-type` | [`ChangelogType`](/docs/customizing.md#changelog-types) | Strategy for building the changelog contents. Defaults to `default` |
6261
| `--changelog-sections` | `string` | Comma-separated list of commit scopes to show in changelog headings |
@@ -83,6 +82,7 @@ Extra options:
8382
| ------ | ---- | ----------- |
8483
| `--config-file` | string | Override the path to the release-please config file. Defaults to `release-please-config.json` |
8584
| `--manifest-file` | string | Override the path to the release-please manifest file. Defaults to `.release-please-manifest.json` |
85+
| `--skip-labeling` | `boolean` | If set, labels will not be applied to pull requests |
8686

8787
### Without a manifest config
8888

@@ -109,6 +109,7 @@ need to specify your release options:
109109
| `--signoff` | string | Add [`Signed-off-by`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---signoff) line at the end of the commit log message using the user and email provided. (format "Name \<email@example.com\>") |
110110
| `--extra-files` | `string[]` | Extra file paths for the release strategy to consider |
111111
| `--version-file` | `string` | Ruby only. Path to the `version.rb` file |
112+
| `--skip-labeling` | `boolean` | If set, labels will not be applied to pull requests |
112113

113114
## Creating a release on GitHub
114115

docs/manifest-releaser.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,14 @@ documented in comments)
225225
// value, but it will increase the number of API calls used.
226226
"commit-search-depth": 500,
227227

228+
// when creating multiple pull requests or releases, issue GitHub API requests
229+
// sequentially rather than concurrently, waiting for the previous request to
230+
// complete before issuing the next one.
231+
// This option may reduce failures due to throttling on repositories releasing
232+
// large numbers of packages at once.
233+
// absence defaults to false, causing calls to be issued concurrently.
234+
"sequential-calls": false,
235+
228236
// per package configuration: at least one entry required.
229237
// the key is the relative path from the repo root to the folder that contains
230238
// all the files for that package.

src/manifest.ts

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,10 @@ export interface ManifestOptions {
134134
signoff?: string;
135135
manifestPath?: string;
136136
labels?: string[];
137-
skipLabeling?: boolean;
138137
releaseLabels?: string[];
139138
snapshotLabels?: string[];
139+
skipLabeling?: boolean;
140+
sequentialCalls?: boolean;
140141
draft?: boolean;
141142
prerelease?: boolean;
142143
draftPullRequest?: boolean;
@@ -178,6 +179,7 @@ export interface ManifestConfig extends ReleaserConfigJson {
178179
'group-pull-request-title-pattern'?: string;
179180
'release-search-depth'?: number;
180181
'commit-search-depth'?: number;
182+
'sequential-calls'?: boolean;
181183
}
182184
// path => version
183185
export type ReleasedVersions = Record<string, Version>;
@@ -216,6 +218,7 @@ export class Manifest {
216218
private signoffUser?: string;
217219
private labels: string[];
218220
private skipLabeling?: boolean;
221+
private sequentialCalls?: boolean;
219222
private releaseLabels: string[];
220223
private snapshotLabels: string[];
221224
private plugins: PluginType[];
@@ -280,6 +283,7 @@ export class Manifest {
280283
manifestOptions?.releaseLabels || DEFAULT_RELEASE_LABELS;
281284
this.labels = manifestOptions?.labels || DEFAULT_LABELS;
282285
this.skipLabeling = manifestOptions?.skipLabeling || false;
286+
this.sequentialCalls = manifestOptions?.sequentialCalls || false;
283287
this.snapshotLabels =
284288
manifestOptions?.snapshotLabels || DEFAULT_SNAPSHOT_LABELS;
285289
this.bootstrapSha = manifestOptions?.bootstrapSha;
@@ -720,7 +724,7 @@ export class Manifest {
720724
/**
721725
* Opens/updates all candidate release pull requests for this repository.
722726
*
723-
* @returns {number[]} Pull request numbers of release pull requests
727+
* @returns {PullRequest[]} Pull request numbers of release pull requests
724728
*/
725729
async createPullRequests(): Promise<(PullRequest | undefined)[]> {
726730
const candidatePullRequests = await this.buildPullRequests();
@@ -742,19 +746,32 @@ export class Manifest {
742746
const openPullRequests = await this.findOpenReleasePullRequests();
743747
const snoozedPullRequests = await this.findSnoozedReleasePullRequests();
744748

745-
const promises: Promise<PullRequest | undefined>[] = [];
746-
for (const pullRequest of candidatePullRequests) {
747-
promises.push(
748-
this.createOrUpdatePullRequest(
749+
if (this.sequentialCalls) {
750+
const pullRequests: PullRequest[] = [];
751+
for (const pullRequest of candidatePullRequests) {
752+
const resultPullRequest = await this.createOrUpdatePullRequest(
749753
pullRequest,
750754
openPullRequests,
751755
snoozedPullRequests
752-
)
753-
);
756+
);
757+
if (resultPullRequest) pullRequests.push(resultPullRequest);
758+
}
759+
return pullRequests;
760+
} else {
761+
const promises: Promise<PullRequest | undefined>[] = [];
762+
for (const pullRequest of candidatePullRequests) {
763+
promises.push(
764+
this.createOrUpdatePullRequest(
765+
pullRequest,
766+
openPullRequests,
767+
snoozedPullRequests
768+
)
769+
);
770+
}
771+
const pullNumbers = await Promise.all(promises);
772+
// reject any pull numbers that were not created or updated
773+
return pullNumbers.filter(number => !!number);
754774
}
755-
const pullNumbers = await Promise.all(promises);
756-
// reject any pull numbers that were not created or updated
757-
return pullNumbers.filter(number => !!number);
758775
}
759776

760777
private async findOpenReleasePullRequests(): Promise<PullRequest[]> {
@@ -968,17 +985,29 @@ export class Manifest {
968985
}
969986
}
970987

971-
const promises: Promise<CreatedRelease[]>[] = [];
972-
for (const pullNumber in releasesByPullRequest) {
973-
promises.push(
974-
this.createReleasesForPullRequest(
988+
if (this.sequentialCalls) {
989+
const resultReleases: CreatedRelease[] = [];
990+
for (const pullNumber in releasesByPullRequest) {
991+
const releases = await this.createReleasesForPullRequest(
975992
releasesByPullRequest[pullNumber],
976993
pullRequestsByNumber[pullNumber]
977-
)
978-
);
994+
);
995+
resultReleases.concat(releases);
996+
}
997+
return resultReleases;
998+
} else {
999+
const promises: Promise<CreatedRelease[]>[] = [];
1000+
for (const pullNumber in releasesByPullRequest) {
1001+
promises.push(
1002+
this.createReleasesForPullRequest(
1003+
releasesByPullRequest[pullNumber],
1004+
pullRequestsByNumber[pullNumber]
1005+
)
1006+
);
1007+
}
1008+
const releases = await Promise.all(promises);
1009+
return releases.reduce((collection, r) => collection.concat(r), []);
9791010
}
980-
const releases = await Promise.all(promises);
981-
return releases.reduce((collection, r) => collection.concat(r), []);
9821011
}
9831012

9841013
private async createReleasesForPullRequest(
@@ -1158,6 +1187,7 @@ async function parseConfig(
11581187
configSnapshotLabel === undefined ? undefined : [configSnapshotLabel],
11591188
releaseSearchDepth: config['release-search-depth'],
11601189
commitSearchDepth: config['commit-search-depth'],
1190+
sequentialCalls: config['sequential-calls'],
11611191
};
11621192
return {config: repositoryConfig, options: manifestOptions};
11631193
}

0 commit comments

Comments
 (0)