Paul Gschwendtner 576e329f33 feat(dev-infra): provide github API instance to lazy merge configuration (#38223)
The merge script currently accepts a configuration function that will
be invoked _only_ when the `ng-dev merge` command is executed. This
has been done that way because the merge tooling usually relies on
external requests to Git or NPM for constructing the branch configurations.

We do not want to perform these slow external queries on any `ng-dev` command
though, so this became a lazily invoked function.

This commit adds support for these configuration functions to run
asynchronously (by returning a Promise that will be awaited), so that
requests could also be made to the Github API. This is benefical as it
could avoid dependence on the local Git state and the HTTP requests
are more powerful/faster.

Additionally, in order to be able to perform Github API requests
with an authenticated instance, the merge tool will pass through
a `GithubClient` instance that uses the specified `--github-token`
(or from the environment). This ensures that all API requests use
the same `GithubClient` instance and can be authenticated (mitigating
potential rate limits).

PR Close #38223
2020-08-05 10:53:17 -07:00

123 lines
4.7 KiB
TypeScript

/**
* @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 {GitClientConfig, NgDevConfig} from '../../utils/config';
import {GithubClient} from '../../utils/git/github';
import {GithubApiMergeStrategyConfig} from './strategies/api-merge';
/** Describes possible values that can be returned for `branches` of a target label. */
export type TargetLabelBranchResult = string[]|Promise<string[]>;
/**
* Possible merge methods supported by the Github API.
* https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-button.
*/
export type GithubApiMergeMethod = 'merge'|'squash'|'rebase';
/**
* Target labels represent Github pull requests labels. These labels instruct the merge
* script into which branches a given pull request should be merged to.
*/
export interface TargetLabel {
/** Pattern that matches the given target label. */
pattern: RegExp|string;
/**
* List of branches a pull request with this target label should be merged into.
* Can also be wrapped in a function that accepts the target branch specified in the
* Github Web UI. This is useful for supporting labels like `target: development-branch`.
*/
branches: TargetLabelBranchResult|((githubTargetBranch: string) => TargetLabelBranchResult);
}
/**
* Configuration for the merge script with all remote options specified. The
* default `MergeConfig` has does not require any of these options as defaults
* are provided by the common dev-infra github configuration.
*/
export type MergeConfigWithRemote = MergeConfig&{remote: GitClientConfig};
/** Configuration for the merge script. */
export interface MergeConfig {
/**
* Configuration for the upstream remote. All of these options are optional as
* defaults are provided by the common dev-infra github configuration.
*/
remote?: GitClientConfig;
/** List of target labels. */
labels: TargetLabel[];
/** Required base commits for given branches. */
requiredBaseCommits?: {[branchName: string]: string};
/** Pattern that matches labels which imply a signed CLA. */
claSignedLabel: string|RegExp;
/** Pattern that matches labels which imply a merge ready pull request. */
mergeReadyLabel: string|RegExp;
/** Label that is applied when special attention from the caretaker is required. */
caretakerNoteLabel?: string|RegExp;
/** Label which can be applied to fixup commit messages in the merge script. */
commitMessageFixupLabel: string|RegExp;
/**
* Whether pull requests should be merged using the Github API. This can be enabled
* if projects want to have their pull requests show up as `Merged` in the Github UI.
* The downside is that fixup or squash commits no longer work as the Github API does
* not support this.
*/
githubApiMerge: false|GithubApiMergeStrategyConfig;
}
/**
* Configuration of the merge script in the dev-infra configuration. Note that the
* merge configuration is retrieved lazily as usually these configurations rely
* on branch name computations. We don't want to run these immediately whenever
* the dev-infra configuration is loaded as that could slow-down other commands.
*/
export type DevInfraMergeConfig =
NgDevConfig<{'merge': (api: GithubClient) => MergeConfig | Promise<MergeConfig>}>;
/** Loads and validates the merge configuration. */
export async function loadAndValidateConfig(
config: Partial<DevInfraMergeConfig>,
api: GithubClient): Promise<{config?: MergeConfig, errors?: string[]}> {
if (config.merge === undefined) {
return {errors: ['No merge configuration found. Set the `merge` configuration.']};
}
if (typeof config.merge !== 'function') {
return {errors: ['Expected merge configuration to be defined lazily through a function.']};
}
const mergeConfig = await config.merge(api);
const errors = validateMergeConfig(mergeConfig);
if (errors.length) {
return {errors};
}
return {config: mergeConfig};
}
/** Validates the specified configuration. Returns a list of failure messages. */
function validateMergeConfig(config: Partial<MergeConfig>): string[] {
const errors: string[] = [];
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;
}