Skip to content

Commit 8aeb5a3

Browse files
committed
feat(ng-dev): create workflow performance testing tooling
Creates a piece of tooling within ng-dev that allows for a set of commands to be run that emulate an expected workflow within a repository. This is then measured for the time it takes to run these commands so they can be checked or tracked over time.
1 parent 57684bd commit 8aeb5a3

File tree

12 files changed

+242
-3
lines changed

12 files changed

+242
-3
lines changed

.github/workflows/ci.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,19 @@ jobs:
7171
- uses: ./github-actions/bazel/setup
7272
- run: yarn install --immutable
7373
- run: yarn bazel test --sandbox_writable_path="$HOME/Library/Application Support" --test_tag_filters=macos --build_tests_only -- //...
74+
75+
workflow-perf:
76+
timeout-minutes: 30
77+
runs-on: ubuntu-latest
78+
steps:
79+
# Because the checkout and setup node action is contained in the dev-infra repo, we must
80+
# checkout the repo to be able to run the action we have created. Other repos will skip
81+
# this step.
82+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
83+
- uses: ./github-actions/npm/checkout-and-setup-node
84+
- uses: ./github-actions/bazel/setup
85+
- run: yarn install --immutable
86+
- run: yarn ng-dev perf workflows --json
87+
# Always run this step to ensure that the job always is successful
88+
- if: ${{ always() }}
89+
run: exit 0

.ng-dev/perf-tests/test-rerun.diff

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
diff --git a/ng-dev/utils/test/g3.spec.ts b/ng-dev/utils/test/g3.spec.ts
2+
index a82c1b7a..8e0b24f8 100644
3+
--- a/ng-dev/utils/test/g3.spec.ts
4+
+++ b/ng-dev/utils/test/g3.spec.ts
5+
@@ -29,9 +29,9 @@ describe('G3Stats', () => {
6+
});
7+
8+
function setupFakeSyncConfig(config: GoogleSyncConfig): string {
9+
- const configFileName = 'sync-test-conf.json';
10+
- fs.writeFileSync(path.join(git.baseDir, configFileName), JSON.stringify(config));
11+
- return configFileName;
12+
+ const somethingelse = 'sync-test-conf.json';
13+
+ fs.writeFileSync(path.join(git.baseDir, somethingelse), JSON.stringify(config));
14+
+ return somethingelse;
15+
}
16+
17+
describe('gathering stats', () => {

.ng-dev/workflows.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
workflows:
2+
- name: Rerun a test
3+
prepare: |
4+
bazel clean;
5+
bazel build //ng-dev/utils/test;
6+
workflow: |
7+
bazel test //ng-dev/utils/test;
8+
git apply .ng-dev/perf-tests/test-rerun.diff;
9+
bazel test //ng-dev/utils/test;
10+
cleanup: |
11+
git apply -R .ng-dev/perf-tests/test-rerun.diff;
12+
13+
- name: Build Everything
14+
prepare: |
15+
bazel clean;
16+
workflow: |
17+
bazel build //...;

ng-dev/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ ts_library(
2727
"//ng-dev/format",
2828
"//ng-dev/misc",
2929
"//ng-dev/ngbot",
30+
"//ng-dev/perf",
3031
"//ng-dev/pr",
3132
"//ng-dev/pr/common/labels",
3233
"//ng-dev/pr/config",

ng-dev/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {buildReleaseParser} from './release/cli.js';
2020
import {tsCircularDependenciesBuilder} from './ts-circular-dependencies/index.js';
2121
import {captureLogOutputForCommand} from './utils/logging.js';
2222
import {buildAuthParser} from './auth/cli.js';
23+
import {buildPerfParser} from './perf/cli.js';
2324
import {Argv} from 'yargs';
2425

2526
runParserWithCompletedFunctions((yargs: Argv) => {
@@ -38,6 +39,7 @@ runParserWithCompletedFunctions((yargs: Argv) => {
3839
.command('caretaker <command>', '', buildCaretakerParser)
3940
.command('misc <command>', '', buildMiscParser)
4041
.command('ngbot <command>', false, buildNgbotParser)
42+
.command('perf <command>', '', buildPerfParser)
4143
.wrap(120)
4244
.strict();
4345
});

ng-dev/perf/BUILD.bazel

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

ng-dev/perf/cli.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
9+
import {Argv} from 'yargs';
10+
11+
import {WorkflowsModule} from './workflow/cli.js';
12+
13+
/** Build the parser for pull request commands. */
14+
export function buildPerfParser(localYargs: Argv) {
15+
return localYargs.help().strict().demandCommand().command(WorkflowsModule);
16+
}

ng-dev/perf/workflow/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_library")
2+
3+
ts_library(
4+
name = "workflow",
5+
srcs = glob(["*.ts"]),
6+
visibility = ["//ng-dev:__subpackages__"],
7+
deps = [
8+
"//ng-dev/utils",
9+
"@npm//@types/node",
10+
"@npm//@types/yargs",
11+
"@npm//yaml",
12+
],
13+
)

ng-dev/perf/workflow/cli.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
9+
import {Argv, CommandModule} from 'yargs';
10+
import {measureWorkflow} from './workflow.js';
11+
import {loadWorkflows} from './loader.js';
12+
import {join} from 'path';
13+
import {determineRepoBaseDirFromCwd} from '../../utils/repo-directory.js';
14+
15+
interface WorkflowsParams {
16+
configFile: string;
17+
json: boolean;
18+
}
19+
20+
/** Builds the checkout pull request command. */
21+
function builder(yargs: Argv) {
22+
return yargs
23+
.option('config-file' as 'configFile', {
24+
default: '.ng-dev/workflows.yml',
25+
type: 'string',
26+
description: 'The path to the workflow definitions in a yml file',
27+
})
28+
.option('json', {
29+
default: false,
30+
type: 'boolean',
31+
description: 'Whether to ouput the results as a json object',
32+
});
33+
}
34+
35+
/** Handles the checkout pull request command. */
36+
async function handler({configFile, json}: WorkflowsParams) {
37+
const workflows = await loadWorkflows(join(determineRepoBaseDirFromCwd(), configFile));
38+
const results: {[key: string]: number} = {};
39+
for (const workflow of workflows) {
40+
const {name, duration} = await measureWorkflow(workflow);
41+
results[name] = duration;
42+
}
43+
44+
if (json) {
45+
process.stdout.write(JSON.stringify(results));
46+
}
47+
}
48+
49+
/** yargs command module for checking out a PR */
50+
export const WorkflowsModule: CommandModule<{}, WorkflowsParams> = {
51+
handler,
52+
builder,
53+
command: 'workflows',
54+
describe: 'Evaluate the performance of the provided workflows',
55+
};

ng-dev/perf/workflow/loader.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {readFile} from 'fs/promises';
2+
import {parse} from 'yaml';
3+
4+
export interface Workflow {
5+
name: string;
6+
workflow: string;
7+
prepare?: string;
8+
cleanup?: string;
9+
}
10+
11+
export async function loadWorkflows(src: string) {
12+
const rawWorkflows = await readFile(src, {encoding: 'utf-8'});
13+
return parse(rawWorkflows).workflows as Workflow[];
14+
}

0 commit comments

Comments
 (0)