Skip to content

Commit 8291875

Browse files
committed
Fetch and filter check runs for ref
1 parent f9f9735 commit 8291875

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

pr-checks/sync-checks.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ import * as yaml from "yaml";
1313

1414
import { OLDEST_SUPPORTED_MAJOR_VERSION } from "./config";
1515

16+
/** Identifies the CodeQL Action repository. */
17+
const codeqlActionRepo = {
18+
owner: "github",
19+
repo: "codeql-action",
20+
};
21+
1622
/** Represents a configuration of which checks should not be set up as required checks. */
1723
interface Exclusions {
1824
/** A list of strings that, if contained in a check name, are excluded. */
@@ -35,6 +41,86 @@ function getApiClient(token: string): ApiClient {
3541
return new githubUtils.GitHub(opts);
3642
}
3743

44+
/**
45+
* Represents information about a check run. We track the `app_id` that generated the check,
46+
* because the API will require it in addition to the name in the future.
47+
*/
48+
interface CheckInfo {
49+
/** The display name of the check. */
50+
context: string;
51+
/** The ID of the app that generated the check. */
52+
app_id: number;
53+
}
54+
55+
/** Removes entries from `checkInfos` based the configuration. */
56+
export function removeExcluded(
57+
exclusions: Exclusions,
58+
checkInfos: CheckInfo[],
59+
): CheckInfo[] {
60+
console.log(exclusions);
61+
62+
return checkInfos.filter((checkInfo) => {
63+
if (exclusions.is.includes(checkInfo.context)) {
64+
console.info(
65+
`Excluding '${checkInfo.context}' because it is an exact exclusion.`,
66+
);
67+
return false;
68+
}
69+
70+
for (const containsStr of exclusions.contains) {
71+
if (checkInfo.context.includes(containsStr)) {
72+
console.info(
73+
`Excluding '${checkInfo.context}' because it contains '${containsStr}'.`,
74+
);
75+
return false;
76+
}
77+
}
78+
79+
// Keep.
80+
return true;
81+
});
82+
}
83+
84+
/** Gets a list of check run names for `ref`. */
85+
async function getChecksFor(
86+
client: ApiClient,
87+
ref: string,
88+
): Promise<CheckInfo[]> {
89+
console.info(`Getting checks for '${ref}'`);
90+
91+
const response = await client.paginate(
92+
"GET /repos/{owner}/{repo}/commits/{ref}/check-runs",
93+
{
94+
...codeqlActionRepo,
95+
ref,
96+
},
97+
);
98+
99+
if (response.length === 0) {
100+
throw new Error(`No checks found for '${ref}'.`);
101+
}
102+
103+
console.info(`Retrieved ${response.length} check runs.`);
104+
105+
const notSkipped = response.filter(
106+
(checkRun) => checkRun.conclusion !== "skipped",
107+
);
108+
console.info(`Of those: ${notSkipped.length} were not skipped.`);
109+
110+
// We use the ID of the app that generated the check run when returned by the API,
111+
// but default to -1 to tell the API that any check with the given name should be
112+
// required.
113+
const checkInfos = notSkipped.map((check) => ({
114+
context: check.name,
115+
app_id: check.app?.id || -1,
116+
}));
117+
118+
// Load the configuration for which checks to exclude and apply it before
119+
// returning the checks.
120+
const exclusions = loadExclusions();
121+
return removeExcluded(exclusions, checkInfos);
122+
}
123+
38124
async function main(): Promise<void> {
39125
const { values: options } = parseArgs({
40126
options: {
@@ -69,6 +155,11 @@ async function main(): Promise<void> {
69155
// Initialise the API client.
70156
const client = getApiClient(options.token);
71157

158+
// Find the check runs for the specified `ref` that we will later set as the required checks
159+
// for the main and release branches.
160+
const checkInfos = await getChecksFor(client, options.ref);
161+
const checkNames = new Set(checkInfos.map((info) => info.context));
162+
72163
process.exit(0);
73164
}
74165

0 commit comments

Comments
 (0)