release-please 12.5.0 → 13.0.0-candidate.2
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.
- package/CHANGELOG.md +7 -0
- package/README.md +4 -0
- package/build/src/bin/release-please.d.ts +55 -11
- package/build/src/bin/release-please.js +419 -152
- package/build/src/bootstrapper.d.ts +12 -0
- package/build/src/bootstrapper.js +60 -0
- package/build/src/changelog-notes/default.d.ts +17 -0
- package/build/src/changelog-notes/default.js +72 -0
- package/build/src/changelog-notes.d.ts +17 -0
- package/build/src/{updaters/java/readme.js → changelog-notes.js} +2 -7
- package/build/src/commit.d.ts +26 -0
- package/build/src/{util/to-conventional-changelog-format.js → commit.js} +97 -2
- package/build/src/errors/index.d.ts +0 -5
- package/build/src/errors/index.js +1 -10
- package/build/src/factory.d.ts +25 -37
- package/build/src/factory.js +160 -150
- package/build/src/github.d.ts +154 -884
- package/build/src/github.js +608 -1036
- package/build/src/manifest.d.ts +191 -47
- package/build/src/manifest.js +599 -487
- package/build/src/plugin.d.ts +20 -0
- package/build/src/{plugins/plugin.js → plugin.js} +10 -9
- package/build/src/plugins/cargo-workspace.d.ts +48 -18
- package/build/src/plugins/cargo-workspace.js +247 -328
- package/build/src/plugins/merge.d.ts +11 -0
- package/build/src/plugins/merge.js +83 -0
- package/build/src/plugins/node-workspace.d.ts +35 -17
- package/build/src/plugins/node-workspace.js +234 -271
- package/build/src/plugins/workspace.d.ts +102 -0
- package/build/src/plugins/workspace.js +170 -0
- package/build/src/pull-request.d.ts +10 -0
- package/build/src/{updaters/java/pom-xml.js → pull-request.js} +2 -7
- package/build/src/release-notes.d.ts +29 -0
- package/build/src/release-notes.js +71 -0
- package/build/src/release-pull-request.d.ts +13 -0
- package/build/src/release-pull-request.js +16 -0
- package/build/src/release.d.ts +6 -0
- package/build/src/release.js +16 -0
- package/build/src/repository.d.ts +5 -0
- package/build/src/repository.js +16 -0
- package/build/src/strategies/dart.d.ts +8 -0
- package/build/src/strategies/dart.js +63 -0
- package/build/src/strategies/elixir.d.ts +5 -0
- package/build/src/{releasers → strategies}/elixir.js +18 -14
- package/build/src/strategies/go-yoshi.d.ts +13 -0
- package/build/src/strategies/go-yoshi.js +106 -0
- package/build/src/strategies/go.d.ts +5 -0
- package/build/src/{releasers → strategies}/go.js +11 -12
- package/build/src/strategies/helm.d.ts +8 -0
- package/build/src/strategies/helm.js +63 -0
- package/build/src/strategies/java-yoshi.d.ts +24 -0
- package/build/src/strategies/java-yoshi.js +203 -0
- package/build/src/strategies/krm-blueprint.d.ts +7 -0
- package/build/src/{releasers → strategies}/krm-blueprint.js +26 -22
- package/build/src/strategies/node.d.ts +9 -0
- package/build/src/strategies/node.js +82 -0
- package/build/src/strategies/ocaml.d.ts +5 -0
- package/build/src/{releasers → strategies}/ocaml.js +34 -28
- package/build/src/strategies/php-yoshi.d.ts +10 -0
- package/build/src/strategies/php-yoshi.js +213 -0
- package/build/src/strategies/php.d.ts +6 -0
- package/build/src/{releasers → strategies}/php.js +24 -23
- package/build/src/strategies/python.d.ts +8 -0
- package/build/src/strategies/python.js +117 -0
- package/build/src/strategies/ruby-yoshi.d.ts +17 -0
- package/build/src/strategies/ruby-yoshi.js +116 -0
- package/build/src/strategies/ruby.d.ts +13 -0
- package/build/src/{releasers → strategies}/ruby.js +26 -27
- package/build/src/strategies/rust.d.ts +20 -0
- package/build/src/strategies/rust.js +120 -0
- package/build/src/strategies/simple.d.ts +5 -0
- package/build/src/{releasers → strategies}/simple.js +18 -14
- package/build/src/strategies/terraform-module.d.ts +7 -0
- package/build/src/{releasers → strategies}/terraform-module.js +29 -23
- package/build/src/strategy.d.ts +100 -0
- package/build/src/strategy.js +233 -0
- package/build/src/update.d.ts +23 -0
- package/build/src/{updaters/update.js → update.js} +1 -1
- package/build/src/updaters/changelog.d.ts +7 -10
- package/build/src/updaters/changelog.js +3 -9
- package/build/src/updaters/changelog.js.map +1 -1
- package/build/src/updaters/composite.d.ts +19 -0
- package/build/src/updaters/composite.js +42 -0
- package/build/src/updaters/dart/pubspec-yaml.d.ts +12 -0
- package/build/src/updaters/{pubspec-yaml.js → dart/pubspec-yaml.js} +13 -12
- package/build/src/updaters/default.d.ts +21 -0
- package/build/src/updaters/{version-txt.js → default.js} +16 -10
- package/build/src/updaters/dotnet/csproj.d.ts +12 -0
- package/build/src/updaters/{java/google-utils.js → dotnet/csproj.js} +16 -13
- package/build/src/updaters/elixir/elixir-mix-exs.d.ts +12 -0
- package/build/src/updaters/{elixir-mix-exs.js → elixir/elixir-mix-exs.js} +12 -10
- package/build/src/updaters/helm/chart-yaml.d.ts +10 -11
- package/build/src/updaters/helm/chart-yaml.js +12 -10
- package/build/src/updaters/java/java-update.d.ts +14 -0
- package/build/src/updaters/java/{java_update.js → java-update.js} +22 -22
- package/build/src/updaters/java/versions-manifest.d.ts +12 -2
- package/build/src/updaters/java/versions-manifest.js +20 -4
- package/build/src/updaters/krm/krm-blueprint-version.d.ts +10 -11
- package/build/src/updaters/krm/krm-blueprint-version.js +13 -12
- package/build/src/updaters/node/package-json.d.ts +12 -0
- package/build/src/updaters/{package-json.js → node/package-json.js} +14 -16
- package/build/src/updaters/node/package-lock-json.d.ts +8 -0
- package/build/src/updaters/node/package-lock-json.js +36 -0
- package/build/src/updaters/node/samples-package-json.d.ts +23 -0
- package/build/src/updaters/{samples-package-json.js → node/samples-package-json.js} +19 -8
- package/build/src/updaters/ocaml/dune-project.d.ts +10 -11
- package/build/src/updaters/ocaml/dune-project.js +11 -9
- package/build/src/updaters/ocaml/esy-json.d.ts +10 -11
- package/build/src/updaters/ocaml/esy-json.js +12 -10
- package/build/src/updaters/ocaml/opam.d.ts +10 -11
- package/build/src/updaters/ocaml/opam.js +11 -9
- package/build/src/updaters/php/php-client-version.d.ts +12 -0
- package/build/src/updaters/{php-client-version.js → php/php-client-version.js} +10 -9
- package/build/src/updaters/php/php-manifest.d.ts +13 -0
- package/build/src/updaters/{php-manifest.js → php/php-manifest.js} +17 -15
- package/build/src/updaters/php/root-composer-update-packages.d.ts +12 -0
- package/build/src/updaters/{root-composer-update-packages.js → php/root-composer-update-packages.js} +17 -16
- package/build/src/updaters/python/pyproject-toml.d.ts +10 -11
- package/build/src/updaters/python/pyproject-toml.js +13 -11
- package/build/src/updaters/python/python-file-with-version.d.ts +7 -11
- package/build/src/updaters/python/python-file-with-version.js +7 -8
- package/build/src/updaters/python/setup-cfg.d.ts +10 -11
- package/build/src/updaters/python/setup-cfg.js +10 -8
- package/build/src/updaters/python/setup-py.d.ts +10 -11
- package/build/src/updaters/python/setup-py.js +10 -8
- package/build/src/updaters/raw-content.d.ts +19 -0
- package/build/src/{plugins/index.js → updaters/raw-content.js} +23 -12
- package/build/src/updaters/release-please-config.d.ts +8 -0
- package/build/src/updaters/release-please-config.js +41 -0
- package/build/src/updaters/release-please-manifest.d.ts +2 -11
- package/build/src/updaters/release-please-manifest.js +11 -14
- package/build/src/updaters/ruby/version-rb.d.ts +12 -0
- package/build/src/updaters/{version-rb.js → ruby/version-rb.js} +10 -8
- package/build/src/updaters/rust/cargo-lock.d.ts +7 -11
- package/build/src/updaters/rust/cargo-lock.js +14 -16
- package/build/src/updaters/rust/cargo-toml.d.ts +7 -11
- package/build/src/updaters/rust/cargo-toml.js +19 -21
- package/build/src/updaters/terraform/module-version.d.ts +10 -11
- package/build/src/updaters/terraform/module-version.js +11 -9
- package/build/src/updaters/terraform/readme.d.ts +10 -11
- package/build/src/updaters/terraform/readme.js +11 -10
- package/build/src/updaters/terraform/readme.js.map +1 -1
- package/build/src/util/branch-name.d.ts +5 -4
- package/build/src/util/branch-name.js +13 -10
- package/build/src/{commit-split.d.ts → util/commit-split.d.ts} +2 -4
- package/build/src/{commit-split.js → util/commit-split.js} +4 -2
- package/build/src/util/indent-commit.d.ts +1 -1
- package/build/src/util/logger.d.ts +5 -2
- package/build/src/util/logger.js +9 -4
- package/build/src/util/pull-request-body.d.ts +20 -0
- package/build/src/util/pull-request-body.js +129 -0
- package/build/src/util/pull-request-title.d.ts +8 -6
- package/build/src/util/pull-request-title.js +20 -6
- package/build/src/util/tag-name.d.ts +9 -0
- package/build/src/util/tag-name.js +41 -0
- package/build/src/{updaters → util}/toml-edit.d.ts +0 -0
- package/build/src/{updaters → util}/toml-edit.js +0 -0
- package/build/src/version.d.ts +11 -0
- package/build/src/version.js +45 -0
- package/build/src/versioning-strategies/always-bump-patch.d.ts +7 -0
- package/build/src/{updaters/package-lock-json.js → versioning-strategies/always-bump-patch.js} +8 -11
- package/build/src/versioning-strategies/default.d.ts +15 -0
- package/build/src/versioning-strategies/default.js +67 -0
- package/build/src/versioning-strategies/dependency-manifest.d.ts +7 -0
- package/build/src/versioning-strategies/dependency-manifest.js +90 -0
- package/build/src/versioning-strategies/java-add-snapshot.d.ts +9 -0
- package/build/src/versioning-strategies/java-add-snapshot.js +53 -0
- package/build/src/versioning-strategies/java-snapshot.d.ts +9 -0
- package/build/src/versioning-strategies/java-snapshot.js +67 -0
- package/build/src/versioning-strategies/service-pack.d.ts +7 -0
- package/build/src/versioning-strategies/service-pack.js +40 -0
- package/build/src/versioning-strategy.d.ts +28 -0
- package/build/src/versioning-strategy.js +55 -0
- package/package.json +9 -8
- package/build/src/constants.d.ts +0 -6
- package/build/src/constants.js +0 -23
- package/build/src/conventional-commits.d.ts +0 -53
- package/build/src/conventional-commits.js +0 -167
- package/build/src/github-release.d.ts +0 -34
- package/build/src/github-release.js +0 -92
- package/build/src/graphql-to-commits.d.ts +0 -60
- package/build/src/graphql-to-commits.js +0 -112
- package/build/src/index.d.ts +0 -94
- package/build/src/index.js +0 -32
- package/build/src/plugins/index.d.ts +0 -5
- package/build/src/plugins/plugin.d.ts +0 -21
- package/build/src/release-pr.d.ts +0 -101
- package/build/src/release-pr.js +0 -461
- package/build/src/releasers/dart.d.ts +0 -9
- package/build/src/releasers/dart.js +0 -65
- package/build/src/releasers/elixir.d.ts +0 -5
- package/build/src/releasers/go-yoshi.d.ts +0 -10
- package/build/src/releasers/go-yoshi.js +0 -162
- package/build/src/releasers/go.d.ts +0 -6
- package/build/src/releasers/helm.d.ts +0 -9
- package/build/src/releasers/helm.js +0 -66
- package/build/src/releasers/index.d.ts +0 -7
- package/build/src/releasers/index.js +0 -76
- package/build/src/releasers/java/bump_type.d.ts +0 -4
- package/build/src/releasers/java/bump_type.js +0 -38
- package/build/src/releasers/java/stability.d.ts +0 -5
- package/build/src/releasers/java/stability.js +0 -37
- package/build/src/releasers/java/version.d.ts +0 -13
- package/build/src/releasers/java/version.js +0 -112
- package/build/src/releasers/java-backport.d.ts +0 -9
- package/build/src/releasers/java-backport.js +0 -43
- package/build/src/releasers/java-bom.d.ts +0 -16
- package/build/src/releasers/java-bom.js +0 -83
- package/build/src/releasers/java-lts.d.ts +0 -9
- package/build/src/releasers/java-lts.js +0 -47
- package/build/src/releasers/java-yoshi.d.ts +0 -28
- package/build/src/releasers/java-yoshi.js +0 -304
- package/build/src/releasers/krm-blueprint.d.ts +0 -6
- package/build/src/releasers/node.d.ts +0 -10
- package/build/src/releasers/node.js +0 -84
- package/build/src/releasers/ocaml.d.ts +0 -5
- package/build/src/releasers/php-yoshi.d.ts +0 -5
- package/build/src/releasers/php-yoshi.js +0 -191
- package/build/src/releasers/php.d.ts +0 -7
- package/build/src/releasers/python.d.ts +0 -11
- package/build/src/releasers/python.js +0 -127
- package/build/src/releasers/ruby-yoshi.d.ts +0 -5
- package/build/src/releasers/ruby-yoshi.js +0 -142
- package/build/src/releasers/ruby.d.ts +0 -11
- package/build/src/releasers/rust.d.ts +0 -30
- package/build/src/releasers/rust.js +0 -163
- package/build/src/releasers/simple.d.ts +0 -5
- package/build/src/releasers/terraform-module.d.ts +0 -6
- package/build/src/updaters/elixir-mix-exs.d.ts +0 -13
- package/build/src/updaters/java/google-utils.d.ts +0 -13
- package/build/src/updaters/java/java_update.d.ts +0 -13
- package/build/src/updaters/java/pom-xml.d.ts +0 -3
- package/build/src/updaters/java/readme.d.ts +0 -3
- package/build/src/updaters/java/readme.js.map +0 -1
- package/build/src/updaters/package-json.d.ts +0 -16
- package/build/src/updaters/package-lock-json.d.ts +0 -7
- package/build/src/updaters/php-client-version.d.ts +0 -13
- package/build/src/updaters/php-manifest.d.ts +0 -13
- package/build/src/updaters/pubspec-yaml.d.ts +0 -13
- package/build/src/updaters/root-composer-update-package.d.ts +0 -13
- package/build/src/updaters/root-composer-update-package.js +0 -45
- package/build/src/updaters/root-composer-update-packages.d.ts +0 -13
- package/build/src/updaters/samples-package-json.d.ts +0 -13
- package/build/src/updaters/update.d.ts +0 -20
- package/build/src/updaters/version-rb.d.ts +0 -13
- package/build/src/updaters/version-txt.d.ts +0 -12
- package/build/src/updaters/version.d.ts +0 -13
- package/build/src/updaters/version.js +0 -31
- package/build/src/util/checkpoint.d.ts +0 -6
- package/build/src/util/checkpoint.js +0 -33
- package/build/src/util/release-notes.d.ts +0 -7
- package/build/src/util/release-notes.js +0 -34
- package/build/src/util/to-conventional-changelog-format.d.ts +0 -2
package/build/src/github.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2021 Google LLC
|
|
3
3
|
//
|
|
4
4
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
// you may not use this file except in compliance with the License.
|
|
@@ -13,29 +13,42 @@
|
|
|
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.GitHub = void 0;
|
|
16
|
+
exports.GitHub = exports.GH_GRAPHQL_URL = exports.GH_API_URL = void 0;
|
|
17
17
|
const code_suggester_1 = require("code-suggester");
|
|
18
|
-
const logger_1 = require("./util/logger");
|
|
19
18
|
const rest_1 = require("@octokit/rest");
|
|
20
19
|
const request_1 = require("@octokit/request");
|
|
21
20
|
const graphql_1 = require("@octokit/graphql");
|
|
22
21
|
const request_error_1 = require("@octokit/request-error");
|
|
23
|
-
function isReposListResponse(arg) {
|
|
24
|
-
return typeof arg === 'object' && Object.hasOwnProperty.call(arg, 'name');
|
|
25
|
-
}
|
|
26
|
-
const chalk = require("chalk");
|
|
27
|
-
const semver = require("semver");
|
|
28
|
-
const graphql_to_commits_1 = require("./graphql-to-commits");
|
|
29
|
-
const branch_name_1 = require("./util/branch-name");
|
|
30
|
-
const constants_1 = require("./constants");
|
|
31
22
|
const errors_1 = require("./errors");
|
|
32
|
-
|
|
23
|
+
const MAX_ISSUE_BODY_SIZE = 65536;
|
|
24
|
+
exports.GH_API_URL = 'https://api.github.com';
|
|
25
|
+
exports.GH_GRAPHQL_URL = 'https://api.github.com';
|
|
26
|
+
const logger_1 = require("./util/logger");
|
|
27
|
+
const manifest_1 = require("./manifest");
|
|
28
|
+
const signoff_commit_message_1 = require("./util/signoff-commit-message");
|
|
33
29
|
class GitHub {
|
|
34
30
|
constructor(options) {
|
|
31
|
+
/**
|
|
32
|
+
* Get the list of file paths modified in a given commit.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} sha The commit SHA
|
|
35
|
+
* @returns {string[]} File paths
|
|
36
|
+
* @throws {GitHubAPIError} on an API error
|
|
37
|
+
*/
|
|
38
|
+
this.getCommitFiles = wrapAsync(async (sha) => {
|
|
39
|
+
logger_1.logger.debug(`Backfilling file list for commit: ${sha}`);
|
|
40
|
+
const resp = await this.octokit.repos.getCommit({
|
|
41
|
+
owner: this.repository.owner,
|
|
42
|
+
repo: this.repository.repo,
|
|
43
|
+
ref: sha,
|
|
44
|
+
});
|
|
45
|
+
const files = resp.data.files || [];
|
|
46
|
+
return files.map(file => file.filename).filter(filename => !!filename);
|
|
47
|
+
});
|
|
35
48
|
this.graphqlRequest = wrapAsync(async (opts, maxRetries = 1) => {
|
|
36
49
|
while (maxRetries >= 0) {
|
|
37
50
|
try {
|
|
38
|
-
return await this.
|
|
51
|
+
return await this.graphql(opts);
|
|
39
52
|
}
|
|
40
53
|
catch (err) {
|
|
41
54
|
if (err.status !== 502) {
|
|
@@ -45,310 +58,6 @@ class GitHub {
|
|
|
45
58
|
maxRetries -= 1;
|
|
46
59
|
}
|
|
47
60
|
});
|
|
48
|
-
/**
|
|
49
|
-
* Returns the list of commits since a given SHA on the target branch
|
|
50
|
-
*
|
|
51
|
-
* @param {string} sha SHA of the base commit or undefined for all commits
|
|
52
|
-
* @param {string} path If provided, limit to commits that affect the provided path
|
|
53
|
-
* @param {number} per_page Pagination option. Defaults to 100
|
|
54
|
-
* @returns {Commit[]} List of commits
|
|
55
|
-
* @throws {GitHubAPIError} on an API error
|
|
56
|
-
*/
|
|
57
|
-
this.commitsSinceShaRest = wrapAsync(async (sha, path, per_page = 100) => {
|
|
58
|
-
let page = 1;
|
|
59
|
-
let found = false;
|
|
60
|
-
const baseBranch = await this.getDefaultBranch();
|
|
61
|
-
const commits = [];
|
|
62
|
-
while (!found) {
|
|
63
|
-
const response = await this.request('GET /repos/{owner}/{repo}/commits{?sha,page,per_page,path}', {
|
|
64
|
-
owner: this.owner,
|
|
65
|
-
repo: this.repo,
|
|
66
|
-
sha: baseBranch,
|
|
67
|
-
page,
|
|
68
|
-
per_page,
|
|
69
|
-
path,
|
|
70
|
-
});
|
|
71
|
-
for (const commit of response.data) {
|
|
72
|
-
if (commit.sha === sha) {
|
|
73
|
-
found = true;
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
// skip merge commits
|
|
77
|
-
if (commit.parents.length === 2) {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
commits.push([commit.sha, commit.commit.message]);
|
|
81
|
-
}
|
|
82
|
-
page++;
|
|
83
|
-
}
|
|
84
|
-
const ret = [];
|
|
85
|
-
for (const [ref, message] of commits) {
|
|
86
|
-
const files = [];
|
|
87
|
-
let page = 1;
|
|
88
|
-
let moreFiles = true;
|
|
89
|
-
while (moreFiles) {
|
|
90
|
-
// the "Get Commit" resource is a bit of an outlier in terms of GitHub's
|
|
91
|
-
// normal pagination: https://git.io/JmVZq
|
|
92
|
-
// The behavior is to return an object representing the commit, a
|
|
93
|
-
// property of which is an array of files. GitHub will return as many
|
|
94
|
-
// associated files as there are, up to a limit of 300, on the initial
|
|
95
|
-
// request. If there are more associated files, it will send "Links"
|
|
96
|
-
// headers to get the next set. There is a total limit of 3000
|
|
97
|
-
// files returned per commit.
|
|
98
|
-
// In practice, the links headers are just the same resourceID plus a
|
|
99
|
-
// "page=N" query parameter with "page=1" being the initial set.
|
|
100
|
-
//
|
|
101
|
-
// TODO: it is more robust to follow the link.next headers (in case
|
|
102
|
-
// GitHub ever changes the pattern) OR use ocktokit pagination for this
|
|
103
|
-
// endpoint when https://git.io/JmVll is addressed.
|
|
104
|
-
const response = (await this.request('GET /repos/{owner}/{repo}/commits/{ref}{?page}', { owner: this.owner, repo: this.repo, ref, page }));
|
|
105
|
-
const commitFiles = response.data.files;
|
|
106
|
-
if (!commitFiles) {
|
|
107
|
-
moreFiles = false;
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
files.push(...commitFiles.map(f => { var _a; return (_a = f.filename) !== null && _a !== void 0 ? _a : ''; }));
|
|
111
|
-
// < 300 files means we hit the end
|
|
112
|
-
// page === 10 means we're at 3000 and that's the limit GH is gonna
|
|
113
|
-
// cough up anyway.
|
|
114
|
-
if (commitFiles.length < 300 || page === 10) {
|
|
115
|
-
moreFiles = false;
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
page++;
|
|
119
|
-
}
|
|
120
|
-
ret.push({ sha: ref, message, files });
|
|
121
|
-
}
|
|
122
|
-
return ret;
|
|
123
|
-
});
|
|
124
|
-
/**
|
|
125
|
-
* Find the SHA of the commit at the provided tag.
|
|
126
|
-
*
|
|
127
|
-
* @param {string} name Tag name
|
|
128
|
-
* @returns {string} The SHA of the commit
|
|
129
|
-
* @throws {GitHubAPIError} on an API error
|
|
130
|
-
*/
|
|
131
|
-
this.getTagSha = wrapAsync(async (name) => {
|
|
132
|
-
const refResponse = (await this.request('GET /repos/:owner/:repo/git/refs/tags/:name', {
|
|
133
|
-
owner: this.owner,
|
|
134
|
-
repo: this.repo,
|
|
135
|
-
name,
|
|
136
|
-
}));
|
|
137
|
-
return refResponse.data.object.sha;
|
|
138
|
-
});
|
|
139
|
-
this.allTags = wrapAsync(async (prefix) => {
|
|
140
|
-
// If we've fallen back to using allTags, support "-", "@", and "/" as a
|
|
141
|
-
// suffix separating the library name from the version #. This allows
|
|
142
|
-
// a repository to be seamlessly be migrated from a tool like lerna:
|
|
143
|
-
const prefixes = [];
|
|
144
|
-
if (prefix) {
|
|
145
|
-
prefix = prefix.substring(0, prefix.length - 1);
|
|
146
|
-
for (const suffix of ['-', '@', '/']) {
|
|
147
|
-
prefixes.push(`${prefix}${suffix}`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const tags = {};
|
|
151
|
-
for await (const response of this.octokit.paginate.iterator(this.decoratePaginateOpts({
|
|
152
|
-
method: 'GET',
|
|
153
|
-
url: `/repos/${this.owner}/${this.repo}/tags?per_page=100`,
|
|
154
|
-
}))) {
|
|
155
|
-
response.data.forEach(data => {
|
|
156
|
-
// For monorepos, a prefix can be provided, indicating that only tags
|
|
157
|
-
// matching the prefix should be returned:
|
|
158
|
-
if (!isReposListResponse(data))
|
|
159
|
-
return;
|
|
160
|
-
let version = data.name;
|
|
161
|
-
if (prefix) {
|
|
162
|
-
let match = false;
|
|
163
|
-
for (prefix of prefixes) {
|
|
164
|
-
if (data.name.startsWith(prefix)) {
|
|
165
|
-
version = data.name.replace(prefix, '');
|
|
166
|
-
match = true;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (!match)
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (semver.valid(version)) {
|
|
173
|
-
version = semver.valid(version);
|
|
174
|
-
tags[version] = { sha: data.commit.sha, name: data.name, version };
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
return tags;
|
|
179
|
-
});
|
|
180
|
-
/**
|
|
181
|
-
* Return a list of merged pull requests. The list is not guaranteed to be sorted
|
|
182
|
-
* by merged_at, but is generally most recent first.
|
|
183
|
-
*
|
|
184
|
-
* @param {string} targetBranch - Base branch of the pull request. Defaults to
|
|
185
|
-
* the configured default branch.
|
|
186
|
-
* @param {number} page - Page of results. Defaults to 1.
|
|
187
|
-
* @param {number} perPage - Number of results per page. Defaults to 100.
|
|
188
|
-
* @returns {MergedGitHubPR[]} - List of merged pull requests
|
|
189
|
-
* @throws {GitHubAPIError} on an API error
|
|
190
|
-
*/
|
|
191
|
-
this.findMergedPullRequests = wrapAsync(async (targetBranch, page = 1, perPage = 100) => {
|
|
192
|
-
if (!targetBranch) {
|
|
193
|
-
targetBranch = await this.getDefaultBranch();
|
|
194
|
-
}
|
|
195
|
-
// TODO: is sorting by updated better?
|
|
196
|
-
const pullsResponse = (await this.request(`GET /repos/:owner/:repo/pulls?state=closed&per_page=${perPage}&page=${page}&base=${targetBranch}&sort=created&direction=desc`, {
|
|
197
|
-
owner: this.owner,
|
|
198
|
-
repo: this.repo,
|
|
199
|
-
}));
|
|
200
|
-
// TODO: distinguish between no more pages and a full page of
|
|
201
|
-
// closed, non-merged pull requests. At page size of 100, this unlikely
|
|
202
|
-
// to matter
|
|
203
|
-
if (!pullsResponse.data) {
|
|
204
|
-
return [];
|
|
205
|
-
}
|
|
206
|
-
return (pullsResponse.data
|
|
207
|
-
// only return merged pull requests
|
|
208
|
-
.filter(pull => {
|
|
209
|
-
return !!pull.merged_at;
|
|
210
|
-
})
|
|
211
|
-
.map(pull => {
|
|
212
|
-
const labels = pull.labels
|
|
213
|
-
? pull.labels.map(l => {
|
|
214
|
-
return l.name + '';
|
|
215
|
-
})
|
|
216
|
-
: [];
|
|
217
|
-
return {
|
|
218
|
-
sha: pull.merge_commit_sha,
|
|
219
|
-
number: pull.number,
|
|
220
|
-
baseRefName: pull.base.ref,
|
|
221
|
-
headRefName: pull.head.ref,
|
|
222
|
-
labels,
|
|
223
|
-
title: pull.title,
|
|
224
|
-
body: pull.body + '',
|
|
225
|
-
};
|
|
226
|
-
}));
|
|
227
|
-
});
|
|
228
|
-
/**
|
|
229
|
-
* Find an existing release pull request with a matching title and labels
|
|
230
|
-
*
|
|
231
|
-
* @param {string} title Substring to match against the issue title
|
|
232
|
-
* @param {string[]} labels List of labels to match the issues
|
|
233
|
-
* @return {IssuesListResponseItem|undefined}
|
|
234
|
-
* @throws {AuthError} if the user is not authenticated to make this request
|
|
235
|
-
* @throws {GitHubAPIError} on other API errors
|
|
236
|
-
*/
|
|
237
|
-
this.findExistingReleaseIssue = wrapAsync(async (title, labels) => {
|
|
238
|
-
for await (const response of this.octokit.paginate.iterator(this.decoratePaginateOpts({
|
|
239
|
-
method: 'GET',
|
|
240
|
-
url: `/repos/${this.owner}/${this.repo}/issues?labels=${labels.join(',')}`,
|
|
241
|
-
per_page: 100,
|
|
242
|
-
}))) {
|
|
243
|
-
for (let i = 0; response.data[i] !== undefined; i++) {
|
|
244
|
-
const issue = response.data[i];
|
|
245
|
-
if (issue.title.indexOf(title) !== -1 && issue.state === 'open') {
|
|
246
|
-
return issue;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return undefined;
|
|
251
|
-
}, e => {
|
|
252
|
-
if (e instanceof request_error_1.RequestError && e.status === 404) {
|
|
253
|
-
// the most likely cause of a 404 during this step is actually
|
|
254
|
-
// that the user does not have access to the repo:
|
|
255
|
-
throw new errors_1.AuthError(e);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
/**
|
|
259
|
-
* Open a pull request
|
|
260
|
-
*
|
|
261
|
-
* @param {GitHubPR} options The pull request options
|
|
262
|
-
* @throws {GitHubAPIError} on an API error
|
|
263
|
-
*/
|
|
264
|
-
this.openPR = wrapAsync(async (options) => {
|
|
265
|
-
var _a;
|
|
266
|
-
const defaultBranch = await this.getDefaultBranch();
|
|
267
|
-
// check if there's an existing PR, so that we can opt to update it
|
|
268
|
-
// rather than creating a new PR.
|
|
269
|
-
const refName = `refs/heads/${options.branch}`;
|
|
270
|
-
let openReleasePR;
|
|
271
|
-
const releasePRCandidates = await this.findOpenReleasePRs(options.labels);
|
|
272
|
-
for (const releasePR of releasePRCandidates) {
|
|
273
|
-
if (refName && refName.includes(releasePR.head.ref)) {
|
|
274
|
-
openReleasePR = releasePR;
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
// Short-circuit if there have been no changes to the pull-request body.
|
|
279
|
-
if (openReleasePR && openReleasePR.body === options.body) {
|
|
280
|
-
logger_1.logger.info(`PR https://github.com/${this.owner}/${this.repo}/pull/${openReleasePR.number} remained the same`);
|
|
281
|
-
return undefined;
|
|
282
|
-
}
|
|
283
|
-
// Update the files for the release if not already supplied
|
|
284
|
-
const changes = (_a = options.changes) !== null && _a !== void 0 ? _a : (await this.getChangeSet(options.updates, defaultBranch));
|
|
285
|
-
const prNumber = await code_suggester_1.createPullRequest(this.octokit, changes, {
|
|
286
|
-
upstreamOwner: this.owner,
|
|
287
|
-
upstreamRepo: this.repo,
|
|
288
|
-
title: options.title,
|
|
289
|
-
branch: options.branch,
|
|
290
|
-
description: options.body.slice(0, constants_1.MAX_ISSUE_BODY_SIZE),
|
|
291
|
-
primary: defaultBranch,
|
|
292
|
-
force: true,
|
|
293
|
-
fork: this.fork,
|
|
294
|
-
message: options.message,
|
|
295
|
-
logger: logger_1.logger,
|
|
296
|
-
});
|
|
297
|
-
// If a release PR was already open, update the title and body:
|
|
298
|
-
if (openReleasePR) {
|
|
299
|
-
logger_1.logger.info(`update pull-request #${openReleasePR.number}: ${chalk.yellow(options.title)}`);
|
|
300
|
-
await this.request('PATCH /repos/:owner/:repo/pulls/:pull_number', {
|
|
301
|
-
pull_number: openReleasePR.number,
|
|
302
|
-
owner: this.owner,
|
|
303
|
-
repo: this.repo,
|
|
304
|
-
title: options.title,
|
|
305
|
-
body: options.body,
|
|
306
|
-
state: 'open',
|
|
307
|
-
});
|
|
308
|
-
return openReleasePR.number;
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
return prNumber;
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
/**
|
|
315
|
-
* Returns the repository's default/primary branch.
|
|
316
|
-
*
|
|
317
|
-
* @returns {string}
|
|
318
|
-
* @throws {GitHubAPIError} on an API error
|
|
319
|
-
*/
|
|
320
|
-
this.getRepositoryDefaultBranch = wrapAsync(async () => {
|
|
321
|
-
if (this.repositoryDefaultBranch) {
|
|
322
|
-
return this.repositoryDefaultBranch;
|
|
323
|
-
}
|
|
324
|
-
const { data } = await this.octokit.repos.get({
|
|
325
|
-
repo: this.repo,
|
|
326
|
-
owner: this.owner,
|
|
327
|
-
headers: {
|
|
328
|
-
Authorization: `token ${this.token}`,
|
|
329
|
-
},
|
|
330
|
-
});
|
|
331
|
-
this.repositoryDefaultBranch = data.default_branch;
|
|
332
|
-
return this.repositoryDefaultBranch;
|
|
333
|
-
});
|
|
334
|
-
/**
|
|
335
|
-
* Close a pull request
|
|
336
|
-
*
|
|
337
|
-
* @param {number} prNumber The pull request number
|
|
338
|
-
* @returns {boolean} Whether the request was attempts
|
|
339
|
-
* @throws {GitHubAPIError} on an API error
|
|
340
|
-
*/
|
|
341
|
-
this.closePR = wrapAsync(async (prNumber) => {
|
|
342
|
-
if (this.fork)
|
|
343
|
-
return false;
|
|
344
|
-
await this.request('PATCH /repos/:owner/:repo/pulls/:pull_number', {
|
|
345
|
-
owner: this.owner,
|
|
346
|
-
repo: this.repo,
|
|
347
|
-
pull_number: prNumber,
|
|
348
|
-
state: 'closed',
|
|
349
|
-
});
|
|
350
|
-
return true;
|
|
351
|
-
});
|
|
352
61
|
/**
|
|
353
62
|
* Fetch the contents of a file with the Contents API
|
|
354
63
|
*
|
|
@@ -358,10 +67,10 @@ class GitHub {
|
|
|
358
67
|
* @throws {GitHubAPIError} on other API errors
|
|
359
68
|
*/
|
|
360
69
|
this.getFileContentsWithSimpleAPI = wrapAsync(async (path, ref, isBranch = true) => {
|
|
361
|
-
ref = isBranch ?
|
|
70
|
+
ref = isBranch ? fullyQualifyBranchRef(ref) : ref;
|
|
362
71
|
const options = {
|
|
363
|
-
owner: this.owner,
|
|
364
|
-
repo: this.repo,
|
|
72
|
+
owner: this.repository.owner,
|
|
73
|
+
repo: this.repository.repo,
|
|
365
74
|
path,
|
|
366
75
|
ref,
|
|
367
76
|
};
|
|
@@ -373,79 +82,33 @@ class GitHub {
|
|
|
373
82
|
};
|
|
374
83
|
});
|
|
375
84
|
/**
|
|
376
|
-
* Fetch the contents of a file
|
|
85
|
+
* Fetch the contents of a file using the Git data API
|
|
377
86
|
*
|
|
378
87
|
* @param {string} path The path to the file in the repository
|
|
379
88
|
* @param {string} branch The branch to fetch from
|
|
380
89
|
* @returns {GitHubFileContents}
|
|
381
90
|
* @throws {GitHubAPIError} on other API errors
|
|
382
91
|
*/
|
|
383
|
-
this.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
throw
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
* @throws {GitHubAPIError} on other API errors
|
|
404
|
-
*/
|
|
405
|
-
this.createRelease = wrapAsync(async (packageName, tagName, sha, releaseNotes, draft) => {
|
|
406
|
-
logger_1.logger.info(`creating release ${tagName}`);
|
|
407
|
-
const name = packageName ? `${packageName} ${tagName}` : tagName;
|
|
408
|
-
return (await this.request('POST /repos/:owner/:repo/releases', {
|
|
409
|
-
owner: this.owner,
|
|
410
|
-
repo: this.repo,
|
|
411
|
-
tag_name: tagName,
|
|
412
|
-
target_commitish: sha,
|
|
413
|
-
body: releaseNotes,
|
|
414
|
-
name,
|
|
415
|
-
draft: draft,
|
|
416
|
-
})).data;
|
|
417
|
-
}, e => {
|
|
418
|
-
if (e instanceof request_error_1.RequestError) {
|
|
419
|
-
if (e.status === 422 &&
|
|
420
|
-
errors_1.GitHubAPIError.parseErrors(e).some(error => {
|
|
421
|
-
return error.code === 'already_exists';
|
|
422
|
-
})) {
|
|
423
|
-
throw new errors_1.DuplicateReleaseError(e, 'tagName');
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
/**
|
|
428
|
-
* Remove labels from an issue or pull request
|
|
429
|
-
*
|
|
430
|
-
* @param {string[]} labels The names of the labels to remove
|
|
431
|
-
* @param {number} prNumber The issue or pull request number
|
|
432
|
-
* @return {boolean} Whether or not the request was attempted
|
|
433
|
-
* @throws {GitHubAPIError} on an API error
|
|
434
|
-
*/
|
|
435
|
-
this.removeLabels = wrapAsync(async (labels, prNumber) => {
|
|
436
|
-
if (this.fork)
|
|
437
|
-
return false;
|
|
438
|
-
for (let i = 0, label; i < labels.length; i++) {
|
|
439
|
-
label = labels[i];
|
|
440
|
-
logger_1.logger.info(`removing label ${chalk.green(label)} from ${chalk.green('' + prNumber)}`);
|
|
441
|
-
await this.request('DELETE /repos/:owner/:repo/issues/:issue_number/labels/:name', {
|
|
442
|
-
owner: this.owner,
|
|
443
|
-
repo: this.repo,
|
|
444
|
-
issue_number: prNumber,
|
|
445
|
-
name: label,
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
return true;
|
|
92
|
+
this.getFileContentsWithDataAPI = wrapAsync(async (path, branch) => {
|
|
93
|
+
const repoTree = await this.octokit.git.getTree({
|
|
94
|
+
owner: this.repository.owner,
|
|
95
|
+
repo: this.repository.repo,
|
|
96
|
+
tree_sha: branch,
|
|
97
|
+
});
|
|
98
|
+
const blobDescriptor = repoTree.data.tree.find(tree => tree.path === path);
|
|
99
|
+
if (!blobDescriptor) {
|
|
100
|
+
throw new Error(`Could not find requested path: ${path}`);
|
|
101
|
+
}
|
|
102
|
+
const resp = await this.octokit.git.getBlob({
|
|
103
|
+
owner: this.repository.owner,
|
|
104
|
+
repo: this.repository.repo,
|
|
105
|
+
file_sha: blobDescriptor.sha,
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
parsedContent: Buffer.from(resp.data.content, 'base64').toString('utf8'),
|
|
109
|
+
content: resp.data.content,
|
|
110
|
+
sha: resp.data.sha,
|
|
111
|
+
};
|
|
449
112
|
});
|
|
450
113
|
/**
|
|
451
114
|
* Returns a list of paths to all files with a given name.
|
|
@@ -460,11 +123,12 @@ class GitHub {
|
|
|
460
123
|
*/
|
|
461
124
|
this.findFilesByFilenameAndRef = wrapAsync(async (filename, ref, prefix) => {
|
|
462
125
|
if (prefix) {
|
|
463
|
-
prefix =
|
|
126
|
+
prefix = normalizePrefix(prefix);
|
|
464
127
|
}
|
|
128
|
+
logger_1.logger.debug(`finding files by filename: ${filename}, ref: ${ref}, prefix: ${prefix}`);
|
|
465
129
|
const response = await this.octokit.git.getTree({
|
|
466
|
-
owner: this.owner,
|
|
467
|
-
repo: this.repo,
|
|
130
|
+
owner: this.repository.owner,
|
|
131
|
+
repo: this.repository.repo,
|
|
468
132
|
tree_sha: ref,
|
|
469
133
|
recursive: 'true',
|
|
470
134
|
});
|
|
@@ -487,6 +151,101 @@ class GitHub {
|
|
|
487
151
|
return path;
|
|
488
152
|
});
|
|
489
153
|
});
|
|
154
|
+
this.createPullRequest = wrapAsync(async (pullRequest, targetBranch, message, updates, options) => {
|
|
155
|
+
// Update the files for the release if not already supplied
|
|
156
|
+
const changes = await this.getChangeSet(updates, targetBranch);
|
|
157
|
+
const prNumber = await code_suggester_1.createPullRequest(this.octokit, changes, {
|
|
158
|
+
upstreamOwner: this.repository.owner,
|
|
159
|
+
upstreamRepo: this.repository.repo,
|
|
160
|
+
title: pullRequest.title,
|
|
161
|
+
branch: pullRequest.headBranchName,
|
|
162
|
+
description: pullRequest.body,
|
|
163
|
+
primary: targetBranch,
|
|
164
|
+
force: true,
|
|
165
|
+
fork: !!(options === null || options === void 0 ? void 0 : options.fork),
|
|
166
|
+
message,
|
|
167
|
+
logger: logger_1.logger,
|
|
168
|
+
draft: !!(options === null || options === void 0 ? void 0 : options.draft),
|
|
169
|
+
labels: pullRequest.labels,
|
|
170
|
+
});
|
|
171
|
+
return await this.getPullRequest(prNumber);
|
|
172
|
+
});
|
|
173
|
+
/**
|
|
174
|
+
* Fetch a pull request given the pull number
|
|
175
|
+
* @param {number} number The pull request number
|
|
176
|
+
* @returns {PullRequest}
|
|
177
|
+
*/
|
|
178
|
+
this.getPullRequest = wrapAsync(async (number) => {
|
|
179
|
+
const response = await this.octokit.pulls.get({
|
|
180
|
+
owner: this.repository.owner,
|
|
181
|
+
repo: this.repository.repo,
|
|
182
|
+
pull_number: number,
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
headBranchName: response.data.head.ref,
|
|
186
|
+
baseBranchName: response.data.base.ref,
|
|
187
|
+
number: response.data.number,
|
|
188
|
+
title: response.data.title,
|
|
189
|
+
body: response.data.body || '',
|
|
190
|
+
files: [],
|
|
191
|
+
labels: response.data.labels
|
|
192
|
+
.map(label => label.name)
|
|
193
|
+
.filter(name => !!name),
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
/**
|
|
197
|
+
* Update a pull request's title and body.
|
|
198
|
+
* @param {number} number The pull request number
|
|
199
|
+
* @param {ReleasePullRequest} releasePullRequest Pull request data to update
|
|
200
|
+
* @param {}
|
|
201
|
+
*/
|
|
202
|
+
this.updatePullRequest = wrapAsync(async (number, releasePullRequest, targetBranch, options) => {
|
|
203
|
+
// Update the files for the release if not already supplied
|
|
204
|
+
const changes = await this.getChangeSet(releasePullRequest.updates, targetBranch);
|
|
205
|
+
let message = releasePullRequest.title.toString();
|
|
206
|
+
if (options === null || options === void 0 ? void 0 : options.signoffUser) {
|
|
207
|
+
message = signoff_commit_message_1.signoffCommitMessage(message, options.signoffUser);
|
|
208
|
+
}
|
|
209
|
+
const title = releasePullRequest.title.toString();
|
|
210
|
+
const body = releasePullRequest.body
|
|
211
|
+
.toString()
|
|
212
|
+
.slice(0, MAX_ISSUE_BODY_SIZE);
|
|
213
|
+
const prNumber = await code_suggester_1.createPullRequest(this.octokit, changes, {
|
|
214
|
+
upstreamOwner: this.repository.owner,
|
|
215
|
+
upstreamRepo: this.repository.repo,
|
|
216
|
+
title,
|
|
217
|
+
branch: releasePullRequest.headRefName,
|
|
218
|
+
description: body,
|
|
219
|
+
primary: targetBranch,
|
|
220
|
+
force: true,
|
|
221
|
+
fork: (options === null || options === void 0 ? void 0 : options.fork) === false ? false : true,
|
|
222
|
+
message,
|
|
223
|
+
logger: logger_1.logger,
|
|
224
|
+
draft: releasePullRequest.draft,
|
|
225
|
+
});
|
|
226
|
+
if (prNumber !== number) {
|
|
227
|
+
logger_1.logger.warn(`updated code for ${prNumber}, but update requested for ${number}`);
|
|
228
|
+
}
|
|
229
|
+
const response = await this.octokit.pulls.update({
|
|
230
|
+
owner: this.repository.owner,
|
|
231
|
+
repo: this.repository.repo,
|
|
232
|
+
pull_number: number,
|
|
233
|
+
title: releasePullRequest.title.toString(),
|
|
234
|
+
body,
|
|
235
|
+
state: 'open',
|
|
236
|
+
});
|
|
237
|
+
return {
|
|
238
|
+
headBranchName: response.data.head.ref,
|
|
239
|
+
baseBranchName: response.data.base.ref,
|
|
240
|
+
number: response.data.number,
|
|
241
|
+
title: response.data.title,
|
|
242
|
+
body: response.data.body || '',
|
|
243
|
+
files: [],
|
|
244
|
+
labels: response.data.labels
|
|
245
|
+
.map(label => label.name)
|
|
246
|
+
.filter(name => !!name),
|
|
247
|
+
};
|
|
248
|
+
});
|
|
490
249
|
/**
|
|
491
250
|
* Returns a list of paths to all files with a given file
|
|
492
251
|
* extension.
|
|
@@ -503,11 +262,11 @@ class GitHub {
|
|
|
503
262
|
*/
|
|
504
263
|
this.findFilesByExtensionAndRef = wrapAsync(async (extension, ref, prefix) => {
|
|
505
264
|
if (prefix) {
|
|
506
|
-
prefix =
|
|
265
|
+
prefix = normalizePrefix(prefix);
|
|
507
266
|
}
|
|
508
267
|
const response = await this.octokit.git.getTree({
|
|
509
|
-
owner: this.owner,
|
|
510
|
-
repo: this.repo,
|
|
268
|
+
owner: this.repository.owner,
|
|
269
|
+
repo: this.repository.repo,
|
|
511
270
|
tree_sha: ref,
|
|
512
271
|
recursive: 'true',
|
|
513
272
|
});
|
|
@@ -530,6 +289,41 @@ class GitHub {
|
|
|
530
289
|
return path;
|
|
531
290
|
});
|
|
532
291
|
});
|
|
292
|
+
/**
|
|
293
|
+
* Create a GitHub release
|
|
294
|
+
*
|
|
295
|
+
* @param {Release} release Release parameters
|
|
296
|
+
* @param {boolean} draft Whether or not to create the release as a draft
|
|
297
|
+
* @throws {DuplicateReleaseError} if the release tag already exists
|
|
298
|
+
* @throws {GitHubAPIError} on other API errors
|
|
299
|
+
*/
|
|
300
|
+
this.createRelease = wrapAsync(async (release, options = {}) => {
|
|
301
|
+
const resp = await this.octokit.repos.createRelease({
|
|
302
|
+
owner: this.repository.owner,
|
|
303
|
+
repo: this.repository.repo,
|
|
304
|
+
tag_name: release.tag.toString(),
|
|
305
|
+
body: release.notes,
|
|
306
|
+
sha: release.sha,
|
|
307
|
+
draft: !!options.draft,
|
|
308
|
+
});
|
|
309
|
+
return {
|
|
310
|
+
name: resp.data.name || undefined,
|
|
311
|
+
tagName: resp.data.tag_name,
|
|
312
|
+
sha: resp.data.target_commitish,
|
|
313
|
+
notes: resp.data.body_text,
|
|
314
|
+
url: resp.data.html_url,
|
|
315
|
+
draft: resp.data.draft,
|
|
316
|
+
};
|
|
317
|
+
}, e => {
|
|
318
|
+
if (e instanceof request_error_1.RequestError) {
|
|
319
|
+
if (e.status === 422 &&
|
|
320
|
+
errors_1.GitHubAPIError.parseErrors(e).some(error => {
|
|
321
|
+
return error.code === 'already_exists';
|
|
322
|
+
})) {
|
|
323
|
+
throw new errors_1.DuplicateReleaseError(e, 'tagName');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
});
|
|
533
327
|
/**
|
|
534
328
|
* Makes a comment on a issue/pull request.
|
|
535
329
|
*
|
|
@@ -538,379 +332,176 @@ class GitHub {
|
|
|
538
332
|
* @throws {GitHubAPIError} on an API error
|
|
539
333
|
*/
|
|
540
334
|
this.commentOnIssue = wrapAsync(async (comment, number) => {
|
|
541
|
-
logger_1.logger.
|
|
542
|
-
|
|
543
|
-
owner: this.owner,
|
|
544
|
-
repo: this.repo,
|
|
335
|
+
logger_1.logger.debug(`adding comment to https://github.com/${this.repository.owner}/${this.repository.repo}/issue/${number}`);
|
|
336
|
+
const resp = await this.octokit.issues.createComment({
|
|
337
|
+
owner: this.repository.owner,
|
|
338
|
+
repo: this.repository.repo,
|
|
545
339
|
issue_number: number,
|
|
546
340
|
body: comment,
|
|
547
|
-
})
|
|
341
|
+
});
|
|
342
|
+
return resp.data.html_url;
|
|
343
|
+
});
|
|
344
|
+
/**
|
|
345
|
+
* Removes labels from an issue/pull request.
|
|
346
|
+
*
|
|
347
|
+
* @param {string[]} labels The labels to remove.
|
|
348
|
+
* @param {number} number The issue/pull request number.
|
|
349
|
+
*/
|
|
350
|
+
this.removeIssueLabels = wrapAsync(async (labels, number) => {
|
|
351
|
+
if (labels.length === 0) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
logger_1.logger.debug(`removing labels: ${labels} from issue/pull ${number}`);
|
|
355
|
+
await Promise.all(labels.map(label => this.octokit.issues.removeLabel({
|
|
356
|
+
owner: this.repository.owner,
|
|
357
|
+
repo: this.repository.repo,
|
|
358
|
+
issue_number: number,
|
|
359
|
+
name: label,
|
|
360
|
+
})));
|
|
548
361
|
});
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Adds label to an issue/pull request.
|
|
364
|
+
*
|
|
365
|
+
* @param {string[]} labels The labels to add.
|
|
366
|
+
* @param {number} number The issue/pull request number.
|
|
367
|
+
*/
|
|
368
|
+
this.addIssueLabels = wrapAsync(async (labels, number) => {
|
|
369
|
+
if (labels.length === 0) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
logger_1.logger.debug(`adding labels: ${labels} from issue/pull ${number}`);
|
|
373
|
+
await this.octokit.issues.addLabels({
|
|
374
|
+
owner: this.repository.owner,
|
|
375
|
+
repo: this.repository.repo,
|
|
376
|
+
issue_number: number,
|
|
377
|
+
labels,
|
|
559
378
|
});
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
},
|
|
566
|
-
};
|
|
567
|
-
this.request = request_1.request.defaults(defaults);
|
|
568
|
-
this.graphql = graphql_1.graphql;
|
|
569
|
-
}
|
|
570
|
-
else {
|
|
571
|
-
// for the benefit of probot applications, we allow a configured instance
|
|
572
|
-
// of octokit to be passed in as a parameter.
|
|
573
|
-
probotMode = true;
|
|
574
|
-
this.octokit = options.octokitAPIs.octokit;
|
|
575
|
-
this.request = options.octokitAPIs.request;
|
|
576
|
-
this.graphql = options.octokitAPIs.graphql;
|
|
577
|
-
}
|
|
379
|
+
});
|
|
380
|
+
this.repository = options.repository;
|
|
381
|
+
this.octokit = options.octokitAPIs.octokit;
|
|
382
|
+
this.request = options.octokitAPIs.request;
|
|
383
|
+
this.graphql = options.octokitAPIs.graphql;
|
|
578
384
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Build a new GitHub client with auto-detected default branch.
|
|
387
|
+
*
|
|
388
|
+
* @param {GitHubCreateOptions} options Configuration options
|
|
389
|
+
* @param {string} options.owner The repository owner.
|
|
390
|
+
* @param {string} options.repo The repository name.
|
|
391
|
+
* @param {string} options.defaultBranch Optional. The repository's default branch.
|
|
392
|
+
* Defaults to the value fetched via the API.
|
|
393
|
+
* @param {string} options.apiUrl Optional. The base url of the GitHub API.
|
|
394
|
+
* @param {string} options.graphqlUrl Optional. The base url of the GraphQL API.
|
|
395
|
+
* @param {OctokitAPISs} options.octokitAPIs Optional. Override the internal
|
|
396
|
+
* client instances with a pre-authenticated instance.
|
|
397
|
+
* @param {string} token Optional. A GitHub API token used for authentication.
|
|
398
|
+
*/
|
|
399
|
+
static async create(options) {
|
|
400
|
+
var _a, _b, _c, _d;
|
|
401
|
+
const apiUrl = (_a = options.apiUrl) !== null && _a !== void 0 ? _a : exports.GH_API_URL;
|
|
402
|
+
const graphqlUrl = (_b = options.graphqlUrl) !== null && _b !== void 0 ? _b : exports.GH_GRAPHQL_URL;
|
|
403
|
+
const releasePleaseVersion = require('../../package.json').version;
|
|
404
|
+
const apis = (_c = options.octokitAPIs) !== null && _c !== void 0 ? _c : {
|
|
405
|
+
octokit: new rest_1.Octokit({
|
|
406
|
+
baseUrl: apiUrl,
|
|
407
|
+
auth: options.token,
|
|
408
|
+
}),
|
|
409
|
+
request: request_1.request.defaults({
|
|
410
|
+
baseUrl: apiUrl,
|
|
584
411
|
headers: {
|
|
585
|
-
|
|
586
|
-
|
|
412
|
+
'user-agent': `release-please/${releasePleaseVersion}`,
|
|
413
|
+
Authorization: `token ${options.token}`,
|
|
587
414
|
},
|
|
588
|
-
})
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
592
|
-
decoratePaginateOpts(opts) {
|
|
593
|
-
if (probotMode) {
|
|
594
|
-
return opts;
|
|
595
|
-
}
|
|
596
|
-
else {
|
|
597
|
-
return Object.assign(opts, {
|
|
415
|
+
}),
|
|
416
|
+
graphql: graphql_1.graphql.defaults({
|
|
417
|
+
baseUrl: graphqlUrl,
|
|
598
418
|
headers: {
|
|
599
|
-
|
|
419
|
+
'user-agent': `release-please/${releasePleaseVersion}`,
|
|
420
|
+
Authorization: `token ${options.token}`,
|
|
421
|
+
'content-type': 'application/vnd.github.v3+json',
|
|
600
422
|
},
|
|
601
|
-
})
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
* @param {string|null} path If provided, limit to commits that affect the provided path
|
|
613
|
-
* @returns {Commit[]} List of commits
|
|
614
|
-
* @throws {GitHubAPIError} on an API error
|
|
615
|
-
*/
|
|
616
|
-
async commitsSinceSha(sha, perPage = 100, labels = false, path = null) {
|
|
617
|
-
const commits = [];
|
|
618
|
-
const method = labels ? 'commitsWithLabels' : 'commitsWithFiles';
|
|
619
|
-
let cursor;
|
|
620
|
-
for (;;) {
|
|
621
|
-
const commitsResponse = await this[method](cursor, perPage, path);
|
|
622
|
-
for (let i = 0, commit; i < commitsResponse.commits.length; i++) {
|
|
623
|
-
commit = commitsResponse.commits[i];
|
|
624
|
-
if (commit.sha === sha) {
|
|
625
|
-
return commits;
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
commits.push(commit);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
if (commitsResponse.hasNextPage === false || !commitsResponse.endCursor) {
|
|
632
|
-
return commits;
|
|
633
|
-
}
|
|
634
|
-
else {
|
|
635
|
-
cursor = commitsResponse.endCursor;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
async commitsWithFiles(cursor = undefined, perPage = 32, path = null, maxFilesChanged = 64) {
|
|
640
|
-
const baseBranch = await this.getDefaultBranch();
|
|
641
|
-
// The GitHub v3 API does not offer an elegant way to fetch commits
|
|
642
|
-
// in conjucntion with the path that they modify. We lean on the graphql
|
|
643
|
-
// API for this one task, fetching commits in descending chronological
|
|
644
|
-
// order along with the file paths attached to them.
|
|
645
|
-
const response = await this.graphqlRequest({
|
|
646
|
-
query: `query commitsWithFiles($cursor: String, $owner: String!, $repo: String!, $baseRef: String!, $perPage: Int, $maxFilesChanged: Int, $path: String) {
|
|
647
|
-
repository(owner: $owner, name: $repo) {
|
|
648
|
-
ref(qualifiedName: $baseRef) {
|
|
649
|
-
target {
|
|
650
|
-
... on Commit {
|
|
651
|
-
history(first: $perPage, after: $cursor, path: $path) {
|
|
652
|
-
edges {
|
|
653
|
-
node {
|
|
654
|
-
... on Commit {
|
|
655
|
-
message
|
|
656
|
-
oid
|
|
657
|
-
associatedPullRequests(first: 1) {
|
|
658
|
-
edges {
|
|
659
|
-
node {
|
|
660
|
-
... on PullRequest {
|
|
661
|
-
number
|
|
662
|
-
mergeCommit {
|
|
663
|
-
oid
|
|
664
|
-
}
|
|
665
|
-
files(first: $maxFilesChanged) {
|
|
666
|
-
edges {
|
|
667
|
-
node {
|
|
668
|
-
path
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
pageInfo {
|
|
672
|
-
endCursor
|
|
673
|
-
hasNextPage
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
pageInfo {
|
|
684
|
-
endCursor
|
|
685
|
-
hasNextPage
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
}`,
|
|
693
|
-
cursor,
|
|
694
|
-
maxFilesChanged,
|
|
695
|
-
owner: this.owner,
|
|
696
|
-
path,
|
|
697
|
-
perPage,
|
|
698
|
-
repo: this.repo,
|
|
699
|
-
baseRef: `refs/heads/${baseBranch}`,
|
|
700
|
-
}, 3);
|
|
701
|
-
return graphql_to_commits_1.graphqlToCommits(this, response);
|
|
702
|
-
}
|
|
703
|
-
async commitsWithLabels(cursor = undefined, perPage = 32, path = null, maxLabels = 16) {
|
|
704
|
-
const baseBranch = await this.getDefaultBranch();
|
|
705
|
-
const response = await this.graphqlRequest({
|
|
706
|
-
query: `query commitsWithLabels($cursor: String, $owner: String!, $repo: String!, $baseRef: String!, $perPage: Int, $maxLabels: Int, $path: String) {
|
|
707
|
-
repository(owner: $owner, name: $repo) {
|
|
708
|
-
ref(qualifiedName: $baseRef) {
|
|
709
|
-
target {
|
|
710
|
-
... on Commit {
|
|
711
|
-
history(first: $perPage, after: $cursor, path: $path) {
|
|
712
|
-
edges {
|
|
713
|
-
node {
|
|
714
|
-
... on Commit {
|
|
715
|
-
message
|
|
716
|
-
oid
|
|
717
|
-
associatedPullRequests(first: 1) {
|
|
718
|
-
edges {
|
|
719
|
-
node {
|
|
720
|
-
... on PullRequest {
|
|
721
|
-
number
|
|
722
|
-
mergeCommit {
|
|
723
|
-
oid
|
|
724
|
-
}
|
|
725
|
-
labels(first: $maxLabels) {
|
|
726
|
-
edges {
|
|
727
|
-
node {
|
|
728
|
-
name
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
pageInfo {
|
|
740
|
-
endCursor
|
|
741
|
-
hasNextPage
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}`,
|
|
749
|
-
cursor,
|
|
750
|
-
maxLabels,
|
|
751
|
-
owner: this.owner,
|
|
752
|
-
path,
|
|
753
|
-
perPage,
|
|
754
|
-
repo: this.repo,
|
|
755
|
-
baseRef: `refs/heads/${baseBranch}`,
|
|
756
|
-
}, 3);
|
|
757
|
-
return graphql_to_commits_1.graphqlToCommits(this, response);
|
|
423
|
+
}),
|
|
424
|
+
};
|
|
425
|
+
const opts = {
|
|
426
|
+
repository: {
|
|
427
|
+
owner: options.owner,
|
|
428
|
+
repo: options.repo,
|
|
429
|
+
defaultBranch: (_d = options.defaultBranch) !== null && _d !== void 0 ? _d : (await GitHub.defaultBranch(options.owner, options.repo, apis.octokit)),
|
|
430
|
+
},
|
|
431
|
+
octokitAPIs: apis,
|
|
432
|
+
};
|
|
433
|
+
return new GitHub(opts);
|
|
758
434
|
}
|
|
759
435
|
/**
|
|
760
|
-
*
|
|
436
|
+
* Returns the default branch for a given repository.
|
|
761
437
|
*
|
|
762
|
-
* @param {
|
|
763
|
-
* @param {string}
|
|
764
|
-
* @param {
|
|
765
|
-
* @
|
|
766
|
-
* @throws {GitHubAPIError} on an API error
|
|
438
|
+
* @param {string} owner The GitHub repository owner
|
|
439
|
+
* @param {string} repo The GitHub repository name
|
|
440
|
+
* @param {OctokitType} octokit An authenticated octokit instance
|
|
441
|
+
* @returns {string} Name of the default branch
|
|
767
442
|
*/
|
|
768
|
-
async
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
query: `query pullRequestFiles($cursor: String, $owner: String!, $repo: String!, $maxFilesChanged: Int, $num: Int!) {
|
|
773
|
-
repository(owner: $owner, name: $repo) {
|
|
774
|
-
pullRequest(number: $num) {
|
|
775
|
-
number
|
|
776
|
-
files(first: $maxFilesChanged, after: $cursor) {
|
|
777
|
-
edges {
|
|
778
|
-
node {
|
|
779
|
-
path
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
pageInfo {
|
|
783
|
-
endCursor
|
|
784
|
-
hasNextPage
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
}`,
|
|
790
|
-
cursor,
|
|
791
|
-
maxFilesChanged,
|
|
792
|
-
owner: this.owner,
|
|
793
|
-
repo: this.repo,
|
|
794
|
-
num,
|
|
443
|
+
static async defaultBranch(owner, repo, octokit) {
|
|
444
|
+
const { data } = await octokit.repos.get({
|
|
445
|
+
repo,
|
|
446
|
+
owner,
|
|
795
447
|
});
|
|
796
|
-
return
|
|
448
|
+
return data.default_branch;
|
|
797
449
|
}
|
|
798
450
|
/**
|
|
799
|
-
*
|
|
800
|
-
*
|
|
451
|
+
* Returns the list of commits to the default branch after the provided filter
|
|
452
|
+
* query has been satified.
|
|
801
453
|
*
|
|
802
|
-
* @param {string}
|
|
803
|
-
* @
|
|
454
|
+
* @param {string} targetBranch Target branch of commit
|
|
455
|
+
* @param {CommitFilter} filter Callback function that returns whether a
|
|
456
|
+
* commit/pull request matches certain criteria
|
|
457
|
+
* @param {number} maxResults Limit the number of results searched.
|
|
458
|
+
* Defaults to unlimited.
|
|
459
|
+
* @returns {Commit[]} List of commits to current branch
|
|
804
460
|
* @throws {GitHubAPIError} on an API error
|
|
805
461
|
*/
|
|
806
|
-
async
|
|
807
|
-
const
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
nodes {
|
|
813
|
-
title
|
|
814
|
-
body
|
|
815
|
-
number
|
|
816
|
-
mergeCommit {
|
|
817
|
-
oid
|
|
818
|
-
}
|
|
819
|
-
files(first: 100) {
|
|
820
|
-
nodes {
|
|
821
|
-
path
|
|
822
|
-
}
|
|
823
|
-
pageInfo {
|
|
824
|
-
hasNextPage
|
|
825
|
-
endCursor
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
labels(first: 10) {
|
|
829
|
-
nodes {
|
|
830
|
-
name
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
}`,
|
|
837
|
-
owner: this.owner,
|
|
838
|
-
repo: this.repo,
|
|
839
|
-
baseBranch,
|
|
840
|
-
headBranch,
|
|
841
|
-
});
|
|
842
|
-
let result = undefined;
|
|
843
|
-
const pr = response.repository.pullRequests.nodes[0];
|
|
844
|
-
if (pr) {
|
|
845
|
-
const files = pr.files.nodes.map(({ path }) => path);
|
|
846
|
-
let hasMoreFiles = pr.files.pageInfo.hasNextPage;
|
|
847
|
-
let cursor = pr.files.pageInfo.endCursor;
|
|
848
|
-
while (hasMoreFiles) {
|
|
849
|
-
const next = await this.pullRequestFiles(pr.number, cursor);
|
|
850
|
-
const nextFiles = next.node.files.edges.map(fe => fe.node.path);
|
|
851
|
-
files.push(...nextFiles);
|
|
852
|
-
cursor = next.node.files.pageInfo.endCursor;
|
|
853
|
-
hasMoreFiles = next.node.files.pageInfo.hasNextPage;
|
|
462
|
+
async commitsSince(targetBranch, filter, maxResults = Number.MAX_SAFE_INTEGER) {
|
|
463
|
+
const commits = [];
|
|
464
|
+
const generator = this.mergeCommitIterator(targetBranch, maxResults);
|
|
465
|
+
for await (const commit of generator) {
|
|
466
|
+
if (filter(commit)) {
|
|
467
|
+
break;
|
|
854
468
|
}
|
|
855
|
-
|
|
856
|
-
sha: pr.mergeCommit.oid,
|
|
857
|
-
title: pr.title,
|
|
858
|
-
body: pr.body,
|
|
859
|
-
number: pr.number,
|
|
860
|
-
baseRefName: baseBranch,
|
|
861
|
-
headRefName: headBranch,
|
|
862
|
-
files,
|
|
863
|
-
labels: pr.labels.nodes.map(({ name }) => name),
|
|
864
|
-
};
|
|
469
|
+
commits.push(commit);
|
|
865
470
|
}
|
|
866
|
-
return
|
|
471
|
+
return commits;
|
|
867
472
|
}
|
|
868
473
|
/**
|
|
869
|
-
*
|
|
870
|
-
* is that we might be dealing with the first relese), use the last semver
|
|
871
|
-
* tag that's available on the repository:
|
|
872
|
-
*
|
|
873
|
-
* TODO: it would be good to not need to maintain this logic, and the
|
|
874
|
-
* logic that introspects version based on the prior release PR.
|
|
474
|
+
* Iterate through commit history with a max number of results scanned.
|
|
875
475
|
*
|
|
876
|
-
* @param {string}
|
|
877
|
-
* @param {
|
|
878
|
-
*
|
|
879
|
-
* @
|
|
476
|
+
* @param {string} targetBranch target branch of commit
|
|
477
|
+
* @param {number} maxResults maxResults - Limit the number of results searched.
|
|
478
|
+
* Defaults to unlimited.
|
|
479
|
+
* @yields {Commit}
|
|
480
|
+
* @throws {GitHubAPIError} on an API error
|
|
880
481
|
*/
|
|
881
|
-
async
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
return undefined;
|
|
890
|
-
// We use a slightly modified version of semver's sorting algorithm, which
|
|
891
|
-
// prefixes the numeric part of a pre-release with '0's, so that
|
|
892
|
-
// 010 is greater than > 002.
|
|
893
|
-
versions.sort((v1, v2) => {
|
|
894
|
-
if (v1.includes('-')) {
|
|
895
|
-
const [prefix, suffix] = v1.split('-');
|
|
896
|
-
v1 = prefix + '-' + suffix.replace(/[a-zA-Z.]/, '').padStart(6, '0');
|
|
482
|
+
async *mergeCommitIterator(targetBranch, maxResults = Number.MAX_SAFE_INTEGER) {
|
|
483
|
+
let cursor = undefined;
|
|
484
|
+
let results = 0;
|
|
485
|
+
while (results < maxResults) {
|
|
486
|
+
const response = await this.mergeCommitsGraphQL(targetBranch, cursor);
|
|
487
|
+
// no response usually means that the branch can't be found
|
|
488
|
+
if (!response) {
|
|
489
|
+
break;
|
|
897
490
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
491
|
+
for (let i = 0; i < response.data.length; i++) {
|
|
492
|
+
results += 1;
|
|
493
|
+
yield response.data[i];
|
|
901
494
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
version: tags[versions[0]].version,
|
|
908
|
-
};
|
|
495
|
+
if (!response.pageInfo.hasNextPage) {
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
cursor = response.pageInfo.endCursor;
|
|
499
|
+
}
|
|
909
500
|
}
|
|
910
|
-
async mergeCommitsGraphQL(cursor) {
|
|
911
|
-
|
|
501
|
+
async mergeCommitsGraphQL(targetBranch, cursor) {
|
|
502
|
+
logger_1.logger.debug(`Fetching merge commits on branch ${targetBranch} with cursor: ${cursor}`);
|
|
912
503
|
const response = await this.graphqlRequest({
|
|
913
|
-
query: `query pullRequestsSince($owner: String!, $repo: String!, $num: Int!, $targetBranch: String!, $cursor: String) {
|
|
504
|
+
query: `query pullRequestsSince($owner: String!, $repo: String!, $num: Int!, $maxFilesChanged: Int, $targetBranch: String!, $cursor: String) {
|
|
914
505
|
repository(owner: $owner, name: $repo) {
|
|
915
506
|
ref(qualifiedName: $targetBranch) {
|
|
916
507
|
target {
|
|
@@ -932,6 +523,15 @@ class GitHub {
|
|
|
932
523
|
mergeCommit {
|
|
933
524
|
oid
|
|
934
525
|
}
|
|
526
|
+
files(first: $maxFilesChanged) {
|
|
527
|
+
nodes {
|
|
528
|
+
path
|
|
529
|
+
}
|
|
530
|
+
pageInfo {
|
|
531
|
+
endCursor
|
|
532
|
+
hasNextPage
|
|
533
|
+
}
|
|
534
|
+
}
|
|
935
535
|
}
|
|
936
536
|
}
|
|
937
537
|
sha: oid
|
|
@@ -948,10 +548,11 @@ class GitHub {
|
|
|
948
548
|
}
|
|
949
549
|
}`,
|
|
950
550
|
cursor,
|
|
951
|
-
owner: this.owner,
|
|
952
|
-
repo: this.repo,
|
|
551
|
+
owner: this.repository.owner,
|
|
552
|
+
repo: this.repository.repo,
|
|
953
553
|
num: 25,
|
|
954
554
|
targetBranch,
|
|
555
|
+
maxFilesChanged: 64,
|
|
955
556
|
});
|
|
956
557
|
// if the branch does exist, return null
|
|
957
558
|
if (!response.repository.ref) {
|
|
@@ -960,71 +561,60 @@ class GitHub {
|
|
|
960
561
|
}
|
|
961
562
|
const history = response.repository.ref.target.history;
|
|
962
563
|
const commits = (history.nodes || []);
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
labels: pullRequest.labels.nodes.map(node => node.name),
|
|
985
|
-
},
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
return {
|
|
989
|
-
commit,
|
|
564
|
+
const commitData = [];
|
|
565
|
+
for (const graphCommit of commits) {
|
|
566
|
+
const commit = {
|
|
567
|
+
sha: graphCommit.sha,
|
|
568
|
+
message: graphCommit.message,
|
|
569
|
+
files: [],
|
|
570
|
+
};
|
|
571
|
+
const pullRequest = graphCommit.associatedPullRequests.nodes.find(pr => {
|
|
572
|
+
return pr.mergeCommit && pr.mergeCommit.oid === graphCommit.sha;
|
|
573
|
+
});
|
|
574
|
+
if (pullRequest) {
|
|
575
|
+
const files = pullRequest.files.nodes.map(node => node.path);
|
|
576
|
+
commit.pullRequest = {
|
|
577
|
+
sha: commit.sha,
|
|
578
|
+
number: pullRequest.number,
|
|
579
|
+
baseBranchName: pullRequest.baseRefName,
|
|
580
|
+
headBranchName: pullRequest.headRefName,
|
|
581
|
+
title: pullRequest.title,
|
|
582
|
+
body: pullRequest.body,
|
|
583
|
+
labels: pullRequest.labels.nodes.map(node => node.name),
|
|
584
|
+
files,
|
|
990
585
|
};
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
/**
|
|
995
|
-
* Search through commit history to find the latest commit that matches to
|
|
996
|
-
* provided filter.
|
|
997
|
-
*
|
|
998
|
-
* @param {CommitFilter} filter - Callback function that returns whether a
|
|
999
|
-
* commit/pull request matches certain criteria
|
|
1000
|
-
* @param {number} maxResults - Limit the number of results searched.
|
|
1001
|
-
* Defaults to unlimited.
|
|
1002
|
-
* @returns {CommitWithPullRequest}
|
|
1003
|
-
* @throws {GitHubAPIError} on an API error
|
|
1004
|
-
*/
|
|
1005
|
-
async findMergeCommit(filter, maxResults = Number.MAX_SAFE_INTEGER) {
|
|
1006
|
-
const generator = this.mergeCommitIterator(maxResults);
|
|
1007
|
-
for await (const commitWithPullRequest of generator) {
|
|
1008
|
-
if (filter(commitWithPullRequest.commit, commitWithPullRequest.pullRequest)) {
|
|
1009
|
-
return commitWithPullRequest;
|
|
586
|
+
// We cannot directly fetch files on commits via graphql, only provide file
|
|
587
|
+
// information for commits with associated pull requests
|
|
588
|
+
commit.files = files;
|
|
1010
589
|
}
|
|
590
|
+
else {
|
|
591
|
+
// In this case, there is no squashed merge commit. This could be a simple
|
|
592
|
+
// merge commit, a rebase merge commit, or a direct commit to the branch.
|
|
593
|
+
// Fallback to fetching the list of commits from the REST API. In the future
|
|
594
|
+
// we can perhaps lazy load these.
|
|
595
|
+
commit.files = await this.getCommitFiles(graphCommit.sha);
|
|
596
|
+
}
|
|
597
|
+
commitData.push(commit);
|
|
1011
598
|
}
|
|
1012
|
-
return
|
|
599
|
+
return {
|
|
600
|
+
pageInfo: history.pageInfo,
|
|
601
|
+
data: commitData,
|
|
602
|
+
};
|
|
1013
603
|
}
|
|
1014
604
|
/**
|
|
1015
|
-
* Iterate through
|
|
605
|
+
* Iterate through merged pull requests with a max number of results scanned.
|
|
1016
606
|
*
|
|
1017
|
-
* @param
|
|
607
|
+
* @param {number} maxResults maxResults - Limit the number of results searched.
|
|
1018
608
|
* Defaults to unlimited.
|
|
1019
|
-
* @yields {
|
|
609
|
+
* @yields {PullRequest}
|
|
1020
610
|
* @throws {GitHubAPIError} on an API error
|
|
1021
611
|
*/
|
|
1022
|
-
async *
|
|
612
|
+
async *pullRequestIterator(targetBranch, status = 'MERGED', maxResults = Number.MAX_SAFE_INTEGER) {
|
|
1023
613
|
let cursor = undefined;
|
|
1024
614
|
let results = 0;
|
|
1025
615
|
while (results < maxResults) {
|
|
1026
|
-
const response = await this.
|
|
1027
|
-
// no response usually means
|
|
616
|
+
const response = await this.pullRequestsGraphQL(targetBranch, status, cursor);
|
|
617
|
+
// no response usually means we ran out of results
|
|
1028
618
|
if (!response) {
|
|
1029
619
|
break;
|
|
1030
620
|
}
|
|
@@ -1039,194 +629,238 @@ class GitHub {
|
|
|
1039
629
|
}
|
|
1040
630
|
}
|
|
1041
631
|
/**
|
|
1042
|
-
*
|
|
632
|
+
* Return a list of merged pull requests. The list is not guaranteed to be sorted
|
|
633
|
+
* by merged_at, but is generally most recent first.
|
|
1043
634
|
*
|
|
1044
|
-
* @param
|
|
1045
|
-
*
|
|
1046
|
-
* @
|
|
635
|
+
* @param {string} targetBranch - Base branch of the pull request. Defaults to
|
|
636
|
+
* the configured default branch.
|
|
637
|
+
* @param {number} page - Page of results. Defaults to 1.
|
|
638
|
+
* @param {number} perPage - Number of results per page. Defaults to 100.
|
|
639
|
+
* @returns {PullRequestHistory | null} - List of merged pull requests
|
|
1047
640
|
* @throws {GitHubAPIError} on an API error
|
|
1048
641
|
*/
|
|
1049
|
-
async
|
|
1050
|
-
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
642
|
+
async pullRequestsGraphQL(targetBranch, states = 'MERGED', cursor) {
|
|
643
|
+
logger_1.logger.debug(`Fetching ${states} pull requests on branch ${targetBranch} with cursor ${cursor}`);
|
|
644
|
+
const response = await this.graphqlRequest({
|
|
645
|
+
query: `query mergedPullRequests($owner: String!, $repo: String!, $num: Int!, $maxFilesChanged: Int, $targetBranch: String!, $states: [PullRequestState!], $cursor: String) {
|
|
646
|
+
repository(owner: $owner, name: $repo) {
|
|
647
|
+
pullRequests(first: $num, after: $cursor, baseRefName: $targetBranch, states: $states, orderBy: {field: CREATED_AT, direction: DESC}) {
|
|
648
|
+
nodes {
|
|
649
|
+
number
|
|
650
|
+
title
|
|
651
|
+
baseRefName
|
|
652
|
+
headRefName
|
|
653
|
+
labels(first: 10) {
|
|
654
|
+
nodes {
|
|
655
|
+
name
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
body
|
|
659
|
+
mergeCommit {
|
|
660
|
+
oid
|
|
661
|
+
}
|
|
662
|
+
files(first: $maxFilesChanged) {
|
|
663
|
+
nodes {
|
|
664
|
+
path
|
|
665
|
+
}
|
|
666
|
+
pageInfo {
|
|
667
|
+
endCursor
|
|
668
|
+
hasNextPage
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
pageInfo {
|
|
673
|
+
endCursor
|
|
674
|
+
hasNextPage
|
|
675
|
+
}
|
|
1060
676
|
}
|
|
1061
|
-
|
|
677
|
+
}
|
|
678
|
+
}`,
|
|
679
|
+
cursor,
|
|
680
|
+
owner: this.repository.owner,
|
|
681
|
+
repo: this.repository.repo,
|
|
682
|
+
num: 25,
|
|
683
|
+
targetBranch,
|
|
684
|
+
states,
|
|
685
|
+
maxFilesChanged: 64,
|
|
686
|
+
});
|
|
687
|
+
if (!response.repository.pullRequests) {
|
|
688
|
+
logger_1.logger.warn(`Could not find merged pull requests for branch ${targetBranch} - it likely does not exist.`);
|
|
689
|
+
return null;
|
|
1062
690
|
}
|
|
691
|
+
const pullRequests = (response.repository.pullRequests.nodes ||
|
|
692
|
+
[]);
|
|
693
|
+
return {
|
|
694
|
+
pageInfo: response.repository.pullRequests.pageInfo,
|
|
695
|
+
data: pullRequests.map(pullRequest => {
|
|
696
|
+
var _a;
|
|
697
|
+
return {
|
|
698
|
+
sha: (_a = pullRequest.mergeCommit) === null || _a === void 0 ? void 0 : _a.oid,
|
|
699
|
+
number: pullRequest.number,
|
|
700
|
+
baseBranchName: pullRequest.baseRefName,
|
|
701
|
+
headBranchName: pullRequest.headRefName,
|
|
702
|
+
labels: (pullRequest.labels.nodes || []).map(l => l.name),
|
|
703
|
+
title: pullRequest.title,
|
|
704
|
+
body: pullRequest.body + '',
|
|
705
|
+
files: pullRequest.files.nodes.map(node => node.path),
|
|
706
|
+
};
|
|
707
|
+
}),
|
|
708
|
+
};
|
|
1063
709
|
}
|
|
1064
710
|
/**
|
|
1065
|
-
*
|
|
1066
|
-
* query has been satified.
|
|
711
|
+
* Iterate through merged pull requests with a max number of results scanned.
|
|
1067
712
|
*
|
|
1068
|
-
* @param {
|
|
1069
|
-
* commit/pull request matches certain criteria
|
|
1070
|
-
* @param {number} maxResults - Limit the number of results searched.
|
|
713
|
+
* @param {number} maxResults maxResults - Limit the number of results searched.
|
|
1071
714
|
* Defaults to unlimited.
|
|
1072
|
-
* @
|
|
715
|
+
* @yields {GitHubRelease}
|
|
1073
716
|
* @throws {GitHubAPIError} on an API error
|
|
1074
717
|
*/
|
|
1075
|
-
async
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
718
|
+
async *releaseIterator(maxResults = Number.MAX_SAFE_INTEGER) {
|
|
719
|
+
let results = 0;
|
|
720
|
+
let cursor = undefined;
|
|
721
|
+
while (results < maxResults) {
|
|
722
|
+
const response = await this.releaseGraphQL(cursor);
|
|
723
|
+
if (!response) {
|
|
724
|
+
break;
|
|
725
|
+
}
|
|
726
|
+
for (let i = 0; i < response.data.length; i++) {
|
|
727
|
+
results += 1;
|
|
728
|
+
yield response.data[i];
|
|
729
|
+
}
|
|
730
|
+
if (!response.pageInfo.hasNextPage) {
|
|
1080
731
|
break;
|
|
1081
732
|
}
|
|
1082
|
-
|
|
733
|
+
cursor = response.pageInfo.endCursor;
|
|
1083
734
|
}
|
|
1084
|
-
return commits;
|
|
1085
735
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
if (filter(mergedPullRequest)) {
|
|
1104
|
-
return mergedPullRequest;
|
|
736
|
+
async releaseGraphQL(cursor) {
|
|
737
|
+
logger_1.logger.debug(`Fetching releases with cursor ${cursor}`);
|
|
738
|
+
const response = await this.graphqlRequest({
|
|
739
|
+
query: `query releases($owner: String!, $repo: String!, $num: Int!, $cursor: String) {
|
|
740
|
+
repository(owner: $owner, name: $repo) {
|
|
741
|
+
releases(first: $num, after: $cursor, orderBy: {field: CREATED_AT, direction: DESC}) {
|
|
742
|
+
nodes {
|
|
743
|
+
name
|
|
744
|
+
tag {
|
|
745
|
+
name
|
|
746
|
+
}
|
|
747
|
+
tagCommit {
|
|
748
|
+
oid
|
|
749
|
+
}
|
|
750
|
+
url
|
|
751
|
+
description
|
|
752
|
+
isDraft
|
|
1105
753
|
}
|
|
754
|
+
pageInfo {
|
|
755
|
+
endCursor
|
|
756
|
+
hasNextPage
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}`,
|
|
761
|
+
cursor,
|
|
762
|
+
owner: this.repository.owner,
|
|
763
|
+
repo: this.repository.repo,
|
|
764
|
+
num: 25,
|
|
765
|
+
});
|
|
766
|
+
if (!response.repository.releases) {
|
|
767
|
+
logger_1.logger.warn('Could not find releases.');
|
|
768
|
+
return null;
|
|
1106
769
|
}
|
|
1107
|
-
|
|
770
|
+
const releases = (response.repository.releases.nodes ||
|
|
771
|
+
[]);
|
|
772
|
+
return {
|
|
773
|
+
pageInfo: response.repository.releases.pageInfo,
|
|
774
|
+
data: releases
|
|
775
|
+
.filter(release => !!release.tagCommit)
|
|
776
|
+
.map(release => {
|
|
777
|
+
if (!release.tag || !release.tagCommit) {
|
|
778
|
+
logger_1.logger.debug(release);
|
|
779
|
+
}
|
|
780
|
+
return {
|
|
781
|
+
name: release.name || undefined,
|
|
782
|
+
tagName: release.tag ? release.tag.name : 'unknown',
|
|
783
|
+
sha: release.tagCommit.oid,
|
|
784
|
+
notes: release.description,
|
|
785
|
+
url: release.url,
|
|
786
|
+
draft: release.isDraft,
|
|
787
|
+
};
|
|
788
|
+
}),
|
|
789
|
+
};
|
|
1108
790
|
}
|
|
1109
791
|
/**
|
|
1110
|
-
*
|
|
1111
|
-
* branch and looks like a release PR.
|
|
792
|
+
* Fetch the contents of a file from the configured branch
|
|
1112
793
|
*
|
|
1113
|
-
*
|
|
794
|
+
* @param {string} path The path to the file in the repository
|
|
795
|
+
* @returns {GitHubFileContents}
|
|
796
|
+
* @throws {GitHubAPIError} on other API errors
|
|
797
|
+
*/
|
|
798
|
+
async getFileContents(path) {
|
|
799
|
+
return await this.getFileContentsOnBranch(path, this.repository.defaultBranch);
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Fetch the contents of a file
|
|
1114
803
|
*
|
|
1115
|
-
* @param {string
|
|
1116
|
-
*
|
|
1117
|
-
* @
|
|
1118
|
-
*
|
|
1119
|
-
* @param {boolean} preRelease - Whether to include pre-release
|
|
1120
|
-
* versions in the response. Defaults to true.
|
|
1121
|
-
* @param {number} maxResults - Limit the number of results searched.
|
|
1122
|
-
* Defaults to unlimited.
|
|
1123
|
-
* @returns {MergedGitHubPR|undefined}
|
|
1124
|
-
* @throws {GitHubAPIError} on an API error
|
|
804
|
+
* @param {string} path The path to the file in the repository
|
|
805
|
+
* @param {string} branch The branch to fetch from
|
|
806
|
+
* @returns {GitHubFileContents}
|
|
807
|
+
* @throws {GitHubAPIError} on other API errors
|
|
1125
808
|
*/
|
|
1126
|
-
async
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
if (
|
|
1133
|
-
|
|
1134
|
-
return false;
|
|
1135
|
-
}
|
|
1136
|
-
const branchName = branch_name_1.BranchName.parse(mergedPullRequest.headRefName);
|
|
1137
|
-
if (!branchName) {
|
|
1138
|
-
return false;
|
|
1139
|
-
}
|
|
1140
|
-
// If branchPrefix is specified, ensure it is found in the branch name.
|
|
1141
|
-
// If branchPrefix is not specified, component should also be undefined.
|
|
1142
|
-
if (branchName.getComponent() !== branchPrefix) {
|
|
1143
|
-
return false;
|
|
1144
|
-
}
|
|
1145
|
-
// In this implementation we expect to have a release version
|
|
1146
|
-
const version = branchName.getVersion();
|
|
1147
|
-
if (!version) {
|
|
1148
|
-
return false;
|
|
1149
|
-
}
|
|
1150
|
-
// What's left by now should just be the version string.
|
|
1151
|
-
// Check for pre-releases if needed.
|
|
1152
|
-
if (!preRelease && version.indexOf('-') >= 0) {
|
|
1153
|
-
return false;
|
|
1154
|
-
}
|
|
1155
|
-
// Make sure we did get a valid semver.
|
|
1156
|
-
const normalizedVersion = semver.valid(version);
|
|
1157
|
-
if (!normalizedVersion) {
|
|
1158
|
-
return false;
|
|
809
|
+
async getFileContentsOnBranch(path, branch) {
|
|
810
|
+
logger_1.logger.debug(`Fetching ${path} from branch ${branch}`);
|
|
811
|
+
try {
|
|
812
|
+
return await this.getFileContentsWithSimpleAPI(path, branch);
|
|
813
|
+
}
|
|
814
|
+
catch (err) {
|
|
815
|
+
if (err.status === 403) {
|
|
816
|
+
return await this.getFileContentsWithDataAPI(path, branch);
|
|
1159
817
|
}
|
|
1160
|
-
|
|
1161
|
-
}
|
|
1162
|
-
return mergedReleasePullRequest;
|
|
818
|
+
throw err;
|
|
819
|
+
}
|
|
1163
820
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
if (labelsB.indexOf(label) === -1)
|
|
1168
|
-
hasAll = false;
|
|
1169
|
-
});
|
|
1170
|
-
return hasAll;
|
|
821
|
+
async getFileJson(path, branch) {
|
|
822
|
+
const content = await this.getFileContentsOnBranch(path, branch);
|
|
823
|
+
return JSON.parse(content.parsedContent);
|
|
1171
824
|
}
|
|
1172
825
|
/**
|
|
1173
|
-
*
|
|
826
|
+
* Returns a list of paths to all files with a given name.
|
|
1174
827
|
*
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
828
|
+
* If a prefix is specified, only return paths that match
|
|
829
|
+
* the provided prefix.
|
|
830
|
+
*
|
|
831
|
+
* @param filename The name of the file to find
|
|
832
|
+
* @param prefix Optional path prefix used to filter results
|
|
833
|
+
* @returns {string[]} List of file paths
|
|
1178
834
|
* @throws {GitHubAPIError} on an API error
|
|
1179
835
|
*/
|
|
1180
|
-
async
|
|
1181
|
-
|
|
1182
|
-
const openReleasePRs = [];
|
|
1183
|
-
const pullsResponse = (await this.request(`GET /repos/:owner/:repo/pulls?state=open&per_page=${perPage}`, {
|
|
1184
|
-
owner: this.owner,
|
|
1185
|
-
repo: this.repo,
|
|
1186
|
-
}));
|
|
1187
|
-
for (const pull of pullsResponse.data) {
|
|
1188
|
-
// Verify that this PR was based against our base branch of interest.
|
|
1189
|
-
if (!pull.base || pull.base.label !== baseLabel)
|
|
1190
|
-
continue;
|
|
1191
|
-
let hasAllLabels = false;
|
|
1192
|
-
const observedLabels = pull.labels.map(l => l.name);
|
|
1193
|
-
for (const expectedLabel of labels) {
|
|
1194
|
-
if (observedLabels.includes(expectedLabel)) {
|
|
1195
|
-
hasAllLabels = true;
|
|
1196
|
-
}
|
|
1197
|
-
else {
|
|
1198
|
-
hasAllLabels = false;
|
|
1199
|
-
break;
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
if (hasAllLabels)
|
|
1203
|
-
openReleasePRs.push(pull);
|
|
1204
|
-
}
|
|
1205
|
-
return openReleasePRs;
|
|
836
|
+
async findFilesByFilename(filename, prefix) {
|
|
837
|
+
return this.findFilesByFilenameAndRef(filename, this.repository.defaultBranch, prefix);
|
|
1206
838
|
}
|
|
1207
839
|
/**
|
|
1208
|
-
*
|
|
840
|
+
* Open a pull request
|
|
1209
841
|
*
|
|
1210
|
-
* @param {
|
|
1211
|
-
* @param {
|
|
1212
|
-
* @
|
|
842
|
+
* @param {ReleasePullRequest} releasePullRequest Pull request data to update
|
|
843
|
+
* @param {string} targetBranch The base branch of the pull request
|
|
844
|
+
* @param {GitHubPR} options The pull request options
|
|
1213
845
|
* @throws {GitHubAPIError} on an API error
|
|
1214
846
|
*/
|
|
1215
|
-
async
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
logger_1.logger.warn('release labels were not added, due to PR being created from fork');
|
|
1220
|
-
return false;
|
|
847
|
+
async createReleasePullRequest(releasePullRequest, targetBranch, options) {
|
|
848
|
+
let message = releasePullRequest.title.toString();
|
|
849
|
+
if (options === null || options === void 0 ? void 0 : options.signoffUser) {
|
|
850
|
+
message = signoff_commit_message_1.signoffCommitMessage(message, options.signoffUser);
|
|
1221
851
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
852
|
+
return await this.createPullRequest({
|
|
853
|
+
headBranchName: releasePullRequest.headRefName,
|
|
854
|
+
baseBranchName: targetBranch,
|
|
855
|
+
number: -1,
|
|
856
|
+
title: releasePullRequest.title.toString(),
|
|
857
|
+
body: releasePullRequest.body.toString().slice(0, MAX_ISSUE_BODY_SIZE),
|
|
858
|
+
labels: releasePullRequest.labels,
|
|
859
|
+
files: [],
|
|
860
|
+
}, targetBranch, message, releasePullRequest.updates, {
|
|
861
|
+
fork: options === null || options === void 0 ? void 0 : options.fork,
|
|
862
|
+
draft: releasePullRequest.draft,
|
|
1228
863
|
});
|
|
1229
|
-
return true;
|
|
1230
864
|
}
|
|
1231
865
|
/**
|
|
1232
866
|
* Given a set of proposed updates, build a changeset to suggest.
|
|
@@ -1241,10 +875,10 @@ class GitHub {
|
|
|
1241
875
|
for (const update of updates) {
|
|
1242
876
|
let content;
|
|
1243
877
|
try {
|
|
1244
|
-
if (update.
|
|
878
|
+
if (update.cachedFileContents) {
|
|
1245
879
|
// we already loaded the file contents earlier, let's not
|
|
1246
880
|
// hit GitHub again.
|
|
1247
|
-
content = { data: update.
|
|
881
|
+
content = { data: update.cachedFileContents };
|
|
1248
882
|
}
|
|
1249
883
|
else {
|
|
1250
884
|
const fileContent = await this.getFileContentsOnBranch(update.path, defaultBranch);
|
|
@@ -1256,15 +890,15 @@ class GitHub {
|
|
|
1256
890
|
throw err;
|
|
1257
891
|
// if the file is missing and create = false, just continue
|
|
1258
892
|
// to the next update, otherwise create the file.
|
|
1259
|
-
if (!update.
|
|
1260
|
-
logger_1.logger.warn(`file ${
|
|
893
|
+
if (!update.createIfMissing) {
|
|
894
|
+
logger_1.logger.warn(`file ${update.path} did not exist`);
|
|
1261
895
|
continue;
|
|
1262
896
|
}
|
|
1263
897
|
}
|
|
1264
898
|
const contentText = content
|
|
1265
899
|
? Buffer.from(content.data.content, 'base64').toString('utf8')
|
|
1266
900
|
: undefined;
|
|
1267
|
-
const updatedContent = update.updateContent(contentText);
|
|
901
|
+
const updatedContent = update.updater.updateContent(contentText);
|
|
1268
902
|
if (updatedContent) {
|
|
1269
903
|
changes.set(update.path, {
|
|
1270
904
|
content: updatedContent,
|
|
@@ -1274,92 +908,6 @@ class GitHub {
|
|
|
1274
908
|
}
|
|
1275
909
|
return changes;
|
|
1276
910
|
}
|
|
1277
|
-
// The base label is basically the default branch, attached to the owner.
|
|
1278
|
-
async getBaseLabel() {
|
|
1279
|
-
const baseBranch = await this.getDefaultBranch();
|
|
1280
|
-
return `${this.owner}:${baseBranch}`;
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Returns the branch we are targetting for releases. Defaults
|
|
1284
|
-
* to the repository's default/primary branch.
|
|
1285
|
-
*
|
|
1286
|
-
* @returns {string}
|
|
1287
|
-
* @throws {GitHubAPIError} on an API error
|
|
1288
|
-
*/
|
|
1289
|
-
async getDefaultBranch() {
|
|
1290
|
-
if (!this.defaultBranch) {
|
|
1291
|
-
this.defaultBranch = await this.getRepositoryDefaultBranch();
|
|
1292
|
-
}
|
|
1293
|
-
return this.defaultBranch;
|
|
1294
|
-
}
|
|
1295
|
-
// Takes a potentially unqualified branch name, and turns it
|
|
1296
|
-
// into a fully qualified ref.
|
|
1297
|
-
//
|
|
1298
|
-
// e.g. main -> refs/heads/main
|
|
1299
|
-
static fullyQualifyBranchRef(refName) {
|
|
1300
|
-
let final = refName;
|
|
1301
|
-
if (final.indexOf('/') < 0) {
|
|
1302
|
-
final = `refs/heads/${final}`;
|
|
1303
|
-
}
|
|
1304
|
-
return final;
|
|
1305
|
-
}
|
|
1306
|
-
/**
|
|
1307
|
-
* Fetch the contents of a file using the Git data API
|
|
1308
|
-
*
|
|
1309
|
-
* @param {string} path The path to the file in the repository
|
|
1310
|
-
* @param {string} branch The branch to fetch from
|
|
1311
|
-
* @returns {GitHubFileContents}
|
|
1312
|
-
* @throws {GitHubAPIError} on other API errors
|
|
1313
|
-
*/
|
|
1314
|
-
async getFileContentsWithDataAPI(path, branch) {
|
|
1315
|
-
const options = {
|
|
1316
|
-
owner: this.owner,
|
|
1317
|
-
repo: this.repo,
|
|
1318
|
-
branch,
|
|
1319
|
-
};
|
|
1320
|
-
const repoTree = await this.request('GET /repos/:owner/:repo/git/trees/:branch', options);
|
|
1321
|
-
const blobDescriptor = repoTree.data.tree.find(tree => tree.path === path);
|
|
1322
|
-
if (!blobDescriptor) {
|
|
1323
|
-
throw new Error(`Could not find requested path: ${path}`);
|
|
1324
|
-
}
|
|
1325
|
-
const resp = await this.request('GET /repos/:owner/:repo/git/blobs/:sha', {
|
|
1326
|
-
owner: this.owner,
|
|
1327
|
-
repo: this.repo,
|
|
1328
|
-
sha: blobDescriptor.sha,
|
|
1329
|
-
});
|
|
1330
|
-
return {
|
|
1331
|
-
parsedContent: Buffer.from(resp.data.content, 'base64').toString('utf8'),
|
|
1332
|
-
content: resp.data.content,
|
|
1333
|
-
sha: resp.data.sha,
|
|
1334
|
-
};
|
|
1335
|
-
}
|
|
1336
|
-
/**
|
|
1337
|
-
* Fetch the contents of a file from the configured branch
|
|
1338
|
-
*
|
|
1339
|
-
* @param {string} path The path to the file in the repository
|
|
1340
|
-
* @returns {GitHubFileContents}
|
|
1341
|
-
* @throws {GitHubAPIError} on other API errors
|
|
1342
|
-
*/
|
|
1343
|
-
async getFileContents(path) {
|
|
1344
|
-
return await this.getFileContentsOnBranch(path, await this.getDefaultBranch());
|
|
1345
|
-
}
|
|
1346
|
-
normalizePrefix(prefix) {
|
|
1347
|
-
return prefix.replace(/^[/\\]/, '').replace(/[/\\]$/, '');
|
|
1348
|
-
}
|
|
1349
|
-
/**
|
|
1350
|
-
* Returns a list of paths to all files with a given name.
|
|
1351
|
-
*
|
|
1352
|
-
* If a prefix is specified, only return paths that match
|
|
1353
|
-
* the provided prefix.
|
|
1354
|
-
*
|
|
1355
|
-
* @param filename The name of the file to find
|
|
1356
|
-
* @param prefix Optional path prefix used to filter results
|
|
1357
|
-
* @returns {string[]} List of file paths
|
|
1358
|
-
* @throws {GitHubAPIError} on an API error
|
|
1359
|
-
*/
|
|
1360
|
-
async findFilesByFilename(filename, prefix) {
|
|
1361
|
-
return this.findFilesByFilenameAndRef(filename, await this.getDefaultBranch(), prefix);
|
|
1362
|
-
}
|
|
1363
911
|
/**
|
|
1364
912
|
* Returns a list of paths to all files with a given file
|
|
1365
913
|
* extension.
|
|
@@ -1374,10 +922,34 @@ class GitHub {
|
|
|
1374
922
|
* @throws {GitHubAPIError} on an API error
|
|
1375
923
|
*/
|
|
1376
924
|
async findFilesByExtension(extension, prefix) {
|
|
1377
|
-
return this.findFilesByExtensionAndRef(extension,
|
|
925
|
+
return this.findFilesByExtensionAndRef(extension, this.repository.defaultBranch, prefix);
|
|
1378
926
|
}
|
|
1379
927
|
}
|
|
1380
928
|
exports.GitHub = GitHub;
|
|
929
|
+
// Takes a potentially unqualified branch name, and turns it
|
|
930
|
+
// into a fully qualified ref.
|
|
931
|
+
//
|
|
932
|
+
// e.g. main -> refs/heads/main
|
|
933
|
+
function fullyQualifyBranchRef(refName) {
|
|
934
|
+
let final = refName;
|
|
935
|
+
if (final.indexOf('/') < 0) {
|
|
936
|
+
final = `refs/heads/${final}`;
|
|
937
|
+
}
|
|
938
|
+
return final;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Normalize a provided prefix by removing leading and trailing
|
|
942
|
+
* slashes.
|
|
943
|
+
*
|
|
944
|
+
* @param prefix String to normalize
|
|
945
|
+
*/
|
|
946
|
+
function normalizePrefix(prefix) {
|
|
947
|
+
const normalized = prefix.replace(/^[/\\]/, '').replace(/[/\\]$/, '');
|
|
948
|
+
if (normalized === manifest_1.ROOT_PROJECT_PATH) {
|
|
949
|
+
return '';
|
|
950
|
+
}
|
|
951
|
+
return normalized;
|
|
952
|
+
}
|
|
1381
953
|
/**
|
|
1382
954
|
* Wrap an async method with error handling
|
|
1383
955
|
*
|