내부 builder 인증과 실행 허용 목록 및 자원 경계를 적용한다
This commit is contained in:
1 parent
6d05c01a76
commit
f78ab77850
13 files changed
+979
-205
No files matched your search
@@ -1,57 +1,78 @@
|
||||
const builderUrl = process.env.RELEASE_BUILDER_URL ?? 'http://builder:15100';
|
||||
const coreRoot = process.env.GATEWAY_WORKSPACE_ROOT ?? '/srv/core/repository';
|
||||
import {
|
||||
BUILD_TIMEOUT_MS,
|
||||
readBuilderToken,
|
||||
readBuilderMessages,
|
||||
} from "./release-builder-security.mjs";
|
||||
const builderUrl = process.env.RELEASE_BUILDER_URL ?? "http://builder:15100";
|
||||
const coreRoot = process.env.GATEWAY_WORKSPACE_ROOT ?? "/srv/core/repository";
|
||||
const publicBuildEnv = Object.fromEntries(
|
||||
Object.entries(process.env).filter(
|
||||
([name, value]) =>
|
||||
typeof value === 'string' &&
|
||||
/^(?:CI|NODE_OPTIONS|PROFILE_FRONTEND_BUILD_NODE_OPTIONS|RAYON_NUM_THREADS|RELEASE_TURBO_CONCURRENCY|TURBO_CACHE_DIR|TZ|VITE_[A-Z0-9_]+)$/.test(name),
|
||||
typeof value === "string" &&
|
||||
/^(?:CI|NODE_OPTIONS|PROFILE_FRONTEND_BUILD_NODE_OPTIONS|RAYON_NUM_THREADS|RELEASE_TURBO_CONCURRENCY|TURBO_CACHE_DIR|TZ|VITE_[A-Z0-9_]+)$/.test(
|
||||
name,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (process.env.RELEASE_BUILD_NODE_OPTIONS) {
|
||||
publicBuildEnv.NODE_OPTIONS = process.env.RELEASE_BUILD_NODE_OPTIONS;
|
||||
}
|
||||
delete publicBuildEnv.RELEASE_BUILD_NODE_OPTIONS;
|
||||
const pnpm = (...args) => ({ command: 'pnpm', args, cwd: coreRoot, env: publicBuildEnv });
|
||||
const pnpm = (...args) => ({
|
||||
command: "pnpm",
|
||||
args,
|
||||
cwd: coreRoot,
|
||||
env: publicBuildEnv,
|
||||
});
|
||||
const commands = [
|
||||
pnpm('install', '--frozen-lockfile'),
|
||||
pnpm('--filter', '@sammo-ts/common', 'build'),
|
||||
pnpm('--filter', '@sammo-ts/infra', 'prisma:generate'),
|
||||
pnpm('--filter', '@sammo-ts/infra', 'build'),
|
||||
pnpm('--filter', '@sammo-ts/logic', 'build'),
|
||||
pnpm('--filter', '@sammo-ts/game-engine', 'build'),
|
||||
pnpm('--filter', '@sammo-ts/gateway-api', 'build'),
|
||||
pnpm('--filter', '@sammo-ts/gateway-frontend', 'build'),
|
||||
pnpm('--filter', '@sammo-ts/release-controller', 'build'),
|
||||
pnpm("install", "--frozen-lockfile"),
|
||||
pnpm("--filter", "@sammo-ts/common", "build"),
|
||||
pnpm("--filter", "@sammo-ts/infra", "prisma:generate"),
|
||||
pnpm("--filter", "@sammo-ts/infra", "build"),
|
||||
pnpm("--filter", "@sammo-ts/logic", "build"),
|
||||
pnpm("--filter", "@sammo-ts/game-engine", "build"),
|
||||
pnpm("--filter", "@sammo-ts/gateway-api", "build"),
|
||||
pnpm("--filter", "@sammo-ts/gateway-frontend", "build"),
|
||||
pnpm("--filter", "@sammo-ts/release-controller", "build"),
|
||||
];
|
||||
|
||||
const response = await fetch(new URL('/v1/builds', builderUrl), {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
const response = await fetch(new URL("/v1/builds", builderUrl), {
|
||||
method: "POST",
|
||||
redirect: "error",
|
||||
signal: AbortSignal.timeout(BUILD_TIMEOUT_MS),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-release-builder-token": readBuilderToken(
|
||||
process.env.RELEASE_BUILDER_TOKEN_FILE,
|
||||
),
|
||||
},
|
||||
body: JSON.stringify({ commands }),
|
||||
});
|
||||
if (!response.ok || !response.body) {
|
||||
throw new Error((await response.text()) || `Release builder returned HTTP ${response.status}.`);
|
||||
}
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let result = null;
|
||||
const handleLine = (line) => {
|
||||
if (!line.trim()) return;
|
||||
const message = JSON.parse(line);
|
||||
if (message.event?.type === 'OUTPUT') {
|
||||
for await (const message of readBuilderMessages(response)) {
|
||||
if (message.event?.type === "OUTPUT") {
|
||||
if (
|
||||
!["stdout", "stderr"].includes(message.event.stream) ||
|
||||
typeof message.event.message !== "string" ||
|
||||
message.event.message.length > 2000
|
||||
)
|
||||
throw new Error("Invalid release build progress.");
|
||||
const output = `${message.event.message}\n`;
|
||||
if (message.event.stream === 'stderr') process.stderr.write(output);
|
||||
if (message.event.stream === "stderr") process.stderr.write(output);
|
||||
else process.stdout.write(output);
|
||||
}
|
||||
if (message.result) result = message.result;
|
||||
};
|
||||
for await (const chunk of response.body) {
|
||||
buffer += decoder.decode(chunk, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() ?? '';
|
||||
for (const line of lines) handleLine(line);
|
||||
}
|
||||
handleLine(buffer);
|
||||
if (!result?.ok) {
|
||||
throw new Error(result?.output || 'Release builder closed without a successful result.');
|
||||
if (message.result) {
|
||||
if (
|
||||
typeof message.result.ok !== "boolean" ||
|
||||
(message.result.ok &&
|
||||
(message.result.exitCode !== 0 || message.result.aborted === true)) ||
|
||||
typeof message.result.output !== "string" ||
|
||||
message.result.output.length > 64 * 1024
|
||||
)
|
||||
throw new Error("Invalid release build result.");
|
||||
result = message.result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!result?.ok)
|
||||
throw new Error("Release builder did not produce a successful result.");
|
||||
Reference in new issue
Block a user