From 2b46d4b8b1dfa666b7c43bb4f7478befb7a9cf8e Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 2 Oct 2024 10:31:30 -0600 Subject: [PATCH] feat(ng-dev): create tool for automatically discovering and updating generated files Automatically query bazel and find all generated files and run the updates for them. --- ng-dev/misc/cli.ts | 4 +- ng-dev/misc/generated-files/cli.ts | 25 ++++++++++ .../generated-files/update-generated-files.ts | 48 +++++++++++++++++++ ng-dev/utils/bazel-bin.ts | 20 ++++++++ package.json | 2 +- tools/update-generated-files.ts | 42 ---------------- 6 files changed, 97 insertions(+), 44 deletions(-) create mode 100644 ng-dev/misc/generated-files/cli.ts create mode 100644 ng-dev/misc/generated-files/update-generated-files.ts create mode 100644 ng-dev/utils/bazel-bin.ts delete mode 100644 tools/update-generated-files.ts diff --git a/ng-dev/misc/cli.ts b/ng-dev/misc/cli.ts index d2cac6eee..3787f379d 100644 --- a/ng-dev/misc/cli.ts +++ b/ng-dev/misc/cli.ts @@ -10,6 +10,7 @@ import {Argv} from 'yargs'; import {BuildAndLinkCommandModule} from './build-and-link/cli.js'; import {UpdateYarnCommandModule} from './update-yarn/cli.js'; import {ValidateLicensesModule} from './validate-licenses/cli.js'; +import {GeneratedFilesModule} from './generated-files/cli.js'; /** Build the parser for the misc commands. */ export function buildMiscParser(localYargs: Argv) { @@ -18,5 +19,6 @@ export function buildMiscParser(localYargs: Argv) { .strict() .command(BuildAndLinkCommandModule) .command(UpdateYarnCommandModule) - .command(ValidateLicensesModule); + .command(ValidateLicensesModule) + .command(GeneratedFilesModule); } diff --git a/ng-dev/misc/generated-files/cli.ts b/ng-dev/misc/generated-files/cli.ts new file mode 100644 index 000000000..e462da979 --- /dev/null +++ b/ng-dev/misc/generated-files/cli.ts @@ -0,0 +1,25 @@ +/** + * @license + * Copyright Google LLC + * + * 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 {Argv, CommandModule} from 'yargs'; +import {updateGeneratedFileTargets} from './update-generated-files.js'; + +async function builder(argv: Argv) { + return argv; +} +async function handler() { + await updateGeneratedFileTargets(); +} + +/** CLI command module. */ +export const GeneratedFilesModule: CommandModule = { + builder, + handler, + command: 'update-generated-files', + describe: 'Automatically discover all bazel generated file targets and update them.', +}; diff --git a/ng-dev/misc/generated-files/update-generated-files.ts b/ng-dev/misc/generated-files/update-generated-files.ts new file mode 100644 index 000000000..810c73148 --- /dev/null +++ b/ng-dev/misc/generated-files/update-generated-files.ts @@ -0,0 +1,48 @@ +/** + * @license + * Copyright Google LLC + * + * 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 {ChildProcess} from '../../utils/child-process.js'; +import {Spinner} from '../../utils/spinner.js'; +import {getBazelBin} from '../../utils/bazel-bin.js'; +import {green, Log} from '../../utils/logging.js'; + +export async function updateGeneratedFileTargets(): Promise { + const spinner = new Spinner('Querying for all generated file targets'); + + // Query for all of the generated file targets + const result = await ChildProcess.spawn( + getBazelBin(), + ['query', `"kind(nodejs_binary, //...) intersect attr(name, '.update$', //...)"`], + {mode: 'silent'}, + ); + + if (result.status !== 0) { + spinner.complete(); + throw Error(`Unexpected error: ${result.stderr}`); + } + + const targets = result.stdout.trim().split(/\r?\n/); + spinner.update(`Found ${targets.length} generated file targets to update`); + + // Build all of the generated file targets in parallel. + await ChildProcess.spawn(getBazelBin(), ['build', targets.join(' ')], {mode: 'silent'}); + + // Individually run the generated file update targets. + for (let idx = 0; idx < targets.length; idx++) { + const target = targets[idx]; + spinner.update(`${idx + 1} of ${targets.length} updates completed`); + const updateResult = await ChildProcess.spawn(getBazelBin(), ['run', target], {mode: 'silent'}); + if (updateResult.status !== 0) { + spinner.complete(); + throw Error(`Unexpected error while updating: ${target}.`); + } + } + + spinner.complete(); + Log.info(` ${green('✔')} Updated all generated files (${targets.length} targets)`); +} diff --git a/ng-dev/utils/bazel-bin.ts b/ng-dev/utils/bazel-bin.ts new file mode 100644 index 000000000..cbde82744 --- /dev/null +++ b/ng-dev/utils/bazel-bin.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright Google LLC + * + * 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 {join} from 'path'; +import {determineRepoBaseDirFromCwd} from './repo-directory.js'; + +let BAZEL_BIN: undefined | string = undefined; + +export function getBazelBin() { + if (BAZEL_BIN === undefined) { + BAZEL_BIN = process.env.BAZEL || join(determineRepoBaseDirFromCwd(), 'node_modules/.bin/bazel'); + } + + return BAZEL_BIN; +} diff --git a/package.json b/package.json index ffc7c58ed..1422539cf 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "ng-dev": "bash ./tools/local-dev.sh", "lint": "yarn tslint -c tslint.json --project tsconfig.json", "build-env-stamp": "tsx --tsconfig tsconfig.json ./ng-dev/release/stamping/_private_main.ts build-env-stamp", - "update-generated-files": "tsx --tsconfig tsconfig.json ./tools/update-generated-files.ts", + "update-generated-files": "yarn ng-dev misc update-generated-files", "pack": "bazel run //:npm_package.pack --config=release" }, "exports": { diff --git a/tools/update-generated-files.ts b/tools/update-generated-files.ts deleted file mode 100644 index e153997b2..000000000 --- a/tools/update-generated-files.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Script that finds all `generated_file_test` targets and runs their `.update` targets. - * This is useful if a dependency that is bundled into all checked-in bundles has been - * updated. All of the checked-in bundles would need to be updated and it's cumbersome - * to manually run all of these targets. - */ - -import {dirname, join} from 'path'; -import {fileURLToPath} from 'url'; - -import {spawnSync, SpawnSyncOptionsWithStringEncoding} from 'child_process'; - -const currentDir = dirname(fileURLToPath(import.meta.url)); -const projectDir = join(currentDir, '../'); -const bazelPath = process.env.BAZEL ?? join(projectDir, 'node_modules/.bin/bazel'); -const spawnOptions: SpawnSyncOptionsWithStringEncoding = { - encoding: 'utf8', - cwd: projectDir, - shell: true, -}; - -const queryProcess = spawnSync( - bazelPath, - ['query', `"kind(nodejs_binary, //...) intersect attr(name, '.update$', //...)"`], - spawnOptions, -); - -if (queryProcess.status !== 0) { - throw Error(`Unexpected error: ${queryProcess.error ?? queryProcess.stderr}`); -} - -const updateTargets = queryProcess.stdout.trim().split(/\r?\n/); - -// Build all of the targets in parallel. -spawnSync(bazelPath, ['build', updateTargets.join(' ')], {...spawnOptions, stdio: 'inherit'}); - -for (const targetName of updateTargets) { - const proc = spawnSync(bazelPath, ['run', targetName], {...spawnOptions, stdio: 'inherit'}); - if (proc.status !== 0) { - throw Error(`Unexpected error while updating: ${targetName}.`); - } -}