forked from trezor/trezor-suite
-
Notifications
You must be signed in to change notification settings - Fork 0
/
githubAnalytics.ts
124 lines (104 loc) · 3.81 KB
/
githubAnalytics.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import 'dotenv/config';
import { Octokit } from 'octokit';
import chalk from 'chalk';
if (!process.env.GH_TOKEN) {
console.log(chalk.red.bold('Missing GH_TOKEN env variable.'));
console.log(
chalk.red(
'Please create a personal access token at https://github.com/settings/tokens and create a .env file with GH_TOKEN=your_token',
),
);
process.exit(1);
}
const GH_TOKEN = process.env.GH_TOKEN!;
// Workflow ID is the name of the workflow file in the .github/workflows directory
const VALIDATION_WORKFLOW_ID = 'validation.yml';
// Number of workflow runs to analyze, should be a multiple of 100
const NUMBER_OF_WORFLOW_RUNS_TO_ANALYZE = 500;
// Create a personal access token at https://github.com/settings/tokens/new?scopes=repo
const octokit = new Octokit({ auth: GH_TOKEN });
type Run = {
id: number;
name: string | null | undefined;
created_at: string;
time: number;
timeInMinutes: string;
};
async function getValidationCheckRuns({ pagesToFetch }: { pagesToFetch: number }): Promise<Run[]> {
if (pagesToFetch < 1) {
return [];
}
try {
const response = await octokit.request(
'GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs',
{
owner: 'trezor',
repo: 'trezor-suite',
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
status: 'success',
page: pagesToFetch,
per_page: 100,
workflow_id: VALIDATION_WORKFLOW_ID,
},
);
const runs: Run[] = response.data.workflow_runs.map(run => ({
id: run.id,
name: run.name,
created_at: run.created_at,
time:
(new Date(run.updated_at).getTime() - new Date(run.run_started_at!).getTime()) /
1000,
timeInMinutes: (
(new Date(run.updated_at).getTime() - new Date(run.run_started_at!).getTime()) /
1000 /
60
).toFixed(2),
}));
return [...runs, ...(await getValidationCheckRuns({ pagesToFetch: pagesToFetch - 1 }))];
} catch (error) {
console.error(chalk.red('Error fetching validation check runs'), error);
return [];
}
}
const pagesToFetch = Math.ceil(NUMBER_OF_WORFLOW_RUNS_TO_ANALYZE / 100);
const validationCheckRuns = await getValidationCheckRuns({ pagesToFetch });
console.log(
'Number of successful %s runs: %d, average time: %d minutes',
VALIDATION_WORKFLOW_ID,
validationCheckRuns.length,
(
validationCheckRuns.reduce((acc, run) => acc + parseFloat(run.timeInMinutes), 0) /
validationCheckRuns.length
).toFixed(2),
);
const sortedTimes = validationCheckRuns.map(run => run.time).sort((a: number, b: number) => a - b);
// Slowest time of validation check runs
const p100Time = sortedTimes[sortedTimes.length - 1];
console.log('Slowest time: %d minutes (%d seconds total)', (p100Time / 60).toFixed(2), p100Time);
const P = [0.99, 0.98, 0.95, 0.9];
P.forEach(p => {
const index = Math.floor(sortedTimes.length * p);
const time = sortedTimes[index];
console.log(
'P%d time (%d runs): %d minutes (%d seconds total)',
p * 100,
index,
(time / 60).toFixed(2),
time,
);
});
// Fastest time of validation check runs
const p0Time = sortedTimes[0];
console.log('Fastest time: %d minutes (%d seconds total)', (p0Time / 60).toFixed(2), p0Time);
// Oldest time of validation check runs
const sortedRuns = validationCheckRuns.sort(
(a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime(),
);
console.log(
'Oldest run in this batch: %s',
new Date(sortedRuns[0].created_at).toLocaleString('en-US', {
timeZone: 'UTC',
}),
);