豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 4fdbd00

Browse files
committed
feat(ng-dev/release): support for custom release pre-checks to be provided
This commit introduces a new config option that can be defined to configure custom release pre-checks that will run before releasing, and after the release output has been built. This allows for pre-release checks and post-build checks. Useful for e.g. validating release output, checking the migration collection, checking the peer dependency ranges etc.
1 parent 71f4f78 commit 4fdbd00

File tree

16 files changed

+411
-17
lines changed

16 files changed

+411
-17
lines changed

ng-dev/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ ts_library(
3030
"//ng-dev/pullapprove",
3131
"//ng-dev/release",
3232
"//ng-dev/release/config",
33+
"//ng-dev/release/precheck",
3334
"//ng-dev/release/publish",
3435
"//ng-dev/release/versioning",
3536
"//ng-dev/ts-circular-dependencies",

ng-dev/cli.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,16 @@
99
import * as yargs from 'yargs';
1010

1111
import {buildCaretakerParser} from './caretaker/cli';
12+
import {buildCiParser} from './ci/cli';
1213
import {buildCommitMessageParser} from './commit-message/cli';
1314
import {buildFormatParser} from './format/cli';
15+
import {buildMiscParser} from './misc/cli';
1416
import {buildNgbotParser} from './ngbot/cli';
1517
import {buildPrParser} from './pr/cli';
1618
import {buildPullapproveParser} from './pullapprove/cli';
1719
import {buildReleaseParser} from './release/cli';
1820
import {tsCircularDependenciesBuilder} from './ts-circular-dependencies/index';
1921
import {captureLogOutputForCommand} from './utils/console';
20-
import {buildMiscParser} from './misc/cli';
21-
import {buildCiParser} from './ci/cli';
22-
import {ReleaseAction} from './index';
23-
24-
// Expose the `ReleaseAction` constructor globally so that the COMP repo
25-
// can hook into it and setup staging/post-building release checks.
26-
// TODO: Remove once https://github.com/angular/dev-infra/issues/402 is resolved.
27-
// or if we have code-splitting w/ ESM enabled for the ng-dev bundling.
28-
(global as any).ReleaseAction = ReleaseAction;
2922

3023
yargs
3124
.scriptName('ng-dev')

ng-dev/index.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export * from './release/config';
1818
// respect to Angular's branching/versioning and release process.
1919
export * from './release/versioning';
2020

21+
// Exposes the release precheck command utilities. These should be available
22+
// as they are needed for authoring pre-release custom checks.
23+
export {ReleasePrecheckError} from './release/precheck';
24+
2125
// Exposes console utils that can be used by consumers to e.g. add messages
2226
// to the dev-infra log which is stored on failures.
2327
export * from './utils/console';
@@ -27,11 +31,3 @@ export * from './utils/console';
2731
export * from './utils/git/authenticated-git-client';
2832
export * from './utils/git/git-client';
2933
export * from './utils/git/github';
30-
31-
// Additional exports for adding custom release pre-staging, post-build checks.
32-
// TODO: Remove once https://github.com/angular/dev-infra/issues/402 is resolved.
33-
export {ReleaseAction} from './release/publish/actions';
34-
export {
35-
FatalReleaseActionError,
36-
UserAbortedReleaseActionError,
37-
} from './release/publish/actions-error';

ng-dev/release/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ ts_library(
1010
"//ng-dev/release/build",
1111
"//ng-dev/release/info",
1212
"//ng-dev/release/notes",
13+
"//ng-dev/release/precheck",
1314
"//ng-dev/release/publish",
1415
"//ng-dev/release/set-dist-tag",
1516
"//ng-dev/release/stamping",

ng-dev/release/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as yargs from 'yargs';
1010
import {ReleaseBuildCommandModule} from './build/cli';
1111
import {ReleaseInfoCommandModule} from './info/cli';
1212
import {ReleaseNotesCommandModule} from './notes/cli';
13+
import {ReleasePrecheckCommandModule} from './precheck/cli';
1314
import {ReleasePublishCommandModule} from './publish/cli';
1415
import {ReleaseSetDistTagCommand} from './set-dist-tag/cli';
1516
import {BuildEnvStampCommand} from './stamping/cli';
@@ -23,6 +24,7 @@ export function buildReleaseParser(localYargs: yargs.Argv) {
2324
.command(ReleasePublishCommandModule)
2425
.command(ReleaseBuildCommandModule)
2526
.command(ReleaseInfoCommandModule)
27+
.command(ReleasePrecheckCommandModule)
2628
.command(ReleaseSetDistTagCommand)
2729
.command(BuildEnvStampCommand)
2830
.command(ReleaseNotesCommandModule);

ng-dev/release/config/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ export interface ReleaseConfig {
5252
releasePrLabels?: string[];
5353
/** Configuration for creating release notes during publishing. */
5454
releaseNotes?: ReleaseNotesConfig;
55+
/**
56+
* Optional function that can be provided to run checks for a version before
57+
* it can be released.
58+
*
59+
* If provided, the release can occur when the promise resolves. Upon rejection,
60+
* the release will abort the release and print the `ReleasePrecheckError` error.
61+
*/
62+
prereleaseCheck?: (
63+
newVersion: SemVer,
64+
builtPackagesWithInfo: BuiltPackageWithInfo[],
65+
) => Promise<void>;
5566
}
5667

5768
/**
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
2+
3+
ts_library(
4+
name = "precheck",
5+
srcs = glob(
6+
[
7+
"**/*.ts",
8+
],
9+
exclude = ["*.spec.ts"],
10+
),
11+
visibility = ["//ng-dev:__subpackages__"],
12+
deps = [
13+
"//ng-dev/release/config",
14+
"//ng-dev/utils",
15+
"@npm//@types/node",
16+
"@npm//@types/semver",
17+
"@npm//@types/yargs",
18+
"@npm//semver",
19+
],
20+
)
21+
22+
ts_library(
23+
name = "test_lib",
24+
testonly = True,
25+
srcs = glob([
26+
"*.spec.ts",
27+
]),
28+
deps = [
29+
":precheck",
30+
"//ng-dev/release/config",
31+
"//ng-dev/utils",
32+
"@npm//@types/semver",
33+
"@npm//semver",
34+
],
35+
)
36+
37+
jasmine_node_test(
38+
name = "test",
39+
specs = [":test_lib"],
40+
)

ng-dev/release/precheck/cli.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
// ---- **IMPORTANT** ----
10+
// This command is part of our external commands invoked by the release publish
11+
// command. Before making changes, keep in mind that more recent `ng-dev` versions
12+
// can still invoke this command.
13+
// ------------------------
14+
15+
import * as semver from 'semver';
16+
import {CommandModule} from 'yargs';
17+
18+
import {assertPassingReleasePrechecks} from './index';
19+
import {getConfig} from '../../utils/config';
20+
import {readBufferFromStdinUntilClosed} from '../../utils/read-stdin-until-closed';
21+
import {assertValidReleaseConfig, BuiltPackageWithInfo} from '../config/index';
22+
import {error, red} from '../../utils/console';
23+
24+
/**
25+
* Type describing the JSON stdin input of this command. The release tool will
26+
* deliver this information through `stdin` because command line arguments are
27+
* less reliable and have max-length limits.
28+
*
29+
* @important When changing this, make sure the release action
30+
* invocation is updated as well.
31+
*/
32+
export interface ReleasePrecheckJsonStdin {
33+
/** Package output that has been built and can be checked. */
34+
builtPackagesWithInfo: BuiltPackageWithInfo[];
35+
/** New version that is intended to be released. */
36+
newVersion: string;
37+
}
38+
39+
async function handler() {
40+
// Note: Stdin input is buffered until we start reading from it. This allows us to
41+
// asynchronously start reading the `stdin` input. With the default `readableFlowing`
42+
// value of `null`, data is buffered. See: https://nodejs.org/api/stream.html#three-states.
43+
const stdin = await readBufferFromStdinUntilClosed();
44+
const config = getConfig();
45+
assertValidReleaseConfig(config);
46+
47+
// Parse the JSON metadata read from `stdin`.
48+
const {builtPackagesWithInfo, newVersion: newVersionRaw} = JSON.parse(
49+
stdin.toString('utf8'),
50+
) as ReleasePrecheckJsonStdin;
51+
52+
if (!Array.isArray(builtPackagesWithInfo)) {
53+
error(red(` ✘ Release pre-checks failed. Invalid list of built packages was provided.`));
54+
process.exitCode = 1;
55+
return;
56+
}
57+
58+
const newVersion = semver.parse(newVersionRaw);
59+
if (newVersion === null) {
60+
error(red(` ✘ Release pre-checks failed. Invalid new version was provided.`));
61+
process.exitCode = 1;
62+
return;
63+
}
64+
65+
if (!(await assertPassingReleasePrechecks(config.release, newVersion, builtPackagesWithInfo))) {
66+
process.exitCode = 1;
67+
}
68+
}
69+
70+
/** CLI command module for running checks before releasing. */
71+
export const ReleasePrecheckCommandModule: CommandModule<{}, {}> = {
72+
handler,
73+
command: 'precheck',
74+
describe: false,
75+
};

ng-dev/release/precheck/index.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {debug} from 'console';
10+
import {SemVer} from 'semver';
11+
import {error, green, info, red, warn, yellow} from '../../utils/console';
12+
import {BuiltPackageWithInfo, ReleaseConfig} from '../config';
13+
14+
/**
15+
* Error class that can be used to report precheck failures. Messaging with
16+
* respect to the pre-check error is required to be handled manually.
17+
*/
18+
export class ReleasePrecheckError extends Error {}
19+
20+
/**
21+
* Runs the release prechecks and checks whether they are passing for the
22+
* specified release config, intended new version and built release packages.
23+
*
24+
* @returns A boolean that indicates whether the prechecks are passing or not.
25+
*/
26+
export async function assertPassingReleasePrechecks(
27+
config: ReleaseConfig,
28+
newVersion: SemVer,
29+
builtPackagesWithInfo: BuiltPackageWithInfo[],
30+
): Promise<boolean> {
31+
if (config.prereleaseCheck === undefined) {
32+
warn(yellow(' ⚠ Skipping release pre-checks. No checks configured.'));
33+
return true;
34+
}
35+
36+
// The user-defined release precheck function is supposed to throw errors upon unmet
37+
// checks. We catch this here and print a better message and determine the status.
38+
try {
39+
await config.prereleaseCheck(newVersion, builtPackagesWithInfo);
40+
info(green(' ✓ Release pre-checks passing.'));
41+
return true;
42+
} catch (e) {
43+
if (isReleasePrecheckError(e)) {
44+
// Note: Error messaging is expected to be handled manually.
45+
debug(e.message);
46+
error(red(` ✘ Release pre-checks failed. Please check the output above.`));
47+
} else {
48+
error(red(e), '\n');
49+
error(red(` ✘ Release pre-checks errored with unexpected runtime error.`));
50+
}
51+
52+
return false;
53+
}
54+
}
55+
56+
/**
57+
* Gets whether the given value is a `ReleasePrecheckError`. This helper exists
58+
* because `instanceof` checks would not work due to us not using code-splitting.
59+
*
60+
* TODO(devversion): Remove this when we expose the same code we use in the CLI,
61+
* using ESBuild code-splitting.
62+
*/
63+
function isReleasePrecheckError(value: unknown): value is ReleasePrecheckError {
64+
return (value as Partial<Object>).constructor?.name === 'ReleasePrecheckError';
65+
}

0 commit comments

Comments
 (0)