-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.ts
139 lines (130 loc) · 4.44 KB
/
main.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { hrtime } from 'process'
import * as core from '@actions/core'
import * as github from '@actions/github'
import { diff, humanise } from './utils/datetime'
import { getPullRequestNumber, getWorkflowRuns } from './utils/github'
import { getWorkflowRunStats } from './utils/stats'
import { getSpeedScore } from './utils/score'
import { buildMarkdownTable } from './utils/markdown'
import type { Context } from '@actions/github/lib/context'
/**
* Input options to the main `run()` method.
*/
export interface ActionOptions {
/** GitHub API [token](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token). This token will be used to get workflow data and post comment on PRs. */
githubToken: string
/** GitHub Action context. */
context: Context
/** Worfklow ID of the workflow to get statistics for. It can be either the workflow ID or the workflow YML file's name. */
workflowId: string | number
/** (Optional) Pull request number. If this is not provided, attempts will be made to guess it. */
pullRequestNumber?: number
}
const PERCENTILE_HELP_LINK =
'https://medium.com/last9/your-percentiles-are-incorrect-p99-of-the-times-11436c97d524' as const
/**
* Collect information about all workflow runs on the repository
* and build statistics, comparing the historical run times with
* the latest run.
*
* @param options Input options.
* @returns PR comment body.
*/
export async function run({
githubToken,
context,
workflowId,
pullRequestNumber,
}: ActionOptions): Promise<void | Array<string>> {
try {
core.debug(`GitHub context: ${JSON.stringify(context, null, 2)}`)
const prNumber = pullRequestNumber || getPullRequestNumber(context)
if (!prNumber) {
core.setFailed('No pull request found.')
return
}
const repository = {
owner: context.payload.repository?.owner.login as string,
repo: context.payload.repository?.name as string,
}
core.debug(`Repository: ${JSON.stringify(repository, null, 2)}`)
const startFetch = hrtime.bigint()
const runData = await getWorkflowRuns(githubToken, workflowId, repository)
const endFetch = hrtime.bigint()
core.debug(
`Finished fetching ${runData.length} workflow runs from GitHub API: ${
Number(endFetch - startFetch) / 1000000
} milliseconds`
)
core.debug(`Worflow run data: ${JSON.stringify(runData, null, 2)}`)
const latestRunTime = diff(runData[0].created_at, runData[0].updated_at)
const runTimeStats = getWorkflowRunStats(runData, latestRunTime)
core.debug(
`Finished collecting build statistics: ${JSON.stringify(runTimeStats)}`
)
const outputTable = buildMarkdownTable([
[
'Fastest',
'Average',
`[p90](${PERCENTILE_HELP_LINK})`,
`[p99](${PERCENTILE_HELP_LINK})`,
'Slowest',
],
[
runTimeStats.min,
runTimeStats.avg,
runTimeStats.p90,
runTimeStats.p99,
runTimeStats.max,
].map(
(ms) =>
`${ms} ms (${humanise({
milliseconds: ms,
})})`
),
])
const octokit = github.getOctokit(githubToken)
const commentBody = [
`The workflow run took \`${latestRunTime}\` milliseconds (${humanise({
milliseconds: latestRunTime,
})}), and was faster than ${getSpeedScore(runTimeStats.percentile)} **${
100 - runTimeStats.percentile
}%** of the past ${runData.length} workflow runs.`,
`${outputTable}`,
]
const newComment = octokit.rest.issues.createComment({
...repository,
issue_number: prNumber,
body: commentBody.join('\n'),
})
core.debug(`Create comment result: ${JSON.stringify(newComment, null, 2)}`)
core.setOutput('stats', runTimeStats)
return commentBody
} catch (error) {
const { name, message } = error as Error
core.setFailed(`Error ${name} – ${message}`)
}
}
/* istanbul ignore next */
if (require.main === module) {
const githubToken = core.getInput('token', {
required: true,
})
const workflowId = core.getInput('workflowId', {
required: true,
})
const pullRequestNumber = core.getInput('pullRequest', {
required: false,
})
const context = github.context
;(async (): Promise<void> => {
await run({
githubToken,
context,
workflowId,
pullRequestNumber: pullRequestNumber
? parseInt(pullRequestNumber, 10)
: undefined,
})
})()
}