release-please 17.4.0 → 17.5.0

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.
Files changed (34) hide show
  1. package/build/src/bin/release-please.js +39 -10
  2. package/build/src/bootstrapper.d.ts +2 -2
  3. package/build/src/changelog-notes/default.js +9 -1
  4. package/build/src/changelog-notes/github.d.ts +2 -2
  5. package/build/src/changelog-notes.d.ts +1 -0
  6. package/build/src/commit.d.ts +6 -0
  7. package/build/src/factories/changelog-notes-factory.d.ts +2 -2
  8. package/build/src/factories/plugin-factory.d.ts +2 -2
  9. package/build/src/factories/versioning-strategy-factory.d.ts +2 -2
  10. package/build/src/factory.d.ts +2 -2
  11. package/build/src/github-api.d.ts +260 -0
  12. package/build/src/github-api.js +701 -0
  13. package/build/src/github.d.ts +21 -171
  14. package/build/src/github.js +154 -687
  15. package/build/src/index.d.ts +2 -2
  16. package/build/src/index.js +1 -1
  17. package/build/src/local-github.d.ts +271 -0
  18. package/build/src/local-github.js +776 -0
  19. package/build/src/manifest.d.ts +7 -5
  20. package/build/src/manifest.js +28 -26
  21. package/build/src/plugin.d.ts +3 -3
  22. package/build/src/plugins/group-priority.d.ts +2 -2
  23. package/build/src/plugins/linked-versions.d.ts +2 -2
  24. package/build/src/plugins/maven-workspace.d.ts +2 -2
  25. package/build/src/plugins/merge.d.ts +2 -2
  26. package/build/src/plugins/node-workspace.d.ts +2 -2
  27. package/build/src/plugins/sentence-case.d.ts +2 -2
  28. package/build/src/plugins/workspace.d.ts +2 -2
  29. package/build/src/scm.d.ts +80 -0
  30. package/build/src/scm.js +16 -0
  31. package/build/src/strategies/base.d.ts +5 -3
  32. package/build/src/strategies/base.js +2 -0
  33. package/build/src/util/pull-request-overflow-handler.d.ts +2 -2
  34. package/package.json +3 -3
@@ -13,23 +13,17 @@
13
13
  // See the License for the specific language governing permissions and
14
14
  // limitations under the License.
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.sleepInMs = exports.GitHub = exports.GH_GRAPHQL_URL = exports.GH_API_URL = void 0;
17
- const code_suggester_1 = require("code-suggester");
18
- const rest_1 = require("@octokit/rest");
19
- const request_1 = require("@octokit/request");
20
- const graphql_1 = require("@octokit/graphql");
16
+ exports.sleepInMs = exports.GitHub = void 0;
21
17
  const request_error_1 = require("@octokit/request-error");
18
+ const code_suggester_1 = require("code-suggester");
22
19
  const errors_1 = require("./errors");
23
20
  const MAX_ISSUE_BODY_SIZE = 65536;
24
21
  const MAX_SLEEP_SECONDS = 20;
25
- exports.GH_API_URL = 'https://api.github.com';
26
- exports.GH_GRAPHQL_URL = 'https://api.github.com';
27
22
  const logger_1 = require("./util/logger");
28
23
  const manifest_1 = require("./manifest");
24
+ const github_api_1 = require("./github-api");
29
25
  const signoff_commit_message_1 = require("./util/signoff-commit-message");
30
26
  const git_file_utils_1 = require("@google-automations/git-file-utils");
31
- const https_proxy_agent_1 = require("https-proxy-agent");
32
- const http_proxy_agent_1 = require("http-proxy-agent");
33
27
  const composite_1 = require("./updaters/composite");
34
28
  class GitHub {
35
29
  constructor(options) {
@@ -133,118 +127,6 @@ class GitHub {
133
127
  this.logger.debug(`finding files by glob: ${glob}, ref: ${ref}, prefix: ${prefix}`);
134
128
  return await this.fileCache.findFilesByGlob(glob, ref, prefix);
135
129
  });
136
- /**
137
- * Open a pull request
138
- *
139
- * @param {PullRequest} pullRequest Pull request data to update
140
- * @param {string} targetBranch The base branch of the pull request
141
- * @param {string} message The commit message for the commit
142
- * @param {Update[]} updates The files to update
143
- * @param {CreatePullRequestOptions} options The pull request options
144
- * @throws {GitHubAPIError} on an API error
145
- */
146
- this.createPullRequest = wrapAsync(async (pullRequest, targetBranch, message, updates, options) => {
147
- // Update the files for the release if not already supplied
148
- const changes = await this.buildChangeSet(updates, targetBranch);
149
- const prNumber = await (0, code_suggester_1.createPullRequest)(this.octokit, changes, {
150
- upstreamOwner: this.repository.owner,
151
- upstreamRepo: this.repository.repo,
152
- title: pullRequest.title,
153
- branch: pullRequest.headBranchName,
154
- description: pullRequest.body,
155
- primary: targetBranch,
156
- force: true,
157
- fork: !!(options === null || options === void 0 ? void 0 : options.fork),
158
- message,
159
- logger: this.logger,
160
- draft: !!(options === null || options === void 0 ? void 0 : options.draft),
161
- labels: pullRequest.labels,
162
- });
163
- return await this.getPullRequest(prNumber);
164
- });
165
- /**
166
- * Fetch a pull request given the pull number
167
- * @param {number} number The pull request number
168
- * @returns {PullRequest}
169
- */
170
- this.getPullRequest = wrapAsync(async (number) => {
171
- const response = await this.octokit.pulls.get({
172
- owner: this.repository.owner,
173
- repo: this.repository.repo,
174
- pull_number: number,
175
- });
176
- return {
177
- headBranchName: response.data.head.ref,
178
- baseBranchName: response.data.base.ref,
179
- number: response.data.number,
180
- title: response.data.title,
181
- body: response.data.body || '',
182
- files: [],
183
- labels: response.data.labels
184
- .map(label => label.name)
185
- .filter(name => !!name),
186
- };
187
- });
188
- /**
189
- * Update a pull request's title and body.
190
- * @param {number} number The pull request number
191
- * @param {ReleasePullRequest} releasePullRequest Pull request data to update
192
- * @param {string} targetBranch The target branch of the pull request
193
- * @param {string} options.signoffUser Optional. Commit signoff message
194
- * @param {boolean} options.fork Optional. Whether to open the pull request from
195
- * a fork or not. Defaults to `false`
196
- * @param {PullRequestOverflowHandler} options.pullRequestOverflowHandler Optional.
197
- * Handles extra large pull request body messages.
198
- */
199
- this.updatePullRequest = wrapAsync(async (number, releasePullRequest, targetBranch, options) => {
200
- // Update the files for the release if not already supplied
201
- const changes = await this.buildChangeSet(releasePullRequest.updates, targetBranch);
202
- let message = releasePullRequest.title.toString();
203
- if (options === null || options === void 0 ? void 0 : options.signoffUser) {
204
- message = (0, signoff_commit_message_1.signoffCommitMessage)(message, options.signoffUser);
205
- }
206
- const title = releasePullRequest.title.toString();
207
- const body = ((options === null || options === void 0 ? void 0 : options.pullRequestOverflowHandler)
208
- ? await options.pullRequestOverflowHandler.handleOverflow(releasePullRequest)
209
- : releasePullRequest.body)
210
- .toString()
211
- .slice(0, MAX_ISSUE_BODY_SIZE);
212
- const prNumber = await (0, code_suggester_1.createPullRequest)(this.octokit, changes, {
213
- upstreamOwner: this.repository.owner,
214
- upstreamRepo: this.repository.repo,
215
- title,
216
- branch: releasePullRequest.headRefName,
217
- description: body,
218
- primary: targetBranch,
219
- force: true,
220
- fork: (options === null || options === void 0 ? void 0 : options.fork) === false ? false : true,
221
- message,
222
- logger: this.logger,
223
- draft: releasePullRequest.draft,
224
- });
225
- if (prNumber !== number) {
226
- this.logger.warn(`updated code for ${prNumber}, but update requested for ${number}`);
227
- }
228
- const response = await this.octokit.pulls.update({
229
- owner: this.repository.owner,
230
- repo: this.repository.repo,
231
- pull_number: number,
232
- title: releasePullRequest.title.toString(),
233
- body,
234
- state: 'open',
235
- });
236
- return {
237
- headBranchName: response.data.head.ref,
238
- baseBranchName: response.data.base.ref,
239
- number: response.data.number,
240
- title: response.data.title,
241
- body: response.data.body || '',
242
- files: [],
243
- labels: response.data.labels
244
- .map(label => label.name)
245
- .filter(name => !!name),
246
- };
247
- });
248
130
  /**
249
131
  * Returns a list of paths to all files with a given file
250
132
  * extension.
@@ -265,213 +147,27 @@ class GitHub {
265
147
  }
266
148
  return this.fileCache.findFilesByExtension(extension, ref, prefix);
267
149
  });
268
- /**
269
- * Create a GitHub release
270
- *
271
- * @param {Release} release Release parameters
272
- * @param {ReleaseOptions} options Release option parameters
273
- * @throws {DuplicateReleaseError} if the release tag already exists
274
- * @throws {GitHubAPIError} on other API errors
275
- */
276
- this.createRelease = wrapAsync(async (release, options = {}) => {
277
- if (options.forceTag) {
278
- try {
279
- await this.octokit.git.createRef({
280
- owner: this.repository.owner,
281
- repo: this.repository.repo,
282
- ref: `refs/tags/${release.tag.toString()}`,
283
- sha: release.sha,
284
- });
285
- }
286
- catch (err) {
287
- // ignore if tag already exists
288
- if (err.status === 422) {
289
- this.logger.debug(`Tag ${release.tag.toString()} already exists, skipping tag creation`);
290
- }
291
- else {
292
- throw err;
293
- }
294
- }
295
- }
296
- const resp = await this.octokit.repos.createRelease({
297
- name: release.name,
298
- owner: this.repository.owner,
299
- repo: this.repository.repo,
300
- tag_name: release.tag.toString(),
301
- body: release.notes,
302
- draft: !!options.draft,
303
- prerelease: !!options.prerelease,
304
- target_commitish: release.sha,
305
- });
306
- return {
307
- id: resp.data.id,
308
- name: resp.data.name || undefined,
309
- tagName: resp.data.tag_name,
310
- sha: resp.data.target_commitish,
311
- notes: resp.data.body_text ||
312
- resp.data.body ||
313
- resp.data.body_html ||
314
- undefined,
315
- url: resp.data.html_url,
316
- draft: resp.data.draft,
317
- uploadUrl: resp.data.upload_url,
318
- };
319
- }, e => {
320
- if (e instanceof request_error_1.RequestError) {
321
- if (e.status === 422 &&
322
- errors_1.GitHubAPIError.parseErrors(e).some(error => {
323
- return error.code === 'already_exists';
324
- })) {
325
- throw new errors_1.DuplicateReleaseError(e, 'tagName');
326
- }
327
- }
328
- });
329
- /**
330
- * Makes a comment on a issue/pull request.
331
- *
332
- * @param {string} comment - The body of the comment to post.
333
- * @param {number} number - The issue or pull request number.
334
- * @throws {GitHubAPIError} on an API error
335
- */
336
- this.commentOnIssue = wrapAsync(async (comment, number) => {
337
- this.logger.debug(`adding comment to https://github.com/${this.repository.owner}/${this.repository.repo}/issues/${number}`);
338
- const resp = await this.octokit.issues.createComment({
339
- owner: this.repository.owner,
340
- repo: this.repository.repo,
341
- issue_number: number,
342
- body: comment,
343
- });
344
- return resp.data.html_url;
345
- });
346
- /**
347
- * Removes labels from an issue/pull request.
348
- *
349
- * @param {string[]} labels The labels to remove.
350
- * @param {number} number The issue/pull request number.
351
- */
352
- this.removeIssueLabels = wrapAsync(async (labels, number) => {
353
- if (labels.length === 0) {
354
- return;
355
- }
356
- this.logger.debug(`removing labels: ${labels} from issue/pull ${number}`);
357
- await Promise.all(labels.map(label => this.octokit.issues.removeLabel({
358
- owner: this.repository.owner,
359
- repo: this.repository.repo,
360
- issue_number: number,
361
- name: label,
362
- })));
363
- });
364
- /**
365
- * Adds label to an issue/pull request.
366
- *
367
- * @param {string[]} labels The labels to add.
368
- * @param {number} number The issue/pull request number.
369
- */
370
- this.addIssueLabels = wrapAsync(async (labels, number) => {
371
- if (labels.length === 0) {
372
- return;
373
- }
374
- this.logger.debug(`adding labels: ${labels} from issue/pull ${number}`);
375
- await this.octokit.issues.addLabels({
376
- owner: this.repository.owner,
377
- repo: this.repository.repo,
378
- issue_number: number,
379
- labels,
380
- });
381
- });
382
150
  this.repository = options.repository;
383
151
  this.octokit = options.octokitAPIs.octokit;
384
- this.request = options.octokitAPIs.request;
385
152
  this.graphql = options.octokitAPIs.graphql;
386
153
  this.fileCache = new git_file_utils_1.RepositoryFileCache(this.octokit, this.repository);
387
154
  this.logger = (_a = options.logger) !== null && _a !== void 0 ? _a : logger_1.logger;
155
+ this.gitHubApi = new github_api_1.GitHubApi({
156
+ repository: this.repository,
157
+ octokitAPIs: options.octokitAPIs,
158
+ logger: this.logger,
159
+ });
388
160
  }
389
- static createDefaultAgent(baseUrl, defaultProxy) {
390
- if (!defaultProxy) {
391
- return undefined;
392
- }
393
- const { host, port } = defaultProxy;
394
- if (new URL(baseUrl).protocol.replace(':', '') === 'http') {
395
- return new http_proxy_agent_1.HttpProxyAgent(`http://${host}:${port}`);
396
- }
397
- else {
398
- return new https_proxy_agent_1.HttpsProxyAgent(`https://${host}:${port}`);
399
- }
161
+ getGitHubApi() {
162
+ return this.gitHubApi;
400
163
  }
401
- /**
402
- * Build a new GitHub client with auto-detected default branch.
403
- *
404
- * @param {GitHubCreateOptions} options Configuration options
405
- * @param {string} options.owner The repository owner.
406
- * @param {string} options.repo The repository name.
407
- * @param {string} options.defaultBranch Optional. The repository's default branch.
408
- * Defaults to the value fetched via the API.
409
- * @param {string} options.apiUrl Optional. The base url of the GitHub API.
410
- * @param {string} options.graphqlUrl Optional. The base url of the GraphQL API.
411
- * @param {OctokitAPISs} options.octokitAPIs Optional. Override the internal
412
- * client instances with a pre-authenticated instance.
413
- * @param {string} token Optional. A GitHub API token used for authentication.
414
- */
415
164
  static async create(options) {
416
- var _a, _b, _c, _d;
417
- const apiUrl = (_a = options.apiUrl) !== null && _a !== void 0 ? _a : exports.GH_API_URL;
418
- const graphqlUrl = (_b = options.graphqlUrl) !== null && _b !== void 0 ? _b : exports.GH_GRAPHQL_URL;
419
- const releasePleaseVersion = require('../../package.json').version;
420
- const apis = (_c = options.octokitAPIs) !== null && _c !== void 0 ? _c : {
421
- octokit: new rest_1.Octokit({
422
- baseUrl: apiUrl,
423
- auth: options.token,
424
- request: {
425
- agent: this.createDefaultAgent(apiUrl, options.proxy),
426
- fetch: options.fetch,
427
- },
428
- }),
429
- request: request_1.request.defaults({
430
- baseUrl: apiUrl,
431
- headers: {
432
- 'user-agent': `release-please/${releasePleaseVersion}`,
433
- Authorization: `token ${options.token}`,
434
- },
435
- fetch: options.fetch,
436
- }),
437
- graphql: graphql_1.graphql.defaults({
438
- baseUrl: graphqlUrl,
439
- request: {
440
- agent: this.createDefaultAgent(graphqlUrl, options.proxy),
441
- fetch: options.fetch,
442
- },
443
- headers: {
444
- 'user-agent': `release-please/${releasePleaseVersion}`,
445
- Authorization: `token ${options.token}`,
446
- 'content-type': 'application/vnd.github.v3+json',
447
- },
448
- }),
449
- };
450
- const opts = {
451
- repository: {
452
- owner: options.owner,
453
- repo: options.repo,
454
- defaultBranch: (_d = options.defaultBranch) !== null && _d !== void 0 ? _d : (await GitHub.defaultBranch(options.owner, options.repo, apis.octokit)),
455
- },
456
- octokitAPIs: apis,
165
+ const gitHubApi = await github_api_1.GitHubApi.create(options);
166
+ return new GitHub({
167
+ repository: gitHubApi.repository,
168
+ octokitAPIs: gitHubApi.octokitAPIs,
457
169
  logger: options.logger,
458
- };
459
- return new GitHub(opts);
460
- }
461
- /**
462
- * Returns the default branch for a given repository.
463
- *
464
- * @param {string} owner The GitHub repository owner
465
- * @param {string} repo The GitHub repository name
466
- * @param {OctokitType} octokit An authenticated octokit instance
467
- * @returns {string} Name of the default branch
468
- */
469
- static async defaultBranch(owner, repo, octokit) {
470
- const { data } = await octokit.repos.get({
471
- repo,
472
- owner,
473
170
  });
474
- return data.default_branch;
475
171
  }
476
172
  /**
477
173
  * Returns the list of commits to the default branch after the provided filter
@@ -533,8 +229,8 @@ class GitHub {
533
229
  }
534
230
  }
535
231
  async mergeCommitsGraphQL(targetBranch, cursor, options = {}) {
536
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
537
- var _k;
232
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
233
+ var _l;
538
234
  this.logger.debug(`Fetching merge commits on branch ${targetBranch} with cursor: ${cursor}`);
539
235
  const query = `query pullRequestsSince($owner: String!, $repo: String!, $num: Int!, $maxFilesChanged: Int, $targetBranch: String!, $cursor: String) {
540
236
  repository(owner: $owner, name: $repo) {
@@ -571,6 +267,13 @@ class GitHub {
571
267
  }
572
268
  sha: oid
573
269
  message
270
+ author {
271
+ name
272
+ email
273
+ user {
274
+ login
275
+ }
276
+ }
574
277
  }
575
278
  pageInfo {
576
279
  hasNextPage
@@ -612,7 +315,7 @@ class GitHub {
612
315
  for (const commit of commits) {
613
316
  for (const pr of commit.associatedPullRequests.nodes) {
614
317
  if ((_c = pr.mergeCommit) === null || _c === void 0 ? void 0 : _c.oid) {
615
- (_d = mergeCommitCount[_k = pr.mergeCommit.oid]) !== null && _d !== void 0 ? _d : (mergeCommitCount[_k] = 0);
318
+ (_d = mergeCommitCount[_l = pr.mergeCommit.oid]) !== null && _d !== void 0 ? _d : (mergeCommitCount[_l] = 0);
616
319
  mergeCommitCount[pr.mergeCommit.oid]++;
617
320
  }
618
321
  }
@@ -622,6 +325,13 @@ class GitHub {
622
325
  const commit = {
623
326
  sha: graphCommit.sha,
624
327
  message: graphCommit.message,
328
+ author: graphCommit.author
329
+ ? {
330
+ name: graphCommit.author.name || 'Unknown',
331
+ email: graphCommit.author.email,
332
+ username: (_e = graphCommit.author.user) === null || _e === void 0 ? void 0 : _e.login,
333
+ }
334
+ : undefined,
625
335
  };
626
336
  const mergePullRequest = graphCommit.associatedPullRequests.nodes.find(pr => {
627
337
  return (
@@ -642,15 +352,15 @@ class GitHub {
642
352
  number: pullRequest.number,
643
353
  baseBranchName: pullRequest.baseRefName,
644
354
  headBranchName: pullRequest.headRefName,
645
- mergeCommitOid: (_e = pullRequest.mergeCommit) === null || _e === void 0 ? void 0 : _e.oid,
355
+ mergeCommitOid: (_f = pullRequest.mergeCommit) === null || _f === void 0 ? void 0 : _f.oid,
646
356
  title: pullRequest.title,
647
357
  body: pullRequest.body,
648
358
  labels: pullRequest.labels.nodes.map(node => node.name),
649
- files: (((_f = pullRequest.files) === null || _f === void 0 ? void 0 : _f.nodes) || []).map(node => node.path),
359
+ files: (((_g = pullRequest.files) === null || _g === void 0 ? void 0 : _g.nodes) || []).map(node => node.path),
650
360
  };
651
361
  }
652
362
  if (mergePullRequest) {
653
- if (((_h = (_g = mergePullRequest.files) === null || _g === void 0 ? void 0 : _g.pageInfo) === null || _h === void 0 ? void 0 : _h.hasNextPage) &&
363
+ if (((_j = (_h = mergePullRequest.files) === null || _h === void 0 ? void 0 : _h.pageInfo) === null || _j === void 0 ? void 0 : _j.hasNextPage) &&
654
364
  options.backfillFiles) {
655
365
  this.logger.info(`PR #${mergePullRequest.number} has many files, backfilling`);
656
366
  commit.files = await this.getCommitFiles(graphCommit.sha);
@@ -658,7 +368,7 @@ class GitHub {
658
368
  else {
659
369
  // We cannot directly fetch files on commits via graphql, only provide file
660
370
  // information for commits with associated pull requests
661
- commit.files = (((_j = mergePullRequest.files) === null || _j === void 0 ? void 0 : _j.nodes) || []).map(node => node.path);
371
+ commit.files = (((_k = mergePullRequest.files) === null || _k === void 0 ? void 0 : _k.nodes) || []).map(node => node.path);
662
372
  }
663
373
  }
664
374
  else if (options.backfillFiles) {
@@ -688,167 +398,7 @@ class GitHub {
688
398
  * @throws {GitHubAPIError} on an API error
689
399
  */
690
400
  async *pullRequestIterator(targetBranch, status = 'MERGED', maxResults = Number.MAX_SAFE_INTEGER, includeFiles = true) {
691
- const generator = includeFiles
692
- ? this.pullRequestIteratorWithFiles(targetBranch, status, maxResults)
693
- : this.pullRequestIteratorWithoutFiles(targetBranch, status, maxResults);
694
- for await (const pullRequest of generator) {
695
- yield pullRequest;
696
- }
697
- }
698
- /**
699
- * Helper implementation of pullRequestIterator that includes files via
700
- * the graphQL API.
701
- *
702
- * @param {string} targetBranch The base branch of the pull request
703
- * @param {string} status The status of the pull request
704
- * @param {number} maxResults Limit the number of results searched
705
- */
706
- async *pullRequestIteratorWithFiles(targetBranch, status = 'MERGED', maxResults = Number.MAX_SAFE_INTEGER) {
707
- let cursor = undefined;
708
- let results = 0;
709
- while (results < maxResults) {
710
- const response = await this.pullRequestsGraphQL(targetBranch, status, cursor);
711
- // no response usually means we ran out of results
712
- if (!response) {
713
- break;
714
- }
715
- for (let i = 0; i < response.data.length; i++) {
716
- results += 1;
717
- yield response.data[i];
718
- }
719
- if (!response.pageInfo.hasNextPage) {
720
- break;
721
- }
722
- cursor = response.pageInfo.endCursor;
723
- }
724
- }
725
- /**
726
- * Helper implementation of pullRequestIterator that excludes files
727
- * via the REST API.
728
- *
729
- * @param {string} targetBranch The base branch of the pull request
730
- * @param {string} status The status of the pull request
731
- * @param {number} maxResults Limit the number of results searched
732
- */
733
- async *pullRequestIteratorWithoutFiles(targetBranch, status = 'MERGED', maxResults = Number.MAX_SAFE_INTEGER) {
734
- const statusMap = {
735
- OPEN: 'open',
736
- CLOSED: 'closed',
737
- MERGED: 'closed',
738
- };
739
- let results = 0;
740
- for await (const { data: pulls } of this.octokit.paginate.iterator('GET /repos/{owner}/{repo}/pulls', {
741
- state: statusMap[status],
742
- owner: this.repository.owner,
743
- repo: this.repository.repo,
744
- base: targetBranch,
745
- sort: 'updated',
746
- direction: 'desc',
747
- })) {
748
- for (const pull of pulls) {
749
- // The REST API does not have an option for "merged"
750
- // pull requests - they are closed with a `merged_at` timestamp
751
- if (status !== 'MERGED' || pull.merged_at) {
752
- results += 1;
753
- yield {
754
- headBranchName: pull.head.ref,
755
- baseBranchName: pull.base.ref,
756
- number: pull.number,
757
- title: pull.title,
758
- body: pull.body || '',
759
- labels: pull.labels.map(label => label.name),
760
- files: [],
761
- sha: pull.merge_commit_sha || undefined,
762
- };
763
- if (results >= maxResults) {
764
- break;
765
- }
766
- }
767
- }
768
- if (results >= maxResults) {
769
- break;
770
- }
771
- }
772
- }
773
- /**
774
- * Return a list of merged pull requests. The list is not guaranteed to be sorted
775
- * by merged_at, but is generally most recent first.
776
- *
777
- * @param {string} targetBranch - Base branch of the pull request. Defaults to
778
- * the configured default branch.
779
- * @param {number} page - Page of results. Defaults to 1.
780
- * @param {number} perPage - Number of results per page. Defaults to 100.
781
- * @returns {PullRequestHistory | null} - List of merged pull requests
782
- * @throws {GitHubAPIError} on an API error
783
- */
784
- async pullRequestsGraphQL(targetBranch, states = 'MERGED', cursor) {
785
- var _a;
786
- this.logger.debug(`Fetching ${states} pull requests on branch ${targetBranch} with cursor ${cursor}`);
787
- const response = await this.graphqlRequest({
788
- query: `query mergedPullRequests($owner: String!, $repo: String!, $num: Int!, $maxFilesChanged: Int, $targetBranch: String!, $states: [PullRequestState!], $cursor: String) {
789
- repository(owner: $owner, name: $repo) {
790
- pullRequests(first: $num, after: $cursor, baseRefName: $targetBranch, states: $states, orderBy: {field: CREATED_AT, direction: DESC}) {
791
- nodes {
792
- number
793
- title
794
- baseRefName
795
- headRefName
796
- labels(first: 10) {
797
- nodes {
798
- name
799
- }
800
- }
801
- body
802
- mergeCommit {
803
- oid
804
- }
805
- files(first: $maxFilesChanged) {
806
- nodes {
807
- path
808
- }
809
- pageInfo {
810
- endCursor
811
- hasNextPage
812
- }
813
- }
814
- }
815
- pageInfo {
816
- endCursor
817
- hasNextPage
818
- }
819
- }
820
- }
821
- }`,
822
- cursor,
823
- owner: this.repository.owner,
824
- repo: this.repository.repo,
825
- num: 25,
826
- targetBranch,
827
- states,
828
- maxFilesChanged: 64,
829
- });
830
- if (!((_a = response === null || response === void 0 ? void 0 : response.repository) === null || _a === void 0 ? void 0 : _a.pullRequests)) {
831
- this.logger.warn(`Could not find merged pull requests for branch ${targetBranch} - it likely does not exist.`);
832
- return null;
833
- }
834
- const pullRequests = (response.repository.pullRequests.nodes ||
835
- []);
836
- return {
837
- pageInfo: response.repository.pullRequests.pageInfo,
838
- data: pullRequests.map(pullRequest => {
839
- var _a, _b, _c;
840
- return {
841
- sha: (_a = pullRequest.mergeCommit) === null || _a === void 0 ? void 0 : _a.oid,
842
- number: pullRequest.number,
843
- baseBranchName: pullRequest.baseRefName,
844
- headBranchName: pullRequest.headRefName,
845
- labels: (((_b = pullRequest.labels) === null || _b === void 0 ? void 0 : _b.nodes) || []).map(l => l.name),
846
- title: pullRequest.title,
847
- body: pullRequest.body + '',
848
- files: (((_c = pullRequest.files) === null || _c === void 0 ? void 0 : _c.nodes) || []).map(node => node.path),
849
- };
850
- }),
851
- };
401
+ yield* this.gitHubApi.pullRequestIterator(targetBranch, status, maxResults, includeFiles);
852
402
  }
853
403
  /**
854
404
  * Iterate through releases with a max number of results scanned.
@@ -860,80 +410,7 @@ class GitHub {
860
410
  * @throws {GitHubAPIError} on an API error
861
411
  */
862
412
  async *releaseIterator(options = {}) {
863
- var _a;
864
- const maxResults = (_a = options.maxResults) !== null && _a !== void 0 ? _a : Number.MAX_SAFE_INTEGER;
865
- let results = 0;
866
- let cursor = undefined;
867
- while (true) {
868
- const response = await this.releaseGraphQL(cursor);
869
- if (!response) {
870
- break;
871
- }
872
- for (let i = 0; i < response.data.length; i++) {
873
- if ((results += 1) > maxResults) {
874
- break;
875
- }
876
- yield response.data[i];
877
- }
878
- if (results > maxResults || !response.pageInfo.hasNextPage) {
879
- break;
880
- }
881
- cursor = response.pageInfo.endCursor;
882
- }
883
- }
884
- async releaseGraphQL(cursor) {
885
- this.logger.debug(`Fetching releases with cursor ${cursor}`);
886
- const response = await this.graphqlRequest({
887
- query: `query releases($owner: String!, $repo: String!, $num: Int!, $cursor: String) {
888
- repository(owner: $owner, name: $repo) {
889
- releases(first: $num, after: $cursor, orderBy: {field: CREATED_AT, direction: DESC}) {
890
- nodes {
891
- name
892
- tag {
893
- name
894
- }
895
- tagCommit {
896
- oid
897
- }
898
- url
899
- description
900
- isDraft
901
- }
902
- pageInfo {
903
- endCursor
904
- hasNextPage
905
- }
906
- }
907
- }
908
- }`,
909
- cursor,
910
- owner: this.repository.owner,
911
- repo: this.repository.repo,
912
- num: 25,
913
- });
914
- if (!response.repository.releases.nodes.length) {
915
- this.logger.warn('Could not find releases.');
916
- return null;
917
- }
918
- const releases = response.repository.releases.nodes;
919
- return {
920
- pageInfo: response.repository.releases.pageInfo,
921
- data: releases
922
- .filter(release => !!release.tagCommit)
923
- .map(release => {
924
- if (!release.tag || !release.tagCommit) {
925
- this.logger.debug(release);
926
- }
927
- return {
928
- name: release.name || undefined,
929
- tagName: release.tag ? release.tag.name : 'unknown',
930
- sha: release.tagCommit.oid,
931
- notes: release.description,
932
- url: release.url,
933
- draft: release.isDraft,
934
- };
935
- }),
936
- };
413
+ yield* this.gitHubApi.releaseIterator(options);
937
414
  }
938
415
  /**
939
416
  * Iterate through tags with a max number of results scanned.
@@ -1030,33 +507,91 @@ class GitHub {
1030
507
  /**
1031
508
  * Open a pull request
1032
509
  *
1033
- * @deprecated This logic is handled by the Manifest class now as it
1034
- * can be more complicated if the release notes are too big
1035
- * @param {ReleasePullRequest} releasePullRequest Pull request data to update
510
+ * @param {PullRequest} pullRequest Pull request data to update
1036
511
  * @param {string} targetBranch The base branch of the pull request
1037
- * @param {GitHubPR} options The pull request options
512
+ * @param {string} message The commit message for the commit
513
+ * @param {Update[]} updates The files to update
514
+ * @param {CreatePullRequestOptions} options The pull request options
1038
515
  * @throws {GitHubAPIError} on an API error
1039
516
  */
1040
- async createReleasePullRequest(releasePullRequest, targetBranch, options) {
517
+ async createPullRequest(pullRequest, targetBranch, message, updates, options) {
518
+ const changes = await this.buildChangeSet(updates, targetBranch);
519
+ const prNumber = await (0, code_suggester_1.createPullRequest)(this.octokit, changes, {
520
+ upstreamOwner: this.repository.owner,
521
+ upstreamRepo: this.repository.repo,
522
+ title: pullRequest.title,
523
+ branch: pullRequest.headBranchName,
524
+ description: pullRequest.body,
525
+ primary: targetBranch,
526
+ force: true,
527
+ fork: !!(options === null || options === void 0 ? void 0 : options.fork),
528
+ message,
529
+ logger: this.logger,
530
+ draft: !!(options === null || options === void 0 ? void 0 : options.draft),
531
+ labels: pullRequest.labels,
532
+ });
533
+ if (prNumber === 0) {
534
+ this.logger.warn('no code changes detected, skipping pull request creation');
535
+ return {
536
+ headBranchName: pullRequest.headBranchName,
537
+ baseBranchName: targetBranch,
538
+ number: 0,
539
+ title: pullRequest.title,
540
+ body: pullRequest.body,
541
+ labels: pullRequest.labels,
542
+ files: [],
543
+ };
544
+ }
545
+ return await this.getPullRequest(prNumber);
546
+ }
547
+ /**
548
+ * Fetch a pull request given the pull number
549
+ * @param {number} number The pull request number
550
+ * @returns {PullRequest}
551
+ */
552
+ async getPullRequest(number) {
553
+ return await this.gitHubApi.getPullRequest(number);
554
+ }
555
+ /**
556
+ * Update a pull request's title and body.
557
+ * @param {number} number The pull request number
558
+ * @param {ReleasePullRequest} releasePullRequest Pull request data to update
559
+ * @param {string} targetBranch The target branch of the pull request
560
+ * @param {string} options.signoffUser Optional. Commit signoff message
561
+ * @param {boolean} options.fork Optional. Whether to open the pull request from
562
+ * a fork or not. Defaults to `false`
563
+ * @param {PullRequestOverflowHandler} options.pullRequestOverflowHandler Optional.
564
+ * Handles extra large pull request body messages.
565
+ */
566
+ async updatePullRequest(number, releasePullRequest, targetBranch, options) {
567
+ const changes = await this.buildChangeSet(releasePullRequest.updates, targetBranch);
1041
568
  let message = releasePullRequest.title.toString();
1042
569
  if (options === null || options === void 0 ? void 0 : options.signoffUser) {
1043
570
  message = (0, signoff_commit_message_1.signoffCommitMessage)(message, options.signoffUser);
1044
571
  }
1045
- const pullRequestLabels = (options === null || options === void 0 ? void 0 : options.skipLabeling)
1046
- ? []
1047
- : releasePullRequest.labels;
1048
- return await this.createPullRequest({
1049
- headBranchName: releasePullRequest.headRefName,
1050
- baseBranchName: targetBranch,
1051
- number: -1,
1052
- title: releasePullRequest.title.toString(),
1053
- body: releasePullRequest.body.toString().slice(0, MAX_ISSUE_BODY_SIZE),
1054
- labels: pullRequestLabels,
1055
- files: [],
1056
- }, targetBranch, message, releasePullRequest.updates, {
1057
- fork: options === null || options === void 0 ? void 0 : options.fork,
572
+ const title = releasePullRequest.title.toString();
573
+ const body = ((options === null || options === void 0 ? void 0 : options.pullRequestOverflowHandler)
574
+ ? await options.pullRequestOverflowHandler.handleOverflow(releasePullRequest)
575
+ : releasePullRequest.body)
576
+ .toString()
577
+ .slice(0, MAX_ISSUE_BODY_SIZE);
578
+ const prNumber = await (0, code_suggester_1.createPullRequest)(this.octokit, changes, {
579
+ upstreamOwner: this.repository.owner,
580
+ upstreamRepo: this.repository.repo,
581
+ title,
582
+ branch: releasePullRequest.headRefName,
583
+ description: body,
584
+ primary: targetBranch,
585
+ force: true,
586
+ fork: (options === null || options === void 0 ? void 0 : options.fork) === false ? false : true,
587
+ message,
588
+ logger: this.logger,
1058
589
  draft: releasePullRequest.draft,
1059
590
  });
591
+ if (prNumber !== number) {
592
+ this.logger.warn(`updated code for ${prNumber}, but update requested for ${number}`);
593
+ }
594
+ return this.gitHubApi.updatePullRequest(number, title, body);
1060
595
  }
1061
596
  /**
1062
597
  * Given a set of proposed updates, build a changeset to suggest.
@@ -1117,6 +652,45 @@ class GitHub {
1117
652
  async findFilesByExtension(extension, prefix) {
1118
653
  return this.findFilesByExtensionAndRef(extension, this.repository.defaultBranch, prefix);
1119
654
  }
655
+ /**
656
+ * Create a GitHub release
657
+ *
658
+ * @param {Release} release Release parameters
659
+ * @param {ReleaseOptions} options Release option parameters
660
+ * @throws {DuplicateReleaseError} if the release tag already exists
661
+ * @throws {GitHubAPIError} on other API errors
662
+ */
663
+ async createRelease(release, options = {}) {
664
+ return await this.gitHubApi.createRelease(release, options);
665
+ }
666
+ /**
667
+ * Makes a comment on a issue/pull request.
668
+ *
669
+ * @param {string} comment - The body of the comment to post.
670
+ * @param {number} number - The issue or pull request number.
671
+ * @throws {GitHubAPIError} on an API error
672
+ */
673
+ async commentOnIssue(comment, number) {
674
+ return await this.gitHubApi.commentOnIssue(comment, number);
675
+ }
676
+ /**
677
+ * Removes labels from an issue/pull request.
678
+ *
679
+ * @param {string[]} labels The labels to remove.
680
+ * @param {number} number The issue/pull request number.
681
+ */
682
+ async removeIssueLabels(labels, number) {
683
+ return await this.gitHubApi.removeIssueLabels(labels, number);
684
+ }
685
+ /**
686
+ * Adds label to an issue/pull request.
687
+ *
688
+ * @param {string[]} labels The labels to add.
689
+ * @param {number} number The issue/pull request number.
690
+ */
691
+ async addIssueLabels(labels, number) {
692
+ return await this.gitHubApi.addIssueLabels(labels, number);
693
+ }
1120
694
  /**
1121
695
  * Generate release notes from GitHub at tag
1122
696
  * @param {string} tagName Name of new release tag
@@ -1124,14 +698,7 @@ class GitHub {
1124
698
  * @param {string} previousTag Optional. Name of previous tag to analyze commits since
1125
699
  */
1126
700
  async generateReleaseNotes(tagName, targetCommitish, previousTag) {
1127
- const resp = await this.octokit.repos.generateReleaseNotes({
1128
- owner: this.repository.owner,
1129
- repo: this.repository.repo,
1130
- tag_name: tagName,
1131
- previous_tag_name: previousTag,
1132
- target_commitish: targetCommitish,
1133
- });
1134
- return resp.data.body;
701
+ return await this.gitHubApi.generateReleaseNotes(tagName, targetCommitish, previousTag);
1135
702
  }
1136
703
  /**
1137
704
  * Create a single file on a new branch based on an existing
@@ -1144,107 +711,7 @@ class GitHub {
1144
711
  * @returns {string} HTML URL of the new file
1145
712
  */
1146
713
  async createFileOnNewBranch(filename, contents, newBranchName, baseBranchName) {
1147
- // create or update new branch to match base branch
1148
- await this.forkBranch(newBranchName, baseBranchName);
1149
- // use the single file upload API
1150
- const { data: { content }, } = await this.octokit.repos.createOrUpdateFileContents({
1151
- owner: this.repository.owner,
1152
- repo: this.repository.repo,
1153
- path: filename,
1154
- // contents need to be base64 encoded
1155
- content: Buffer.from(contents, 'binary').toString('base64'),
1156
- message: 'Saving release notes',
1157
- branch: newBranchName,
1158
- });
1159
- if (!(content === null || content === void 0 ? void 0 : content.html_url)) {
1160
- throw new Error(`Failed to write to file: ${filename} on branch: ${newBranchName}`);
1161
- }
1162
- return content.html_url;
1163
- }
1164
- /**
1165
- * Helper to fetch the SHA of a branch
1166
- * @param {string} branchName The name of the branch
1167
- * @return {string | undefined} Returns the SHA of the branch
1168
- * or undefined if it can't be found.
1169
- */
1170
- async getBranchSha(branchName) {
1171
- this.logger.debug(`Looking up SHA for branch: ${branchName}`);
1172
- try {
1173
- const { data: { object: { sha }, }, } = await this.octokit.git.getRef({
1174
- owner: this.repository.owner,
1175
- repo: this.repository.repo,
1176
- ref: `heads/${branchName}`,
1177
- });
1178
- this.logger.debug(`SHA for branch: ${sha}`);
1179
- return sha;
1180
- }
1181
- catch (e) {
1182
- if (e instanceof request_error_1.RequestError && e.status === 404) {
1183
- this.logger.debug(`Branch: ${branchName} does not exist`);
1184
- return undefined;
1185
- }
1186
- throw e;
1187
- }
1188
- }
1189
- /**
1190
- * Helper to fork a branch from an existing branch. Uses `force` so
1191
- * it will overwrite the contents of `targetBranchName` to match
1192
- * the current contents of `baseBranchName`.
1193
- *
1194
- * @param {string} targetBranchName The name of the new forked branch
1195
- * @param {string} baseBranchName The base branch from which to fork.
1196
- * @returns {string} The branch SHA
1197
- * @throws {ConfigurationError} if the base branch cannot be found.
1198
- */
1199
- async forkBranch(targetBranchName, baseBranchName) {
1200
- const baseBranchSha = await this.getBranchSha(baseBranchName);
1201
- if (!baseBranchSha) {
1202
- // this is highly unlikely to be thrown as we will have
1203
- // already attempted to read from the branch
1204
- throw new errors_1.ConfigurationError(`Unable to find base branch: ${baseBranchName}`, 'core', `${this.repository.owner}/${this.repository.repo}`);
1205
- }
1206
- // see if newBranchName exists
1207
- if (await this.getBranchSha(targetBranchName)) {
1208
- // branch already exists, update it to the match the base branch
1209
- const branchSha = await this.updateBranchSha(targetBranchName, baseBranchSha);
1210
- this.logger.debug(`Updated ${targetBranchName} to match ${baseBranchName} at ${branchSha}`);
1211
- return branchSha;
1212
- }
1213
- else {
1214
- // branch does not exist, create a new branch from the base branch
1215
- const branchSha = await this.createNewBranch(targetBranchName, baseBranchSha);
1216
- this.logger.debug(`Forked ${targetBranchName} from ${baseBranchName} at ${branchSha}`);
1217
- return branchSha;
1218
- }
1219
- }
1220
- /**
1221
- * Helper to create a new branch from a given SHA.
1222
- * @param {string} branchName The new branch name
1223
- * @param {string} branchSha The SHA of the branch
1224
- * @returns {string} The SHA of the new branch
1225
- */
1226
- async createNewBranch(branchName, branchSha) {
1227
- this.logger.debug(`Creating new branch: ${branchName} at ${branchSha}`);
1228
- const { data: { object: { sha }, }, } = await this.octokit.git.createRef({
1229
- owner: this.repository.owner,
1230
- repo: this.repository.repo,
1231
- ref: `refs/heads/${branchName}`,
1232
- sha: branchSha,
1233
- });
1234
- this.logger.debug(`New branch: ${branchName} at ${sha}`);
1235
- return sha;
1236
- }
1237
- async updateBranchSha(branchName, branchSha) {
1238
- this.logger.debug(`Updating branch ${branchName} to ${branchSha}`);
1239
- const { data: { object: { sha }, }, } = await this.octokit.git.updateRef({
1240
- owner: this.repository.owner,
1241
- repo: this.repository.repo,
1242
- ref: `heads/${branchName}`,
1243
- sha: branchSha,
1244
- force: true,
1245
- });
1246
- this.logger.debug(`Updated branch: ${branchName} to ${sha}`);
1247
- return sha;
714
+ return await this.gitHubApi.createFileOnNewBranch(filename, contents, newBranchName, baseBranchName);
1248
715
  }
1249
716
  }
1250
717
  exports.GitHub = GitHub;