From 4ed3c0efe69002a7d1f011fc214b7ffb131bea59 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Mar 2026 17:53:16 +0000 Subject: [PATCH 1/6] Generate esbuild metadata file --- .gitignore | 2 ++ build.mjs | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 74149b57b3..4dd74f80b6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ build/ eslint.sarif # for local incremental compilation tsconfig.tsbuildinfo +# esbuild metadata file +meta.json diff --git a/build.mjs b/build.mjs index 54b9cafd48..d94a967c93 100644 --- a/build.mjs +++ b/build.mjs @@ -1,4 +1,4 @@ -import { copyFile, rm } from "node:fs/promises"; +import { copyFile, rm, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -74,7 +74,10 @@ const context = await esbuild.context({ define: { __CODEQL_ACTION_VERSION__: JSON.stringify(pkg.version), }, + metafile: true, }); -await context.rebuild(); +const result = await context.rebuild(); +await writeFile("meta.json", JSON.stringify(result.metafile)); + await context.dispose(); From b1981a5480849eb5e897dd696650471fd8124662 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Mar 2026 18:13:48 +0000 Subject: [PATCH 2/6] Move `getApiClient` out of `sync-checks.ts` --- pr-checks/api-client.ts | 13 +++++++++++++ pr-checks/sync-checks.ts | 14 +------------- 2 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 pr-checks/api-client.ts diff --git a/pr-checks/api-client.ts b/pr-checks/api-client.ts new file mode 100644 index 0000000000..93675dba77 --- /dev/null +++ b/pr-checks/api-client.ts @@ -0,0 +1,13 @@ +import * as githubUtils from "@actions/github/lib/utils"; +import { type Octokit } from "@octokit/core"; +import { type PaginateInterface } from "@octokit/plugin-paginate-rest"; +import { type Api } from "@octokit/plugin-rest-endpoint-methods"; + +/** The type of the Octokit client. */ +export type ApiClient = Octokit & Api & { paginate: PaginateInterface }; + +/** Constructs an `ApiClient` using `token` for authentication. */ +export function getApiClient(token: string): ApiClient { + const opts = githubUtils.getOctokitOptions(token); + return new githubUtils.GitHub(opts); +} diff --git a/pr-checks/sync-checks.ts b/pr-checks/sync-checks.ts index 5b5b0bd26e..ef07531107 100755 --- a/pr-checks/sync-checks.ts +++ b/pr-checks/sync-checks.ts @@ -5,12 +5,9 @@ import * as fs from "fs"; import { parseArgs } from "node:util"; -import * as githubUtils from "@actions/github/lib/utils"; -import { type Octokit } from "@octokit/core"; -import { type PaginateInterface } from "@octokit/plugin-paginate-rest"; -import { type Api } from "@octokit/plugin-rest-endpoint-methods"; import * as yaml from "yaml"; +import { type ApiClient, getApiClient } from "./api-client"; import { OLDEST_SUPPORTED_MAJOR_VERSION, PR_CHECK_EXCLUDED_FILE, @@ -49,15 +46,6 @@ function loadExclusions(): Exclusions { ) as Exclusions; } -/** The type of the Octokit client. */ -type ApiClient = Octokit & Api & { paginate: PaginateInterface }; - -/** Constructs an `ApiClient` using `token` for authentication. */ -function getApiClient(token: string): ApiClient { - const opts = githubUtils.getOctokitOptions(token); - return new githubUtils.GitHub(opts); -} - /** * Represents information about a check run. We track the `app_id` that generated the check, * because the API will require it in addition to the name in the future. From 47f1709a3c85a31013a3c4e5e131e722cc4cc39f Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Mar 2026 18:18:08 +0000 Subject: [PATCH 3/6] Add basic metadata analysis script --- package.json | 2 +- pr-checks/bundle-metadata.ts | 31 +++++++++++++++++++++++++++++++ pr-checks/config.ts | 3 +++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100755 pr-checks/bundle-metadata.ts diff --git a/package.json b/package.json index e4a80d1bd8..b76677e46c 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "CodeQL action", "scripts": { "_build_comment": "echo 'Run the full build so we typecheck the project and can reuse the transpiled files in npm test'", - "build": "./scripts/check-node-modules.sh && npm run transpile && node build.mjs", + "build": "./scripts/check-node-modules.sh && npm run transpile && node build.mjs && npx tsx ./pr-checks/bundle-metadata.ts", "lint": "eslint --report-unused-disable-directives --max-warnings=0 .", "lint-ci": "SARIF_ESLINT_IGNORE_SUPPRESSED=true eslint --report-unused-disable-directives --max-warnings=0 . --format @microsoft/eslint-formatter-sarif --output-file=eslint.sarif", "lint-fix": "eslint --report-unused-disable-directives --max-warnings=0 . --fix", diff --git a/pr-checks/bundle-metadata.ts b/pr-checks/bundle-metadata.ts new file mode 100755 index 0000000000..1e7585536c --- /dev/null +++ b/pr-checks/bundle-metadata.ts @@ -0,0 +1,31 @@ +#!/usr/bin/env npx tsx + +import * as fs from "node:fs/promises"; + +import { BUNDLE_METADATA_FILE } from "./config"; + +interface Output { + bytes: number; +} + +interface Metadata { + outputs: Output[]; +} + +async function main() { + const fileContents = await fs.readFile(BUNDLE_METADATA_FILE); + const metadata = JSON.parse(String(fileContents)) as Metadata; + + for (const [outputFile, outputData] of Object.entries( + metadata.outputs, + ).reverse()) { + console.info( + `${outputFile}: ${(outputData.bytes / (1024 * 1024)).toFixed(2)}MB`, + ); + } +} + +// Only call `main` if this script was run directly. +if (require.main === module) { + void main(); +} diff --git a/pr-checks/config.ts b/pr-checks/config.ts index 7f2826d59c..253843f226 100644 --- a/pr-checks/config.ts +++ b/pr-checks/config.ts @@ -8,3 +8,6 @@ export const PR_CHECKS_DIR = __dirname; /** The path of the file configuring which checks shouldn't be required. */ export const PR_CHECK_EXCLUDED_FILE = path.join(PR_CHECKS_DIR, "excluded.yml"); + +/** The path to the esbuild metadata file. */ +export const BUNDLE_METADATA_FILE = path.join(PR_CHECKS_DIR, "..", "meta.json"); From 4e0952a3c05c98605f93e1f076c88c754b421372 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Mar 2026 19:13:02 +0000 Subject: [PATCH 4/6] Output largest inputs --- pr-checks/bundle-metadata.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/pr-checks/bundle-metadata.ts b/pr-checks/bundle-metadata.ts index 1e7585536c..b112cbfbde 100755 --- a/pr-checks/bundle-metadata.ts +++ b/pr-checks/bundle-metadata.ts @@ -4,14 +4,25 @@ import * as fs from "node:fs/promises"; import { BUNDLE_METADATA_FILE } from "./config"; +interface InputInfo { + bytesInOutput: number; +} + +type Inputs = Record; + interface Output { bytes: number; + inputs: Inputs; } interface Metadata { outputs: Output[]; } +function toMB(bytes: number): string { + return `${(bytes / (1024 * 1024)).toFixed(2)}MB`; +} + async function main() { const fileContents = await fs.readFile(BUNDLE_METADATA_FILE); const metadata = JSON.parse(String(fileContents)) as Metadata; @@ -19,9 +30,15 @@ async function main() { for (const [outputFile, outputData] of Object.entries( metadata.outputs, ).reverse()) { - console.info( - `${outputFile}: ${(outputData.bytes / (1024 * 1024)).toFixed(2)}MB`, - ); + console.info(`${outputFile}: ${toMB(outputData.bytes)}`); + + for (const [inputName, inputData] of Object.entries(outputData.inputs)) { + // Ignore any inputs that make up less than 5% of the output. + const percentage = (inputData.bytesInOutput / outputData.bytes) * 100.0; + if (percentage < 5.0) continue; + + console.info(` ${inputName}: ${toMB(inputData.bytesInOutput)}`); + } } } From 3db32b5d2786d4034682d3a454f87821b65d2c71 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Mar 2026 19:13:22 +0000 Subject: [PATCH 5/6] Fix `outputs` type --- pr-checks/bundle-metadata.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr-checks/bundle-metadata.ts b/pr-checks/bundle-metadata.ts index b112cbfbde..25c282e9ae 100755 --- a/pr-checks/bundle-metadata.ts +++ b/pr-checks/bundle-metadata.ts @@ -16,7 +16,7 @@ interface Output { } interface Metadata { - outputs: Output[]; + outputs: Record; } function toMB(bytes: number): string { From f98bf5e3478b909b02d517d0d4dfd8b207d441bd Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 27 Mar 2026 19:14:05 +0000 Subject: [PATCH 6/6] Output relative to `__dirname` --- build.mjs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build.mjs b/build.mjs index d94a967c93..dd078826b5 100644 --- a/build.mjs +++ b/build.mjs @@ -64,7 +64,11 @@ const onEndPlugin = { const context = await esbuild.context({ // Include upload-lib.ts as an entry point for use in testing environments. - entryPoints: globSync([`${SRC_DIR}/*-action.ts`, `${SRC_DIR}/*-action-post.ts`, "src/upload-lib.ts"]), + entryPoints: globSync([ + `${SRC_DIR}/*-action.ts`, + `${SRC_DIR}/*-action-post.ts`, + "src/upload-lib.ts", + ]), bundle: true, format: "cjs", outdir: OUT_DIR, @@ -78,6 +82,6 @@ const context = await esbuild.context({ }); const result = await context.rebuild(); -await writeFile("meta.json", JSON.stringify(result.metafile)); +await writeFile(join(__dirname, "meta.json"), JSON.stringify(result.metafile)); await context.dispose();