Skip to content

Commit 4ff2b91

Browse files
committed
ci: dump hardhat actions catalog at image build time
Without this, the runner image ships /app/actions-catalog.json missing, runner.ts falls back to actionsCatalog = {}, and Talos admin's fetchRunnerEditableFlags() gets 404 on /actions/<task>/params for every task, surfacing as "No editable flags are registered for this task" in the action edit dialog (regardless of params actually defined on the hardhat task). Mirrors the dump step already in talos/docker/arm-oeth.Dockerfile (used by docker-compose dev). The talos-owned Dockerfile is not the one CI builds with — this workflow uses ./dockerfile-actions — so the dump was silently absent in production. Self-contained CommonJS script copied verbatim from talos/docker/dump-actions-catalog.cjs.
1 parent 5a96fd1 commit 4ff2b91

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

dockerfile-actions

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ RUN --mount=type=ssh \
5353
# Copy the rest of the workspace.
5454
COPY . .
5555

56+
# Dump the hardhat task catalog to /app/actions-catalog.json at build time.
57+
# Runs under Node (where hardhat works) — the bun parent can't load hardhat
58+
# itself (keccak native module crashes; bun#18546). The runner reads this
59+
# file at boot and passes it to runContainer's `actionsCatalog`. A dump
60+
# failure is non-fatal: empty catalog ⇒ admin UI shows zero editable flags
61+
# per action (fail-closed), the rest of the runner is unaffected.
62+
RUN cd /app \
63+
&& NODE_PATH=/app/node_modules/.pnpm/node_modules \
64+
node dump-actions-catalog.cjs > /app/actions-catalog.json \
65+
|| (echo "[build] hardhat catalog dump failed; shipping empty catalog" \
66+
&& echo "{}" > /app/actions-catalog.json)
67+
5668
# Hand /app over to the runtime user. After this, all reads/writes by
5769
# the runner process are as `runner`, not root. Closes security/M-012.
5870
RUN chown -R runner:runner /app

dump-actions-catalog.cjs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Dump the sibling's hardhat task registry as a JSON catalog the Talos
4+
* admin UI can intersect with its global EDITABLE_FLAGS whitelist.
5+
*
6+
* Invoked at sibling-image build time (Node, not bun): the runner's bun
7+
* parent process cannot load hardhat directly — keccak's native module
8+
* uses libuv functions bun doesn't implement (oven-sh/bun#18546). Hardhat
9+
* stays in Node-land here; the bundled JSON is shipped into the image
10+
* and read by `runner.ts` at boot.
11+
*
12+
* Output shape matches `ActionParam[]` from `@talos/client/actions-catalog`.
13+
* Self-contained on purpose — no @talos/client import (the bundle is ESM
14+
* and this script runs under Node CJS).
15+
*/
16+
17+
const hre = require("hardhat");
18+
19+
function camelToKebab(s) {
20+
return s.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
21+
}
22+
23+
function normalizeType(name) {
24+
return ["string", "int", "float", "boolean"].includes(name)
25+
? name
26+
: "unknown";
27+
}
28+
29+
function normalizeDefault(v) {
30+
if (v === undefined) return { hasDefault: false, defaultValue: null };
31+
if (v === null) return { hasDefault: true, defaultValue: null };
32+
if (
33+
typeof v === "string" ||
34+
typeof v === "number" ||
35+
typeof v === "boolean"
36+
) {
37+
return { hasDefault: true, defaultValue: v };
38+
}
39+
return { hasDefault: true, defaultValue: null };
40+
}
41+
42+
const catalog = {};
43+
for (const [taskName, def] of Object.entries(hre.tasks)) {
44+
const params = [];
45+
const pd = def.paramDefinitions || {};
46+
for (const p of Object.values(pd)) {
47+
const { hasDefault, defaultValue } = normalizeDefault(p.defaultValue);
48+
params.push({
49+
paramName: p.name,
50+
cliFlag: `--${camelToKebab(p.name)}`,
51+
description: p.description || "",
52+
type: normalizeType(p.type?.name),
53+
isOptional: p.isOptional !== false,
54+
isFlag: !!p.isFlag,
55+
hasDefault,
56+
defaultValue,
57+
});
58+
}
59+
catalog[taskName] = params;
60+
}
61+
62+
process.stdout.write(JSON.stringify(catalog));

0 commit comments

Comments
 (0)