diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index ee38ac8c..5496b06a 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -476,6 +476,11 @@ lanes: builderImage: golang:1.25-bookworm goProxy: https://goproxy.cn,direct dockerNetworkMode: host + - id: opencode-git + kind: opencode-git + target: 127.0.0.1:5000/hwlab/opencode:1.17.7-git + sourceImage: ghcr.io/anomalyco/opencode:1.17.7 + dockerNetworkMode: host public: webUrl: https://hwlab.pikapython.com apiUrl: https://hwlab.pikapython.com @@ -750,6 +755,11 @@ lanes: builderImage: golang:1.25-bookworm goProxy: https://goproxy.cn,direct dockerNetworkMode: host + - id: opencode-git + kind: opencode-git + target: 127.0.0.1:5000/hwlab/opencode:1.17.7-git + sourceImage: ghcr.io/anomalyco/opencode:1.17.7 + dockerNetworkMode: host public: webUrl: https://hwlab.pikapython.com apiUrl: https://hwlab.pikapython.com @@ -1101,6 +1111,11 @@ lanes: builderImage: golang:1.25-bookworm goProxy: https://goproxy.cn,direct dockerNetworkMode: host + - id: opencode-git + kind: opencode-git + target: 127.0.0.1:5000/hwlab/opencode:1.17.7-git + sourceImage: ghcr.io/anomalyco/opencode:1.17.7 + dockerNetworkMode: host public: webUrl: https://hwlab.pikapython.com apiUrl: https://hwlab.pikapython.com diff --git a/scripts/src/hwlab-node-lanes.ts b/scripts/src/hwlab-node-lanes.ts index 7971a7e0..14f076b0 100644 --- a/scripts/src/hwlab-node-lanes.ts +++ b/scripts/src/hwlab-node-lanes.ts @@ -313,12 +313,13 @@ export interface HwlabRuntimeImageRewriteSpec { export interface HwlabRuntimeImageBuildSpec { readonly id: string; - readonly kind: "moonbridge"; + readonly kind: "moonbridge" | "opencode-git"; readonly target: string; - readonly sourceRepo: string; - readonly sourceRef: string; - readonly builderImage: string; - readonly goProxy: string; + readonly sourceImage?: string; + readonly sourceRepo?: string; + readonly sourceRef?: string; + readonly builderImage?: string; + readonly goProxy?: string; readonly dockerNetworkMode: "default" | "host"; } @@ -1569,7 +1570,16 @@ function runtimeImageBuildsConfig(value: unknown, path: string): HwlabRuntimeIma const itemPath = `${path}[${index}]`; const raw = asRecord(item, itemPath); const kind = stringField(raw, "kind", itemPath); - if (kind !== "moonbridge") throw new Error(`${itemPath}.kind must be moonbridge`); + if (kind !== "moonbridge" && kind !== "opencode-git") throw new Error(`${itemPath}.kind must be moonbridge or opencode-git`); + if (kind === "opencode-git") { + return { + id: stringField(raw, "id", itemPath), + kind, + target: stringField(raw, "target", itemPath), + sourceImage: stringField(raw, "sourceImage", itemPath), + dockerNetworkMode: enumStringField(raw, "dockerNetworkMode", itemPath, ["default", "host"]), + }; + } return { id: stringField(raw, "id", itemPath), kind, diff --git a/scripts/src/hwlab-node/web-probe.ts b/scripts/src/hwlab-node/web-probe.ts index 367239a8..538177e0 100644 --- a/scripts/src/hwlab-node/web-probe.ts +++ b/scripts/src/hwlab-node/web-probe.ts @@ -847,7 +847,7 @@ interface NodeRuntimeImageDependency { id: string; target: string; source: string; - mode: "pull" | "build-moonbridge"; + mode: "pull" | "build-moonbridge" | "build-opencode-git"; sourceRepo?: string; sourceRef?: string; builderImage?: string; @@ -866,15 +866,25 @@ function nodeRuntimeImageDependencies(spec: HwlabRuntimeLaneSpec): NodeRuntimeIm images.push({ id: `rewrite-${index + 1}`, target: rewrite.target, source: rewrite.source, mode: "pull" }); }); spec.runtimeImageBuilds.forEach((build) => { + if (build.kind === "moonbridge") { + images.push({ + id: build.id, + target: build.target, + source: `${build.sourceRepo}#${build.sourceRef}`, + mode: "build-moonbridge", + sourceRepo: build.sourceRepo, + sourceRef: build.sourceRef, + builderImage: build.builderImage, + goProxy: build.goProxy, + dockerNetworkMode: build.dockerNetworkMode, + }); + return; + } images.push({ id: build.id, target: build.target, - source: `${build.sourceRepo}#${build.sourceRef}`, - mode: "build-moonbridge", - sourceRepo: build.sourceRepo, - sourceRef: build.sourceRef, - builderImage: build.builderImage, - goProxy: build.goProxy, + source: build.sourceImage ?? "", + mode: "build-opencode-git", dockerNetworkMode: build.dockerNetworkMode, }); }); @@ -1051,7 +1061,7 @@ export function ensureNodeBaseImage(spec: HwlabRuntimeLaneSpec, dryRun: boolean, " if [ \"$present\" = false ]; then", " action=seed", " if [ \"$dry_run\" = false ]; then", - " if [ \"$mode\" = build-moonbridge ]; then", + " if [ \"$mode\" = build-moonbridge ]; then", " action=build", " source_present_before=build-source", " effective_build_container_http_proxy=\"$build_container_http_proxy\"", @@ -1106,6 +1116,46 @@ export function ensureNodeBaseImage(spec: HwlabRuntimeLaneSpec, dryRun: boolean, " failed=true", " fi", " rm -rf \"$tmpdir\"", + " elif [ \"$mode\" = build-opencode-git ]; then", + " action=build", + " source_present_before=false", + " if [ -n \"$source\" ] && docker image inspect \"$source\" >/dev/null 2>&1; then source_present_before=true; fi", + " effective_build_container_http_proxy=\"$build_container_http_proxy\"", + " effective_build_container_https_proxy=\"$build_container_https_proxy\"", + " effective_build_container_all_proxy=\"$build_container_all_proxy\"", + " effective_docker_build_add_host_args=\"$docker_build_add_host_args\"", + " if [ \"$docker_network_mode\" = host ]; then", + " effective_build_container_http_proxy=\"$build_http_proxy\"", + " effective_build_container_https_proxy=\"$build_https_proxy\"", + " effective_build_container_all_proxy=\"$build_all_proxy\"", + " effective_docker_build_add_host_args=\"\"", + " fi", + " tmpdir=$(mktemp -d /tmp/hwlab-node-runtime-image-$id.XXXXXX)", + " dockerfile=\"$tmpdir/Dockerfile\"", + " cat > \"$dockerfile\" <<'DOCKERFILE'", + "ARG SOURCE_IMAGE", + "FROM ${SOURCE_IMAGE}", + "ARG HTTP_PROXY", + "ARG HTTPS_PROXY", + "ARG ALL_PROXY", + "ARG NO_PROXY", + "ENV HTTP_PROXY=${HTTP_PROXY}", + "ENV HTTPS_PROXY=${HTTPS_PROXY}", + "ENV ALL_PROXY=${ALL_PROXY}", + "ENV NO_PROXY=${NO_PROXY}", + "ENV http_proxy=${HTTP_PROXY}", + "ENV https_proxy=${HTTPS_PROXY}", + "ENV all_proxy=${ALL_PROXY}", + "ENV no_proxy=${NO_PROXY}", + "RUN apk add --no-cache git", + "DOCKERFILE", + " if env HTTP_PROXY=\"$build_http_proxy\" HTTPS_PROXY=\"$build_https_proxy\" ALL_PROXY=\"$build_all_proxy\" NO_PROXY=\"$build_no_proxy\" http_proxy=\"$build_http_proxy\" https_proxy=\"$build_https_proxy\" all_proxy=\"$build_all_proxy\" no_proxy=\"$build_no_proxy\" docker build $effective_docker_build_add_host_args --network \"$docker_network_mode\" --build-arg SOURCE_IMAGE=\"$source\" --build-arg HTTP_PROXY=\"$effective_build_container_http_proxy\" --build-arg HTTPS_PROXY=\"$effective_build_container_https_proxy\" --build-arg ALL_PROXY=\"$effective_build_container_all_proxy\" --build-arg NO_PROXY=\"$build_no_proxy\" --build-arg http_proxy=\"$effective_build_container_http_proxy\" --build-arg https_proxy=\"$effective_build_container_https_proxy\" --build-arg all_proxy=\"$effective_build_container_all_proxy\" --build-arg no_proxy=\"$build_no_proxy\" -t \"$target\" -f \"$dockerfile\" \"$tmpdir\" >/tmp/hwlab-node-runtime-image-$id-build.out 2>&1; then", + " docker push \"$target\" >/tmp/hwlab-node-runtime-image-$id-push.out 2>&1 || { cat /tmp/hwlab-node-runtime-image-$id-push.out >&2 2>/dev/null || true; failed=true; }", + " else", + " cat /tmp/hwlab-node-runtime-image-$id-build.out >&2 2>/dev/null || true", + " failed=true", + " fi", + " rm -rf \"$tmpdir\"", " else", " source_present_before=true", " if ! docker image inspect \"$source\" >/dev/null 2>&1; then", @@ -1147,7 +1197,7 @@ export function ensureNodeBaseImage(spec: HwlabRuntimeLaneSpec, dryRun: boolean, nodeRuntimeImageCalls(spec, "ensure_image"), "if [ \"$failed\" = true ]; then exit 1; fi", ].join("\n"); - const hasBuild = nodeRuntimeImageDependencies(spec).some((image) => image.mode === "build-moonbridge"); + const hasBuild = nodeRuntimeImageDependencies(spec).some((image) => image.mode === "build-moonbridge" || image.mode === "build-opencode-git"); const result = hasBuild && !dryRun ? runNodeHostScriptAsync(spec, script, Math.min(timeoutSeconds, 600), "runtime-image-build") : runNodeHostScript(spec, script, Math.min(timeoutSeconds, 300));