feat(dev-infra): provide tooling to check what branches a pr targets (#39504)
Create a command in the `ng-dev` toolset that allows user's to check what branches a PR will merge into based on its targeting. PR Close #39504
This commit is contained in:
parent
7e33cb9626
commit
d0dd0e80f6
@ -2599,6 +2599,235 @@ function buildNgbotParser(localYargs) {
|
||||
return localYargs.help().strict().demandCommand().command('verify', 'Verify the NgBot config', {}, () => verify());
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/** Loads and validates the merge configuration. */
|
||||
function loadAndValidateConfig(config, api) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function () {
|
||||
var mergeConfig, errors;
|
||||
return tslib.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (config.merge === undefined) {
|
||||
return [2 /*return*/, { errors: ['No merge configuration found. Set the `merge` configuration.'] }];
|
||||
}
|
||||
if (typeof config.merge !== 'function') {
|
||||
return [2 /*return*/, { errors: ['Expected merge configuration to be defined lazily through a function.'] }];
|
||||
}
|
||||
return [4 /*yield*/, config.merge(api)];
|
||||
case 1:
|
||||
mergeConfig = _a.sent();
|
||||
errors = validateMergeConfig(mergeConfig);
|
||||
if (errors.length) {
|
||||
return [2 /*return*/, { errors: errors }];
|
||||
}
|
||||
return [2 /*return*/, { config: mergeConfig }];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
/** Validates the specified configuration. Returns a list of failure messages. */
|
||||
function validateMergeConfig(config) {
|
||||
var errors = [];
|
||||
if (!config.labels) {
|
||||
errors.push('No label configuration.');
|
||||
}
|
||||
else if (!Array.isArray(config.labels)) {
|
||||
errors.push('Label configuration needs to be an array.');
|
||||
}
|
||||
if (!config.claSignedLabel) {
|
||||
errors.push('No CLA signed label configured.');
|
||||
}
|
||||
if (!config.mergeReadyLabel) {
|
||||
errors.push('No merge ready label configured.');
|
||||
}
|
||||
if (config.githubApiMerge === undefined) {
|
||||
errors.push('No explicit choice of merge strategy. Please set `githubApiMerge`.');
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/** Checks whether the specified value matches the given pattern. */
|
||||
function matchesPattern(value, pattern) {
|
||||
return typeof pattern === 'string' ? value === pattern : pattern.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/**
|
||||
* Unique error that can be thrown in the merge configuration if an
|
||||
* invalid branch is targeted.
|
||||
*/
|
||||
var InvalidTargetBranchError = /** @class */ (function () {
|
||||
function InvalidTargetBranchError(failureMessage) {
|
||||
this.failureMessage = failureMessage;
|
||||
}
|
||||
return InvalidTargetBranchError;
|
||||
}());
|
||||
/**
|
||||
* Unique error that can be thrown in the merge configuration if an
|
||||
* invalid label has been applied to a pull request.
|
||||
*/
|
||||
var InvalidTargetLabelError = /** @class */ (function () {
|
||||
function InvalidTargetLabelError(failureMessage) {
|
||||
this.failureMessage = failureMessage;
|
||||
}
|
||||
return InvalidTargetLabelError;
|
||||
}());
|
||||
/** Gets the target label from the specified pull request labels. */
|
||||
function getTargetLabelFromPullRequest(config, labels) {
|
||||
var e_1, _a;
|
||||
var _loop_1 = function (label) {
|
||||
var match = config.labels.find(function (_a) {
|
||||
var pattern = _a.pattern;
|
||||
return matchesPattern(label, pattern);
|
||||
});
|
||||
if (match !== undefined) {
|
||||
return { value: match };
|
||||
}
|
||||
};
|
||||
try {
|
||||
for (var labels_1 = tslib.__values(labels), labels_1_1 = labels_1.next(); !labels_1_1.done; labels_1_1 = labels_1.next()) {
|
||||
var label = labels_1_1.value;
|
||||
var state_1 = _loop_1(label);
|
||||
if (typeof state_1 === "object")
|
||||
return state_1.value;
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (labels_1_1 && !labels_1_1.done && (_a = labels_1.return)) _a.call(labels_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Gets the branches from the specified target label.
|
||||
*
|
||||
* @throws {InvalidTargetLabelError} Invalid label has been applied to pull request.
|
||||
* @throws {InvalidTargetBranchError} Invalid Github target branch has been selected.
|
||||
*/
|
||||
function getBranchesFromTargetLabel(label, githubTargetBranch) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function () {
|
||||
var _a;
|
||||
return tslib.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (!(typeof label.branches === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, label.branches(githubTargetBranch)];
|
||||
case 1:
|
||||
_a = _b.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 2: return [4 /*yield*/, label.branches];
|
||||
case 3:
|
||||
_a = _b.sent();
|
||||
_b.label = 4;
|
||||
case 4: return [2 /*return*/, _a];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
function checkTargetBranchesForPr(prNumber, jsonOutput = false) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function* () {
|
||||
/** The ng-dev configuration. */
|
||||
const config = getConfig();
|
||||
/** Repo owner and name for the github repository. */
|
||||
const { owner, name: repo } = config.github;
|
||||
/** The git client to get a Github API service instance. */
|
||||
const git = new GitClient(undefined, config);
|
||||
/** The validated merge config. */
|
||||
const { config: mergeConfig, errors } = yield loadAndValidateConfig(config, git.github);
|
||||
if (errors !== undefined) {
|
||||
throw Error(`Invalid configuration found: ${errors}`);
|
||||
}
|
||||
/** The current state of the pull request from Github. */
|
||||
const prData = (yield git.github.pulls.get({ owner, repo, pull_number: prNumber })).data;
|
||||
/** The list of labels on the PR as strings. */
|
||||
const labels = prData.labels.map(l => l.name);
|
||||
/** The branch targetted via the Github UI. */
|
||||
const githubTargetBranch = prData.base.ref;
|
||||
/** The active label which is being used for targetting the PR. */
|
||||
const targetLabel = getTargetLabelFromPullRequest(mergeConfig, labels);
|
||||
if (targetLabel === null) {
|
||||
error(red(`No target label was found on pr #${prNumber}`));
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
/** The target branches based on the target label and branch targetted in the Github UI. */
|
||||
const targets = yield getBranchesFromTargetLabel(targetLabel, githubTargetBranch);
|
||||
// When requested, print a json output to stdout, rather than using standard ng-dev logging.
|
||||
if (jsonOutput) {
|
||||
process.stdout.write(JSON.stringify(targets));
|
||||
return;
|
||||
}
|
||||
info.group(`PR #${prNumber} will merge into:`);
|
||||
targets.forEach(target => info(`- ${target}`));
|
||||
info.groupEnd();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/** Builds the command. */
|
||||
function builder$5(yargs) {
|
||||
return yargs
|
||||
.positional('pr', {
|
||||
description: 'The pull request number',
|
||||
type: 'number',
|
||||
demandOption: true,
|
||||
})
|
||||
.option('json', {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Print response as json',
|
||||
});
|
||||
}
|
||||
/** Handles the command. */
|
||||
function handler$5({ pr, json }) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function* () {
|
||||
yield checkTargetBranchesForPr(pr, json);
|
||||
});
|
||||
}
|
||||
/** yargs command module describing the command. */
|
||||
const CheckTargetBranchesModule = {
|
||||
handler: handler$5,
|
||||
builder: builder$5,
|
||||
command: 'check-target-branches <pr>',
|
||||
describe: 'Check a PR to determine what branches it is currently targeting',
|
||||
};
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
@ -2799,11 +3028,11 @@ function checkOutPullRequestLocally(prNumber, githubToken, opts = {}) {
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/** Builds the checkout pull request command. */
|
||||
function builder$5(yargs) {
|
||||
function builder$6(yargs) {
|
||||
return addGithubTokenOption(yargs).positional('prNumber', { type: 'number', demandOption: true });
|
||||
}
|
||||
/** Handles the checkout pull request command. */
|
||||
function handler$5({ prNumber, githubToken }) {
|
||||
function handler$6({ prNumber, githubToken }) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function* () {
|
||||
const prCheckoutOptions = { allowIfMaintainerCannotModify: true, branchName: `pr-${prNumber}` };
|
||||
yield checkOutPullRequestLocally(prNumber, githubToken, prCheckoutOptions);
|
||||
@ -2811,8 +3040,8 @@ function handler$5({ prNumber, githubToken }) {
|
||||
}
|
||||
/** yargs command module for checking out a PR */
|
||||
const CheckoutCommandModule = {
|
||||
handler: handler$5,
|
||||
builder: builder$5,
|
||||
handler: handler$6,
|
||||
builder: builder$6,
|
||||
command: 'checkout <pr-number>',
|
||||
describe: 'Checkout a PR from the upstream repo',
|
||||
};
|
||||
@ -2985,59 +3214,6 @@ function getThirtyDaysAgoDate() {
|
||||
return date.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/** Loads and validates the merge configuration. */
|
||||
function loadAndValidateConfig(config, api) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function () {
|
||||
var mergeConfig, errors;
|
||||
return tslib.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (config.merge === undefined) {
|
||||
return [2 /*return*/, { errors: ['No merge configuration found. Set the `merge` configuration.'] }];
|
||||
}
|
||||
if (typeof config.merge !== 'function') {
|
||||
return [2 /*return*/, { errors: ['Expected merge configuration to be defined lazily through a function.'] }];
|
||||
}
|
||||
return [4 /*yield*/, config.merge(api)];
|
||||
case 1:
|
||||
mergeConfig = _a.sent();
|
||||
errors = validateMergeConfig(mergeConfig);
|
||||
if (errors.length) {
|
||||
return [2 /*return*/, { errors: errors }];
|
||||
}
|
||||
return [2 /*return*/, { config: mergeConfig }];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
/** Validates the specified configuration. Returns a list of failure messages. */
|
||||
function validateMergeConfig(config) {
|
||||
var errors = [];
|
||||
if (!config.labels) {
|
||||
errors.push('No label configuration.');
|
||||
}
|
||||
else if (!Array.isArray(config.labels)) {
|
||||
errors.push('Label configuration needs to be an array.');
|
||||
}
|
||||
if (!config.claSignedLabel) {
|
||||
errors.push('No CLA signed label configured.');
|
||||
}
|
||||
if (!config.mergeReadyLabel) {
|
||||
errors.push('No merge ready label configured.');
|
||||
}
|
||||
if (config.githubApiMerge === undefined) {
|
||||
errors.push('No explicit choice of merge strategy. Please set `githubApiMerge`.');
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
@ -3121,101 +3297,6 @@ function getTargettedBranchesConfirmationPromptMessage(pullRequest) {
|
||||
return "Pull request #" + pullRequest.prNumber + " will merge into:\n" + targetBranchListAsString + "\nDo you want to proceed merging?";
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/** Checks whether the specified value matches the given pattern. */
|
||||
function matchesPattern(value, pattern) {
|
||||
return typeof pattern === 'string' ? value === pattern : pattern.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/**
|
||||
* Unique error that can be thrown in the merge configuration if an
|
||||
* invalid branch is targeted.
|
||||
*/
|
||||
var InvalidTargetBranchError = /** @class */ (function () {
|
||||
function InvalidTargetBranchError(failureMessage) {
|
||||
this.failureMessage = failureMessage;
|
||||
}
|
||||
return InvalidTargetBranchError;
|
||||
}());
|
||||
/**
|
||||
* Unique error that can be thrown in the merge configuration if an
|
||||
* invalid label has been applied to a pull request.
|
||||
*/
|
||||
var InvalidTargetLabelError = /** @class */ (function () {
|
||||
function InvalidTargetLabelError(failureMessage) {
|
||||
this.failureMessage = failureMessage;
|
||||
}
|
||||
return InvalidTargetLabelError;
|
||||
}());
|
||||
/** Gets the target label from the specified pull request labels. */
|
||||
function getTargetLabelFromPullRequest(config, labels) {
|
||||
var e_1, _a;
|
||||
var _loop_1 = function (label) {
|
||||
var match = config.labels.find(function (_a) {
|
||||
var pattern = _a.pattern;
|
||||
return matchesPattern(label, pattern);
|
||||
});
|
||||
if (match !== undefined) {
|
||||
return { value: match };
|
||||
}
|
||||
};
|
||||
try {
|
||||
for (var labels_1 = tslib.__values(labels), labels_1_1 = labels_1.next(); !labels_1_1.done; labels_1_1 = labels_1.next()) {
|
||||
var label = labels_1_1.value;
|
||||
var state_1 = _loop_1(label);
|
||||
if (typeof state_1 === "object")
|
||||
return state_1.value;
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (labels_1_1 && !labels_1_1.done && (_a = labels_1.return)) _a.call(labels_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Gets the branches from the specified target label.
|
||||
*
|
||||
* @throws {InvalidTargetLabelError} Invalid label has been applied to pull request.
|
||||
* @throws {InvalidTargetBranchError} Invalid Github target branch has been selected.
|
||||
*/
|
||||
function getBranchesFromTargetLabel(label, githubTargetBranch) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function () {
|
||||
var _a;
|
||||
return tslib.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (!(typeof label.branches === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, label.branches(githubTargetBranch)];
|
||||
case 1:
|
||||
_a = _b.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 2: return [4 /*yield*/, label.branches];
|
||||
case 3:
|
||||
_a = _b.sent();
|
||||
_b.label = 4;
|
||||
case 4: return [2 /*return*/, _a];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
@ -4251,7 +4332,8 @@ function buildPrParser(localYargs) {
|
||||
.command('merge <pr-number>', 'Merge pull requests', buildMergeCommand, handleMergeCommand)
|
||||
.command('discover-new-conflicts <pr-number>', 'Check if a pending PR causes new conflicts for other pending PRs', buildDiscoverNewConflictsCommand, handleDiscoverNewConflictsCommand)
|
||||
.command('rebase <pr-number>', 'Rebase a pending PR and push the rebased commits back to Github', buildRebaseCommand, handleRebaseCommand)
|
||||
.command(CheckoutCommandModule);
|
||||
.command(CheckoutCommandModule)
|
||||
.command(CheckTargetBranchesModule);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4718,7 +4800,7 @@ function buildReleaseOutput() {
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/** Yargs command builder for configuring the `ng-dev release build` command. */
|
||||
function builder$6(argv) {
|
||||
function builder$7(argv) {
|
||||
return argv.option('json', {
|
||||
type: 'boolean',
|
||||
description: 'Whether the built packages should be printed to stdout as JSON.',
|
||||
@ -4726,7 +4808,7 @@ function builder$6(argv) {
|
||||
});
|
||||
}
|
||||
/** Yargs command handler for building a release. */
|
||||
function handler$6(args) {
|
||||
function handler$7(args) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function* () {
|
||||
const { npmPackages } = getReleaseConfig();
|
||||
let builtPackages = yield buildReleaseOutput();
|
||||
@ -4761,8 +4843,8 @@ function handler$6(args) {
|
||||
}
|
||||
/** CLI command module for building release output. */
|
||||
const ReleaseBuildCommandModule = {
|
||||
builder: builder$6,
|
||||
handler: handler$6,
|
||||
builder: builder$7,
|
||||
handler: handler$7,
|
||||
command: 'build',
|
||||
describe: 'Builds the release output for the current branch.',
|
||||
};
|
||||
@ -6217,11 +6299,11 @@ class ReleaseTool {
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/** Yargs command builder for configuring the `ng-dev release publish` command. */
|
||||
function builder$7(argv) {
|
||||
function builder$8(argv) {
|
||||
return addGithubTokenOption(argv);
|
||||
}
|
||||
/** Yargs command handler for staging a release. */
|
||||
function handler$7(args) {
|
||||
function handler$8(args) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function* () {
|
||||
const config = getConfig();
|
||||
const releaseConfig = getReleaseConfig(config);
|
||||
@ -6244,8 +6326,8 @@ function handler$7(args) {
|
||||
}
|
||||
/** CLI command module for publishing a release. */
|
||||
const ReleasePublishCommandModule = {
|
||||
builder: builder$7,
|
||||
handler: handler$7,
|
||||
builder: builder$8,
|
||||
handler: handler$8,
|
||||
command: 'publish',
|
||||
describe: 'Publish new releases and configure version branches.',
|
||||
};
|
||||
@ -6257,7 +6339,7 @@ const ReleasePublishCommandModule = {
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
function builder$8(args) {
|
||||
function builder$9(args) {
|
||||
return args
|
||||
.positional('tagName', {
|
||||
type: 'string',
|
||||
@ -6271,7 +6353,7 @@ function builder$8(args) {
|
||||
});
|
||||
}
|
||||
/** Yargs command handler for building a release. */
|
||||
function handler$8(args) {
|
||||
function handler$9(args) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function* () {
|
||||
const { targetVersion: rawVersion, tagName } = args;
|
||||
const { npmPackages, publishRegistry } = getReleaseConfig();
|
||||
@ -6303,8 +6385,8 @@ function handler$8(args) {
|
||||
}
|
||||
/** CLI command module for setting an NPM dist tag. */
|
||||
const ReleaseSetDistTagCommand = {
|
||||
builder: builder$8,
|
||||
handler: handler$8,
|
||||
builder: builder$9,
|
||||
handler: handler$9,
|
||||
command: 'set-dist-tag <tag-name> <target-version>',
|
||||
describe: 'Sets a given NPM dist tag for all release packages.',
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ ts_library(
|
||||
module_name = "@angular/dev-infra-private/pr",
|
||||
visibility = ["//dev-infra:__subpackages__"],
|
||||
deps = [
|
||||
"//dev-infra/pr/check-target-branches",
|
||||
"//dev-infra/pr/checkout",
|
||||
"//dev-infra/pr/discover-new-conflicts",
|
||||
"//dev-infra/pr/merge",
|
||||
|
13
dev-infra/pr/check-target-branches/BUILD.bazel
Normal file
13
dev-infra/pr/check-target-branches/BUILD.bazel
Normal file
@ -0,0 +1,13 @@
|
||||
load("@npm//@bazel/typescript:index.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "check-target-branches",
|
||||
srcs = glob(["*.ts"]),
|
||||
module_name = "@angular/dev-infra-private/pr/check-target-branches",
|
||||
visibility = ["//dev-infra:__subpackages__"],
|
||||
deps = [
|
||||
"//dev-infra/pr/merge",
|
||||
"//dev-infra/utils",
|
||||
"@npm//@types/yargs",
|
||||
],
|
||||
)
|
52
dev-infra/pr/check-target-branches/check-target-branches.ts
Normal file
52
dev-infra/pr/check-target-branches/check-target-branches.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {getConfig} from '../../utils/config';
|
||||
import {error, info, red} from '../../utils/console';
|
||||
import {GitClient} from '../../utils/git/index';
|
||||
import {loadAndValidateConfig} from '../merge/config';
|
||||
import {getBranchesFromTargetLabel, getTargetLabelFromPullRequest} from '../merge/target-label';
|
||||
|
||||
export async function checkTargetBranchesForPr(prNumber: number, jsonOutput = false) {
|
||||
/** The ng-dev configuration. */
|
||||
const config = getConfig();
|
||||
/** Repo owner and name for the github repository. */
|
||||
const {owner, name: repo} = config.github;
|
||||
/** The git client to get a Github API service instance. */
|
||||
const git = new GitClient(undefined, config);
|
||||
/** The validated merge config. */
|
||||
const {config: mergeConfig, errors} = await loadAndValidateConfig(config, git.github);
|
||||
if (errors !== undefined) {
|
||||
throw Error(`Invalid configuration found: ${errors}`);
|
||||
}
|
||||
/** The current state of the pull request from Github. */
|
||||
const prData = (await git.github.pulls.get({owner, repo, pull_number: prNumber})).data;
|
||||
/** The list of labels on the PR as strings. */
|
||||
const labels = prData.labels.map(l => l.name);
|
||||
/** The branch targetted via the Github UI. */
|
||||
const githubTargetBranch = prData.base.ref;
|
||||
/** The active label which is being used for targetting the PR. */
|
||||
const targetLabel = getTargetLabelFromPullRequest(mergeConfig!, labels);
|
||||
if (targetLabel === null) {
|
||||
error(red(`No target label was found on pr #${prNumber}`));
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
/** The target branches based on the target label and branch targetted in the Github UI. */
|
||||
const targets = await getBranchesFromTargetLabel(targetLabel, githubTargetBranch);
|
||||
|
||||
// When requested, print a json output to stdout, rather than using standard ng-dev logging.
|
||||
if (jsonOutput) {
|
||||
process.stdout.write(JSON.stringify(targets));
|
||||
return;
|
||||
}
|
||||
|
||||
info.group(`PR #${prNumber} will merge into:`);
|
||||
targets.forEach(target => info(`- ${target}`));
|
||||
info.groupEnd();
|
||||
}
|
44
dev-infra/pr/check-target-branches/cli.ts
Normal file
44
dev-infra/pr/check-target-branches/cli.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Arguments, Argv, CommandModule} from 'yargs';
|
||||
|
||||
import {checkTargetBranchesForPr} from './check-target-branches';
|
||||
|
||||
export interface CheckTargetBranchesOptions {
|
||||
pr: number;
|
||||
json: boolean;
|
||||
}
|
||||
|
||||
/** Builds the command. */
|
||||
function builder(yargs: Argv) {
|
||||
return yargs
|
||||
.positional('pr', {
|
||||
description: 'The pull request number',
|
||||
type: 'number',
|
||||
demandOption: true,
|
||||
})
|
||||
.option('json', {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Print response as json',
|
||||
});
|
||||
}
|
||||
|
||||
/** Handles the command. */
|
||||
async function handler({pr, json}: Arguments<CheckTargetBranchesOptions>) {
|
||||
await checkTargetBranchesForPr(pr, json);
|
||||
}
|
||||
|
||||
/** yargs command module describing the command. */
|
||||
export const CheckTargetBranchesModule: CommandModule<{}, CheckTargetBranchesOptions> = {
|
||||
handler,
|
||||
builder,
|
||||
command: 'check-target-branches <pr>',
|
||||
describe: 'Check a PR to determine what branches it is currently targeting',
|
||||
};
|
@ -8,6 +8,7 @@
|
||||
|
||||
import * as yargs from 'yargs';
|
||||
|
||||
import {CheckTargetBranchesModule} from './check-target-branches/cli';
|
||||
import {CheckoutCommandModule} from './checkout/cli';
|
||||
import {buildDiscoverNewConflictsCommand, handleDiscoverNewConflictsCommand} from './discover-new-conflicts/cli';
|
||||
import {buildMergeCommand, handleMergeCommand} from './merge/cli';
|
||||
@ -26,5 +27,6 @@ export function buildPrParser(localYargs: yargs.Argv) {
|
||||
.command(
|
||||
'rebase <pr-number>', 'Rebase a pending PR and push the rebased commits back to Github',
|
||||
buildRebaseCommand, handleRebaseCommand)
|
||||
.command(CheckoutCommandModule);
|
||||
.command(CheckoutCommandModule)
|
||||
.command(CheckTargetBranchesModule);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user