Skip to content

Commit 2c50e1a

Browse files
committed
feat(ng-dev): create a configuration check subcommand
Create a subcommand which validates that the configuration provided is portable in its initial load and passes expected assertions for specific configurations.
1 parent c86c5f3 commit 2c50e1a

File tree

8 files changed

+162
-0
lines changed

8 files changed

+162
-0
lines changed

ng-dev/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ ts_project(
5454
"//ng-dev/auth",
5555
"//ng-dev/caretaker",
5656
"//ng-dev/commit-message",
57+
"//ng-dev/config",
5758
"//ng-dev/format",
5859
"//ng-dev/misc",
5960
"//ng-dev/ngbot",

ng-dev/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {captureLogOutputForCommand} from './utils/logging.js';
2222
import {ngDevVersionMiddleware} from './utils/version-check.js';
2323
import {buildAuthParser} from './auth/cli.js';
2424
import {buildPerfParser} from './perf/cli.js';
25+
import {buildConfigParser} from './config/cli.js';
2526
import {buildAiParser} from './ai/cli.js';
2627
import {Argv} from 'yargs';
2728

@@ -44,6 +45,7 @@ runParserWithCompletedFunctions((yargs: Argv) => {
4445
.command('ngbot <command>', false, buildNgbotParser)
4546
.command('perf <command>', '', buildPerfParser)
4647
.command('ai <command>', '', buildAiParser)
48+
.command('config <command>', false, buildConfigParser)
4749
.wrap(120)
4850
.strict();
4951
});

ng-dev/config/BUILD.bazel

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
load("//tools:defaults.bzl", "ts_project")
2+
3+
ts_project(
4+
name = "config",
5+
srcs = [
6+
"cli.ts",
7+
],
8+
visibility = ["//ng-dev:__subpackages__"],
9+
deps = [
10+
"//ng-dev:node_modules/@types/yargs",
11+
"//ng-dev/config/validate",
12+
],
13+
)

ng-dev/config/cli.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC
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+
import {Argv} from 'yargs';
9+
import {ValidateModule} from './validate/cli.js';
10+
11+
/** Build the parser for the release commands. */
12+
export function buildConfigParser(localYargs: Argv) {
13+
return localYargs.help().strict().demandCommand().command(ValidateModule);
14+
}

ng-dev/config/validate/BUILD.bazel

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("//tools:defaults.bzl", "ts_project")
2+
3+
ts_project(
4+
name = "validate",
5+
srcs = glob(["*.ts"]),
6+
visibility = ["//ng-dev/config:__subpackages__"],
7+
deps = [
8+
"//ng-dev:node_modules/@types/yargs",
9+
"//ng-dev/commit-message",
10+
"//ng-dev/format",
11+
"//ng-dev/pr/config",
12+
"//ng-dev/release/config",
13+
"//ng-dev/utils",
14+
],
15+
)

ng-dev/config/validate/cli.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC
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+
import {Argv, CommandModule} from 'yargs';
9+
import {green, Log, red} from '../../utils/logging';
10+
import {checkPortability} from './portability';
11+
import {checkValidity} from './validity';
12+
import {ConfigValidationError} from '../../utils/config';
13+
14+
export interface Options {}
15+
16+
/** Builds the command. */
17+
async function builder(yargs: Argv) {
18+
return yargs;
19+
}
20+
21+
/** Handles the command. */
22+
async function handler() {
23+
try {
24+
await checkPortability();
25+
await checkValidity();
26+
Log.info(`${green('✓')} ng-dev configuration validation passed`);
27+
} catch (error) {
28+
if (error instanceof ConfigValidationError) {
29+
error.errors.forEach((e) => Log.info(e));
30+
} else {
31+
Log.info(error);
32+
}
33+
Log.info(`${red('✘')} ng-dev configuration validation failed, see above for more details`);
34+
}
35+
}
36+
37+
/** yargs command module for logging into the ng-dev service. */
38+
export const ValidateModule: CommandModule<{}, Options> = {
39+
handler,
40+
builder,
41+
command: 'validate',
42+
describe: 'Validate that the configuration provided in .ng-dev/ is valid and portable',
43+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC
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+
import {ChildProcess} from '../../utils/child-process';
9+
import {join} from 'path';
10+
import {tmpdir} from 'os';
11+
import {cp, mkdtemp, rm} from 'fs/promises';
12+
import {determineRepoBaseDirFromCwd} from '../../utils/repo-directory';
13+
import {Log} from '../../utils/logging';
14+
15+
export async function checkPortability() {
16+
Log.debug('Copying ng-dev configuration to isolated temp directory');
17+
const tmpConfigDir = await mkdtemp(join(tmpdir(), 'ng-dev-config-check-'));
18+
const repoBaseDir = determineRepoBaseDirFromCwd();
19+
try {
20+
await cp(join(repoBaseDir, '.ng-dev'), tmpConfigDir, {recursive: true});
21+
Log.debug('Validating configuration loads in isolation');
22+
const baseConfigFile = join(tmpConfigDir, 'config.mjs');
23+
const {status, stderr} = await ChildProcess.exec(`node ${baseConfigFile}`, {
24+
cwd: tmpConfigDir,
25+
mode: 'silent',
26+
});
27+
if (status !== 0) {
28+
throw Error(stderr);
29+
}
30+
} catch (err) {
31+
throw err;
32+
} finally {
33+
await rm(tmpConfigDir, {recursive: true, maxRetries: 3});
34+
}
35+
}

ng-dev/config/validate/validity.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC
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+
import {assertValidCommitMessageConfig} from '../../commit-message/config';
9+
import {assertValidFormatConfig} from '../../format/config';
10+
import {assertValidPullRequestConfig} from '../../pr/config';
11+
import {assertValidReleaseConfig} from '../../release/config';
12+
import {
13+
assertValidCaretakerConfig,
14+
assertValidGithubConfig,
15+
getConfig,
16+
NgDevConfig,
17+
} from '../../utils/config';
18+
19+
export async function checkValidity() {
20+
const config = (await getConfig()) as NgDevConfig<{[key: string]: any}>;
21+
if (config['github']) {
22+
assertValidGithubConfig(config);
23+
}
24+
if (config['caretaker']) {
25+
assertValidCaretakerConfig(config);
26+
}
27+
if (config['commitMessage']) {
28+
assertValidCommitMessageConfig(config);
29+
}
30+
if (config['pullRequest']) {
31+
assertValidPullRequestConfig(config);
32+
}
33+
if (config['format']) {
34+
assertValidFormatConfig(config);
35+
}
36+
if (config['release']) {
37+
assertValidReleaseConfig(config);
38+
}
39+
}

0 commit comments

Comments
 (0)