Build: Generate shared entrypoint

Reduces tarred repo size from 8.5 MB to 2.2 MB
This commit is contained in:
Henry Mercer
2026-05-14 16:29:39 +01:00
parent 336884853e
commit 3c41d8f998
13 changed files with 162274 additions and 1176363 deletions
+103 -8
View File
@@ -1,5 +1,5 @@
import { copyFile, rm, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { basename, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import * as esbuild from "esbuild";
@@ -13,6 +13,71 @@ const __dirname = dirname(__filename);
const SRC_DIR = join(__dirname, "src");
const OUT_DIR = join(__dirname, "lib");
/**
* Name of the shared entrypoint file that imports each Action's code. By introducing a single
* entrypoint for all the Actions, we avoid duplicating code across each Action's bundle.
*/
const SHARED_ENTRYPOINT = "actions-entrypoint";
/** The names of all the Action entry points (as referenced by `action.yml`s). */
function findActionNames() {
return globSync([
`${SRC_DIR}/*-action.ts`,
`${SRC_DIR}/*-action-post.ts`,
])
.map((p) => basename(p, ".ts"))
.sort();
}
const ACTION_NAMES = findActionNames();
/**
* Generate the source for the shared entry point. The generated module dispatches at runtime to the
* Action selected by `CODEQL_ACTION_ENTRYPOINT`, using `require()` to incorporate each Action's
* code without executing the top-level side effects.
*/
function generateEntrypointTypescriptSource() {
const cases = ACTION_NAMES
.map(
(name) =>
` case ${JSON.stringify(name)}:\n require("./${name}");\n break;`,
)
.join("\n");
return `const entrypoint = process.env.CODEQL_ACTION_ENTRYPOINT;
switch (entrypoint) {
${cases}
default:
throw new Error(
\`Unknown CodeQL Action entrypoint: \${JSON.stringify(entrypoint)}. \` +
"This file is intended to be invoked via the generated stubs in lib/.",
);
}
`;
}
/**
* Resolve the virtual shared entry point and provide its generated source to esbuild without
* writing it to disk.
*
* @type {esbuild.Plugin}
*/
const virtualEntrypointPlugin = {
name: "virtual-actions-entrypoint",
setup(build) {
const namespace = "actions-entrypoint";
build.onResolve({ filter: new RegExp(`^${RegExp.escape(SHARED_ENTRYPOINT)}$`) }, () => ({
path: SHARED_ENTRYPOINT,
namespace,
}));
// Restrict using the namespace. The path filter does not need to discriminate any further.
build.onLoad({ filter: /.*/, namespace }, () => ({
contents: generateEntrypointTypescriptSource(),
resolveDir: SRC_DIR,
loader: "ts",
}));
},
};
/**
* Clean the output directory before building.
*
@@ -62,18 +127,48 @@ const onEndPlugin = {
},
};
/**
* Emit a tiny stub file for each Action entrypoint. Each stub sets an environment variable
* identifying which action was invoked and then `require()`s the shared bundle, which dispatches to
* the correct Action's code.
*
* @type {esbuild.Plugin}
*/
const emitActionStubsPlugin = {
name: "emit-action-stubs",
setup(build) {
build.onEnd(async () => {
await Promise.all(
ACTION_NAMES.map(async (name) => {
const stub =
`"use strict";\n` +
`process.env.CODEQL_ACTION_ENTRYPOINT = ${JSON.stringify(name)};\n` +
`require("./${SHARED_ENTRYPOINT}.js");\n`;
await writeFile(join(OUT_DIR, `${name}.js`), stub);
}),
);
});
},
};
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",
]),
// Bundle every action together via the shared entry point. We also keep
// `upload-lib.ts` as a separate entry point for use in testing environments.
entryPoints: [
{ in: SHARED_ENTRYPOINT, out: SHARED_ENTRYPOINT },
join(SRC_DIR, "upload-lib.ts"),
],
bundle: true,
format: "cjs",
outdir: OUT_DIR,
platform: "node",
plugins: [cleanPlugin, copyDefaultsPlugin, onEndPlugin],
plugins: [
cleanPlugin,
copyDefaultsPlugin,
virtualEntrypointPlugin,
emitActionStubsPlugin,
onEndPlugin,
],
target: ["node20"],
define: {
__CODEQL_ACTION_VERSION__: JSON.stringify(pkg.version),
+162149
View File
File diff suppressed because one or more lines are too long
+2 -129182
View File
File diff suppressed because one or more lines are too long
+2 -96037
View File
File diff suppressed because one or more lines are too long
+2 -88243
View File
File diff suppressed because one or more lines are too long
+2 -136976
View File
File diff suppressed because one or more lines are too long
+2 -93005
View File
File diff suppressed because one or more lines are too long
+2 -87797
View File
File diff suppressed because one or more lines are too long
+2 -89619
View File
File diff suppressed because one or more lines are too long
+2 -127994
View File
File diff suppressed because one or more lines are too long
+2 -105057
View File
File diff suppressed because one or more lines are too long
+2 -128019
View File
File diff suppressed because one or more lines are too long
+2 -94426
View File
File diff suppressed because one or more lines are too long