fix: parameterize AgentRun base image

This commit is contained in:
Codex
2026-06-13 06:29:13 +00:00
parent 5727fe275c
commit a9c8f4cff2
3 changed files with 33 additions and 1 deletions
+4
View File
@@ -95,6 +95,8 @@ controlPlane:
containerfile: deploy/container/Containerfile
repository: agentrun-mgr-env
network: host
buildArgs:
BUN_IMAGE: oven/bun:1.2.15-alpine
httpProxy: http://127.0.0.1:10808
httpsProxy: http://127.0.0.1:10808
noProxy:
@@ -225,6 +227,8 @@ controlPlane:
containerfile: deploy/container/Containerfile
repository: agentrun-mgr-env
network: host
buildArgs:
BUN_IMAGE: oven/bun:1-alpine
httpProxy: http://127.0.0.1:18789
httpsProxy: http://127.0.0.1:18789
noProxy:
+13
View File
@@ -140,6 +140,7 @@ export interface AgentRunImageBuildSpec {
readonly containerfile: string;
readonly repository: string;
readonly network: string;
readonly buildArgs: Readonly<Record<string, string>>;
readonly httpProxy: string | null;
readonly httpsProxy: string | null;
readonly noProxy: readonly string[];
@@ -247,6 +248,7 @@ export function agentRunLaneSummary(spec: AgentRunLaneSpec): Record<string, unkn
containerfile: spec.deployment.manager.imageBuild.containerfile,
repository: spec.deployment.manager.imageBuild.repository,
network: spec.deployment.manager.imageBuild.network,
buildArgNames: Object.keys(spec.deployment.manager.imageBuild.buildArgs).sort(),
proxyConfigured: spec.deployment.manager.imageBuild.httpProxy !== null || spec.deployment.manager.imageBuild.httpsProxy !== null,
noProxyCount: spec.deployment.manager.imageBuild.noProxy.length,
envIdentityFileCount: spec.deployment.manager.imageBuild.envIdentityFiles.length,
@@ -495,6 +497,7 @@ function parseImageBuild(input: Record<string, unknown>, path: string): AgentRun
containerfile: relativePathField(input, "containerfile", path),
repository: stringField(input, "repository", path),
network: stringField(input, "network", path),
buildArgs: stringRecordField(recordField(input, "buildArgs", path), `${path}.buildArgs`),
httpProxy: optionalStringField(input, "httpProxy", path) ?? null,
httpsProxy: optionalStringField(input, "httpsProxy", path) ?? null,
noProxy: stringArrayField(input, "noProxy", path),
@@ -631,6 +634,16 @@ function stringArrayField(obj: Record<string, unknown>, key: string, path: strin
});
}
function stringRecordField(obj: Record<string, unknown>, path: string): Readonly<Record<string, string>> {
const result: Record<string, string> = {};
for (const [key, value] of Object.entries(obj)) {
if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key)) throw new Error(`${path}.${key} must be a valid build arg name`);
if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${path}.${key} must be a non-empty string`);
result[key] = value.trim();
}
return result;
}
function enumField<T extends string>(obj: Record<string, unknown>, key: string, path: string, values: readonly T[]): T {
const value = stringField(obj, key, path);
if (!values.includes(value as T)) throw new Error(`${path}.${key} must be one of ${values.join(", ")}`);
+16 -1
View File
@@ -2674,6 +2674,7 @@ async function triggerCurrentYamlLane(config: UniDeskConfig, options: TriggerOpt
imageBuild: {
repository: `${spec.ci.registryPrefix}/${spec.deployment.manager.imageBuild.repository}`,
containerfile: spec.deployment.manager.imageBuild.containerfile,
buildArgNames: Object.keys(spec.deployment.manager.imageBuild.buildArgs).sort(),
timeoutSeconds: spec.deployment.manager.imageBuild.timeoutSeconds,
pollSeconds: spec.deployment.manager.imageBuild.pollSeconds,
proxyConfigured: spec.deployment.manager.imageBuild.httpProxy !== null || spec.deployment.manager.imageBuild.httpsProxy !== null,
@@ -3296,6 +3297,9 @@ function yamlLaneBuildImageSubmitScript(spec: AgentRunLaneSpec, sourceCommit: st
const noProxy = build.noProxy.join(",");
const imageRepository = `${spec.ci.registryPrefix}/${build.repository}`;
const stateDir = `/tmp/unidesk-agentrun-build-${spec.nodeId}-${spec.lane}`;
const buildArgs = Object.entries(build.buildArgs)
.sort(([left], [right]) => left.localeCompare(right))
.map(([key, value]) => `${key}=${value}`);
const script = [
"set -eu",
`workspace=${shQuote(spec.source.workspace)}`,
@@ -3309,14 +3313,17 @@ function yamlLaneBuildImageSubmitScript(spec: AgentRunLaneSpec, sourceCommit: st
`https_proxy_value=${build.httpsProxy === null ? "''" : shQuote(build.httpsProxy)}`,
`no_proxy_value=${shQuote(noProxy)}`,
`env_identity_files=${shQuote(JSON.stringify(build.envIdentityFiles))}`,
`build_args_json=${shQuote(JSON.stringify(buildArgs))}`,
"mkdir -p \"$state_dir\"",
"cd \"$workspace\"",
"git checkout \"$source_commit\"",
"env_identity=$(ENV_IDENTITY_FILES=\"$env_identity_files\" node <<'NODE'",
"env_identity=$(ENV_IDENTITY_FILES=\"$env_identity_files\" BUILD_ARGS_JSON=\"$build_args_json\" node <<'NODE'",
"const { createHash } = require('node:crypto');",
"const { readFileSync, existsSync } = require('node:fs');",
"const files = JSON.parse(process.env.ENV_IDENTITY_FILES || '[]');",
"const buildArgs = JSON.parse(process.env.BUILD_ARGS_JSON || '[]');",
"const hash = createHash('sha256');",
"for (const item of buildArgs) { hash.update('build-arg'); hash.update('\\0'); hash.update(item); hash.update('\\0'); }",
"for (const file of files) { hash.update(file); hash.update('\\0'); if (existsSync(file)) hash.update(readFileSync(file)); hash.update('\\0'); }",
"process.stdout.write(hash.digest('hex').slice(0, 24));",
"NODE",
@@ -3343,6 +3350,14 @@ function yamlLaneBuildImageSubmitScript(spec: AgentRunLaneSpec, sourceCommit: st
" if [ -n \"$http_proxy_value\" ]; then args=\"$args --build-arg HTTP_PROXY=$http_proxy_value --build-arg http_proxy=$http_proxy_value\"; fi",
" if [ -n \"$https_proxy_value\" ]; then args=\"$args --build-arg HTTPS_PROXY=$https_proxy_value --build-arg https_proxy=$https_proxy_value\"; fi",
" if [ -n \"$no_proxy_value\" ]; then args=\"$args --build-arg NO_PROXY=$no_proxy_value --build-arg no_proxy=$no_proxy_value\"; fi",
" build_arg_values=$(BUILD_ARGS_JSON=\"$build_args_json\" node <<'NODE'",
"const values = JSON.parse(process.env.BUILD_ARGS_JSON || '[]');",
"for (const value of values) console.log(value);",
"NODE",
" )",
" while IFS= read -r build_arg_value; do [ -n \"$build_arg_value\" ] && args=\"$args --build-arg $build_arg_value\"; done <<EOF",
"$build_arg_values",
"EOF",
" if docker image inspect \"$image\" >/dev/null 2>&1; then build_status=reused; else docker build $args -f \"$containerfile\" -t \"$image\" \"$context_dir\"; build_status=built; fi",
" docker push \"$image\"",
" digest=$(docker inspect --format='{{index .RepoDigests 0}}' \"$image\" 2>/dev/null | sed 's/^.*@//' || true)",