Skip to content

Commit 2ca2e98

Browse files
committed
feat(ng-dev): add support for uploading results of workflow testing to database (#2472)
Uploads the workflow test results our database when a specific github sha is provided as a reference key for the data. PR Close #2472
1 parent 01c8c16 commit 2ca2e98

File tree

9 files changed

+377
-42
lines changed

9 files changed

+377
-42
lines changed

.github/workflows/perf.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ on:
55
branches:
66
- main
77

8-
permissions: {}
8+
permissions:
9+
contents: 'read'
10+
id-token: 'write'
911

1012
defaults:
1113
run:
1214
shell: bash
1315

1416
jobs:
1517
list:
16-
timeout-minutes: 30
18+
timeout-minutes: 5
1719
runs-on: ubuntu-latest
1820
outputs:
1921
workflows: ${{ steps.workflows.outputs.workflows }}
@@ -43,4 +45,13 @@ jobs:
4345
- uses: ./github-actions/npm/checkout-and-setup-node
4446
- uses: ./github-actions/bazel/setup
4547
- run: yarn install --immutable
46-
- run: yarn ng-dev perf workflows --name ${{ matrix.workflow }}
48+
# We utilize the google-github-actions/auth action to allow us to get an active credential using workflow
49+
# identity federation. This allows us to request short lived credentials on demand, rather than storing
50+
# credentials in secrets long term. More information can be found at:
51+
# https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform
52+
- uses: 'google-github-actions/auth@v2'
53+
with:
54+
project_id: 'internal-200822'
55+
workload_identity_provider: 'projects/823469418460/locations/global/workloadIdentityPools/measurables-tracking/providers/angular'
56+
service_account: '[email protected]'
57+
- run: yarn ng-dev perf workflows --name ${{ matrix.workflow }} --commit-sha ${{github.sha}}

ng-dev/BUILD.bazel

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ NG_DEV_EXTERNALS = [
66
# `typescript` is external because we want the project to provide a TypeScript version.
77
# TODO: Figure out how we want to manage dependencies for the dev-infra tool.
88
"typescript",
9-
# Package uses `__filename` and `__dirname` and cannot be bundled in ESM. We do not
9+
# Packages using `__filename` and `__dirname` and cannot be bundled in ESM. We do not
1010
# intend to provide interop globals for this as it could hide other significant issues.
1111
"@yarnpkg/lockfile",
12+
"@google-cloud/spanner",
1213
]
1314

1415
ts_library(

ng-dev/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
}
1616
},
1717
"dependencies": {
18+
"@google-cloud/spanner": "7.16.0",
1819
"@octokit/rest": "21.0.2",
1920
"@types/semver": "^7.3.6",
2021
"@types/supports-color": "^8.1.1",

ng-dev/perf/workflow/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ ts_library(
66
visibility = ["//ng-dev:__subpackages__"],
77
deps = [
88
"//ng-dev/utils",
9+
"@npm//@google-cloud/spanner",
910
"@npm//@types/node",
1011
"@npm//@types/yargs",
1112
"@npm//yaml",

ng-dev/perf/workflow/cli.ts

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ import {measureWorkflow} from './workflow.js';
1111
import {loadWorkflows} from './loader.js';
1212
import {join} from 'path';
1313
import {determineRepoBaseDirFromCwd} from '../../utils/repo-directory.js';
14+
import {addWorkflowPerformanceResult} from './database.js';
15+
import {Spinner} from '../../utils/spinner.js';
1416

1517
interface WorkflowsParams {
1618
configFile: string;
1719
list: boolean;
1820
name?: string;
21+
commitSha?: string;
1922
}
2023

2124
/** Builds the checkout pull request command. */
@@ -34,30 +37,48 @@ function builder(yargs: Argv) {
3437
.option('name', {
3538
type: 'string',
3639
description: 'A specific workflow to run by name',
40+
})
41+
.option('commit-sha' as 'commitSha', {
42+
type: 'string',
43+
description: 'The commit sha to associate the measurement with, uploading it to our database',
3744
});
3845
}
3946

4047
/** Handles the checkout pull request command. */
41-
async function handler({configFile, list, name}: WorkflowsParams) {
48+
async function handler({configFile, list, name, commitSha}: WorkflowsParams) {
4249
const workflows = await loadWorkflows(join(determineRepoBaseDirFromCwd(), configFile));
4350

4451
if (list) {
4552
process.stdout.write(JSON.stringify(Object.keys(workflows)));
4653
return;
4754
}
4855

56+
const results: {name: string; value: number}[] = [];
57+
4958
if (name) {
50-
const {duration} = await measureWorkflow(workflows[name]);
51-
process.stdout.write(JSON.stringify({[name]: duration}));
52-
return;
59+
const {value} = await measureWorkflow(workflows[name]);
60+
results.push({value, name});
61+
} else {
62+
for (const workflow of Object.values(workflows)) {
63+
const {name, value} = await measureWorkflow(workflow);
64+
results.push({value, name});
65+
}
5366
}
5467

55-
const results: {[key: string]: number} = {};
56-
for (const workflow of Object.values(workflows)) {
57-
const {name, duration} = await measureWorkflow(workflow);
58-
results[name] = duration;
68+
if (commitSha) {
69+
const spinner = new Spinner('Uploading performance results to database');
70+
try {
71+
for (let {value, name} of results) {
72+
await addWorkflowPerformanceResult({
73+
name,
74+
value,
75+
commit_sha: commitSha,
76+
});
77+
}
78+
} finally {
79+
spinner.success('Upload complete');
80+
}
5981
}
60-
process.stdout.write(JSON.stringify(results));
6182
}
6283

6384
/** yargs command module for checking out a PR. */

ng-dev/perf/workflow/database.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 {Spanner} from '@google-cloud/spanner';
10+
11+
/**
12+
* The workflow performance results are stored in a spanner database within our
13+
* project to be able to retrieve later, The commit sha will be used as a foreign
14+
* key to point to specific commits found in the table of commit shas expressing
15+
* the full graph of commits within the git graph for the repository.
16+
*
17+
* The workflow performance table is defined with the following characteristics.
18+
*
19+
* |--------------------------------------------------------------------|
20+
* | Column | Type | Notes |
21+
* |--------------------------------------------------------------------|
22+
* | workflow_performance_id | STRING(36) | Autogenerated |
23+
* | commit_sha | STRING(40) | |
24+
* | value | FLOAT64 | |
25+
* | name | STRING(256) | |
26+
* |--------------------------------------------------------------------|
27+
*/
28+
29+
/** The reuslt of workflow performance eresult to be stored in the database. */
30+
export interface WorkflowPerformanceRowResult {
31+
commit_sha: string;
32+
value: number;
33+
name: string;
34+
}
35+
36+
/** Add a single workflow performance result to the spanner database. */
37+
export async function addWorkflowPerformanceResult(result: WorkflowPerformanceRowResult) {
38+
/** The spanner client instance. */
39+
const spanner = new Spanner({
40+
projectId: 'internal-200822',
41+
});
42+
/** The spanner instance within our project. */
43+
const instance = spanner.instance('ng-measurables');
44+
/** The commit performance database within our spanner instance. */
45+
const database = instance.database('commit_performance');
46+
/** The table holding workflow performance information. */
47+
const workflowPerformanceTable = database.table('WorkflowPerformance');
48+
49+
try {
50+
await workflowPerformanceTable.insert(result);
51+
} finally {
52+
await database.close();
53+
}
54+
}

ng-dev/perf/workflow/workflow.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ export async function measureWorkflow({name, workflow, prepare, cleanup}: Workfl
3333

3434
spinner.success(`${name}: ${results.duration.toFixed(2)}ms`);
3535

36-
return results.toJSON();
36+
return {
37+
name,
38+
value: results.duration,
39+
};
3740
} finally {
3841
spinner.complete();
3942
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"@bazel/ibazel": "^0.23.0",
7272
"@bazel/jasmine": "patch:@bazel/jasmine@npm:5.8.1#.yarn/patches/@bazel-jasmine-npm.patch",
7373
"@google-cloud/firestore": "^7.0.0",
74+
"@google-cloud/spanner": "7.16.0",
7475
"@google-cloud/storage": "^7.0.0",
7576
"@inquirer/prompts": "^7.0.0",
7677
"@inquirer/type": "^3.0.0",

0 commit comments

Comments
 (0)