From e466610a8fae6c6bd2d6d967ade9d8f546a01a0a Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Wed, 6 May 2026 17:36:47 -0500 Subject: [PATCH] refactor: generalize container build tooling for Docker and Podman compatibility Rename Docker-branded env vars, scripts, and mise tasks to generic container terminology while preserving full backwards compatibility. - Add ce_resolve_containerfile() helper to container-engine.sh that auto-detects Containerfile.X or Dockerfile.X (Containerfile preferred) - Rename build scripts from docker-* to container-* (4 files) - Add CONTAINER_* env var equivalents with DOCKER_* fallbacks (9 vars) - Create tasks/container.toml with canonical build:container:* task names - Convert tasks/docker.toml to pure alias file for backwards compat - Update all cross-references in shell scripts, TOML task files, CI workflow, architecture docs, and agent files - Fix pre-existing bug: remove exec keyword from container-build-ci.sh (exec does not work with bash functions) Closes #968 Signed-off-by: Adam Miller --- .github/workflows/docker-build.yml | 2 +- AGENTS.md | 4 +- architecture/build-containers.md | 2 +- crates/openshell-cli/src/doctor_llm_prompt.md | 2 +- crates/openshell-vm/scripts/build-rootfs.sh | 10 +- e2e/with-docker-gateway.sh | 2 +- .../cluster-deploy-fast-test.sh | 4 +- ...docker-cleanup.sh => container-cleanup.sh} | 4 +- scripts/remote-deploy.sh | 4 +- tasks/ci.toml | 2 +- tasks/cluster.toml | 2 +- tasks/container.toml | 46 ++++ tasks/docker.toml | 61 +++-- tasks/python.toml | 4 +- tasks/scripts/cluster-bootstrap.sh | 4 +- tasks/scripts/cluster-deploy-fast.sh | 35 +-- tasks/scripts/cluster-push-component.sh | 2 +- ...cker-build-ci.sh => container-build-ci.sh} | 21 +- tasks/scripts/container-build-image.sh | 224 ++++++++++++++++++ tasks/scripts/container-engine.sh | 28 ++- ...arch.sh => container-publish-multiarch.sh} | 40 ++-- tasks/scripts/docker-build-image.sh | 212 ----------------- tasks/scripts/gateway-docker.sh | 2 +- tasks/scripts/gateway.sh | 6 +- 24 files changed, 410 insertions(+), 313 deletions(-) rename scripts/{docker-cleanup.sh => container-cleanup.sh} (98%) create mode 100644 tasks/container.toml rename tasks/scripts/{docker-build-ci.sh => container-build-ci.sh} (56%) create mode 100755 tasks/scripts/container-build-image.sh rename tasks/scripts/{docker-publish-multiarch.sh => container-publish-multiarch.sh} (79%) delete mode 100755 tasks/scripts/docker-build-image.sh diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 3b3aa1cb8..9e4e2fcd4 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -226,7 +226,7 @@ jobs: DOCKER_BUILDER: openshell run: | set -euo pipefail - mise exec -- tasks/scripts/docker-build-image.sh "${{ inputs.component }}" \ + mise exec -- tasks/scripts/container-build-image.sh "${{ inputs.component }}" \ --cache-from "type=gha,scope=${{ inputs.component }}-${{ matrix.arch }}" \ --cache-to "type=gha,mode=max,scope=${{ inputs.component }}-${{ matrix.arch }}" diff --git a/AGENTS.md b/AGENTS.md index 93062fd5f..49fb0e3bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -179,9 +179,9 @@ ocsf_emit!(event); - Always use `uv` for Python commands (e.g., `uv pip install`, `uv run`, `uv venv`) -## Docker +## Containers -- Always prefer `mise` commands over direct docker builds (e.g., `mise run docker:build` instead of `docker build`) +- Always prefer `mise` commands over direct container builds (e.g., `mise run build:container` instead of `docker build` or `podman build`) ## Cluster Infrastructure Changes diff --git a/architecture/build-containers.md b/architecture/build-containers.md index c59ec9a5a..7dd552b23 100644 --- a/architecture/build-containers.md +++ b/architecture/build-containers.md @@ -30,7 +30,7 @@ The chart remains the supported deployment artifact for Kubernetes. `deploy/docker/Dockerfile.images` no longer compiles Rust. CI calls `.github/workflows/shadow-rust-native-build.yml` through `workflow_call` to build `openshell-gateway` or `openshell-sandbox` natively on the target architecture. `.github/workflows/docker-build.yml` downloads the resulting artifact, stages it at `deploy/docker/.build/prebuilt-binaries//`, builds the per-arch image with the local Buildx driver, and merges multi-arch pushes with `docker buildx imagetools create`. Callers normally publish the GitHub SHA tag, but can pass `image-tag` to publish isolated temporary tags for validation. -Local image builds use `tasks/scripts/stage-prebuilt-binaries.sh` through `tasks/scripts/docker-build-image.sh` before invoking Docker, so clean checkouts do not need to create the staging directory manually. +Local image builds use `tasks/scripts/stage-prebuilt-binaries.sh` through `tasks/scripts/container-build-image.sh` before invoking Docker, so clean checkouts do not need to create the staging directory manually. ## Supervisor Delivery diff --git a/crates/openshell-cli/src/doctor_llm_prompt.md b/crates/openshell-cli/src/doctor_llm_prompt.md index fbb26a565..74a870f99 100644 --- a/crates/openshell-cli/src/doctor_llm_prompt.md +++ b/crates/openshell-cli/src/doctor_llm_prompt.md @@ -280,7 +280,7 @@ If DNS is broken, all image pulls from the distribution registry will fail, as w | `tls handshake eof` from `openshell status` | Server not running or mTLS credentials missing/mismatched | Check StatefulSet replicas (Step 3) and mTLS files (Step 6) | | StatefulSet `0/0` replicas | StatefulSet scaled to zero (failed deploy, manual scale-down, or Helm misconfiguration) | `openshell doctor exec -- kubectl -n openshell scale statefulset openshell --replicas=1` | | Local mTLS files missing | Deploy was interrupted before credentials were persisted | Extract from cluster secret `openshell-client-tls` (Step 6) | -| Container not found | Image not built | `mise run docker:build:cluster` (local) or re-deploy (remote) | +| Container not found | Image not built | `mise run build:container:cluster` (local) or re-deploy (remote) | | Container exited, OOMKilled | Insufficient memory | Increase host memory or reduce workload | | Container exited, non-zero exit | k3s crash, port conflict, privilege issue | Check `openshell doctor logs` for details | | `/readyz` fails | k3s still starting or crashed | Wait longer or check container logs for k3s errors | diff --git a/crates/openshell-vm/scripts/build-rootfs.sh b/crates/openshell-vm/scripts/build-rootfs.sh index bfafe8c85..d999cf250 100755 --- a/crates/openshell-vm/scripts/build-rootfs.sh +++ b/crates/openshell-vm/scripts/build-rootfs.sh @@ -81,13 +81,13 @@ fi case "$GUEST_ARCH" in aarch64) - DOCKER_PLATFORM="linux/arm64" + CONTAINER_PLATFORM="linux/arm64" K3S_BINARY_SUFFIX="-arm64" K3S_CHECKSUM_VAR="K3S_ARM64_SHA256" RUST_TARGET="aarch64-unknown-linux-gnu" ;; x86_64) - DOCKER_PLATFORM="linux/amd64" + CONTAINER_PLATFORM="linux/amd64" K3S_BINARY_SUFFIX="" # x86_64 binary has no suffix K3S_CHECKSUM_VAR="K3S_AMD64_SHA256" RUST_TARGET="x86_64-unknown-linux-gnu" @@ -294,7 +294,7 @@ fi ce rm -f "${CONTAINER_NAME}" 2>/dev/null || true echo "==> Building base image..." -ce build --platform "${DOCKER_PLATFORM}" -t "${BASE_IMAGE_TAG}" \ +ce build --platform "${CONTAINER_PLATFORM}" -t "${BASE_IMAGE_TAG}" \ --build-arg "BASE_IMAGE=${VM_BASE_IMAGE}" -f - . <<'DOCKERFILE' ARG BASE_IMAGE FROM ${BASE_IMAGE} @@ -318,7 +318,7 @@ DOCKERFILE # Create a container and export the filesystem echo "==> Creating container..." -ce create --platform "${DOCKER_PLATFORM}" --name "${CONTAINER_NAME}" "${BASE_IMAGE_TAG}" /bin/true +ce create --platform "${CONTAINER_PLATFORM}" --name "${CONTAINER_NAME}" "${BASE_IMAGE_TAG}" /bin/true echo "==> Exporting filesystem..." # Previous builds may leave overlayfs work/ dirs with permissions that @@ -513,7 +513,7 @@ pull_and_save() { # Try to pull; if the registry is unavailable, fall back to the # local Docker image cache (image may exist from a previous pull). echo " pulling: ${image}..." - if ! ce pull --platform "${DOCKER_PLATFORM}" "${image}" --quiet 2>/dev/null; then + if ! ce pull --platform "${CONTAINER_PLATFORM}" "${image}" --quiet 2>/dev/null; then echo " pull failed, checking local image cache..." if ! ce image inspect "${image}" >/dev/null 2>&1; then echo "ERROR: image ${image} not available locally or from registry" diff --git a/e2e/with-docker-gateway.sh b/e2e/with-docker-gateway.sh index 449e257c4..900eca67e 100755 --- a/e2e/with-docker-gateway.sh +++ b/e2e/with-docker-gateway.sh @@ -342,7 +342,7 @@ else CONTAINER_ENGINE=docker \ DOCKER_PLATFORM="linux/${DAEMON_ARCH}" \ DOCKER_OUTPUT="type=local,dest=${SUPERVISOR_OUT_DIR}" \ - bash "${ROOT}/tasks/scripts/docker-build-image.sh" supervisor-output + bash "${ROOT}/tasks/scripts/container-build-image.sh" supervisor-output fi if [ ! -f "${SUPERVISOR_BIN}" ]; then diff --git a/scripts/build-benchmark/cluster-deploy-fast-test.sh b/scripts/build-benchmark/cluster-deploy-fast-test.sh index e1867354b..6235e79f4 100755 --- a/scripts/build-benchmark/cluster-deploy-fast-test.sh +++ b/scripts/build-benchmark/cluster-deploy-fast-test.sh @@ -230,7 +230,7 @@ run_fast_deploy() { BUILDKIT_PROGRESS=plain \ CLUSTER_NAME="${CLUSTER_NAME}" \ DEPLOY_FAST_STATE_FILE="${state_file}" \ - DOCKER_BUILD_CACHE_DIR="${CACHE_DIR}" \ + CONTAINER_BUILD_CACHE_DIR="${CACHE_DIR}" \ "$@" \ mise run cluster ) >"${log_file}" 2>&1 || true @@ -252,7 +252,7 @@ run_fast_deploy_args() { BUILDKIT_PROGRESS=plain \ CLUSTER_NAME="${CLUSTER_NAME}" \ DEPLOY_FAST_STATE_FILE="${state_file}" \ - DOCKER_BUILD_CACHE_DIR="${CACHE_DIR}" \ + CONTAINER_BUILD_CACHE_DIR="${CACHE_DIR}" \ mise run cluster -- "$@" ) >"${log_file}" 2>&1 || true end=$(date +%s) diff --git a/scripts/docker-cleanup.sh b/scripts/container-cleanup.sh similarity index 98% rename from scripts/docker-cleanup.sh rename to scripts/container-cleanup.sh index fc9a69809..a0e622d25 100755 --- a/scripts/docker-cleanup.sh +++ b/scripts/container-cleanup.sh @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -# Clean up stale Docker images, volumes, and build cache that are not in use +# Clean up stale container images, volumes, and build cache that are not in use # by the currently deployed OpenShell cluster. # # Preserves: @@ -13,7 +13,7 @@ # - Volumes attached to running containers # # Usage: -# ./scripts/docker-cleanup.sh [options] +# ./scripts/container-cleanup.sh [options] # # Options: # --dry-run Show what would be removed without deleting anything diff --git a/scripts/remote-deploy.sh b/scripts/remote-deploy.sh index 8b859b688..ab6291a68 100755 --- a/scripts/remote-deploy.sh +++ b/scripts/remote-deploy.sh @@ -235,8 +235,8 @@ rm -f .env echo "==> Building container images (tag=${IMAGE_TAG})..." export OPENSHELL_CARGO_VERSION="${CARGO_VERSION}" export IMAGE_TAG -mise exec -- tasks/scripts/docker-build-image.sh cluster -mise exec -- tasks/scripts/docker-build-image.sh gateway +mise exec -- tasks/scripts/container-build-image.sh cluster +mise exec -- tasks/scripts/container-build-image.sh gateway export OPENSHELL_CLUSTER_IMAGE="openshell/cluster:${IMAGE_TAG}" export OPENSHELL_PUSH_IMAGES="openshell/gateway:${IMAGE_TAG}" diff --git a/tasks/ci.toml b/tasks/ci.toml index c5728168b..843a3149c 100644 --- a/tasks/ci.toml +++ b/tasks/ci.toml @@ -5,7 +5,7 @@ [build] description = "Build the whole project" -depends = ["build:rust:workspace", "build:docker", "build:python:wheel"] +depends = ["build:rust:workspace", "build:container", "build:python:wheel"] ["build:rust"] description = "Alias for build:rust:workspace" diff --git a/tasks/cluster.toml b/tasks/cluster.toml index 248e61119..a2242d08d 100644 --- a/tasks/cluster.toml +++ b/tasks/cluster.toml @@ -10,7 +10,7 @@ run = "tasks/scripts/cluster.sh" ["cluster:build:full"] description = "Build and deploy local k3s cluster with OpenShell" depends = [ - "build:docker:gateway", + "build:container:gateway", ] run = "tasks/scripts/cluster-bootstrap.sh build" hide = true diff --git a/tasks/container.toml b/tasks/container.toml new file mode 100644 index 000000000..666a642c8 --- /dev/null +++ b/tasks/container.toml @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Container image build tasks (engine-neutral: Docker or Podman) + +["build:container"] +description = "Build all container images" +depends = [ + "build:container:gateway", + "build:container:cluster", + "build:container:supervisor", +] +hide = true + +["build:container:ci"] +description = "Build the CI container image" +run = "tasks/scripts/container-build-ci.sh" +hide = true + +["build:container:gateway"] +description = "Build the gateway container image" +run = "tasks/scripts/container-build-image.sh gateway" +hide = true + +["build:container:supervisor"] +description = "Build the standalone supervisor container image (Ubuntu-based, for K8s pods)" +run = "tasks/scripts/container-build-image.sh supervisor" +hide = true + +["build:container:cluster"] +description = "Build the k3s cluster image (component images pulled at runtime from registry)" +run = "tasks/scripts/container-build-image.sh cluster" +hide = true + +["build:container:cluster:multiarch"] +description = "Build multi-arch cluster image and push to a registry" +run = "tasks/scripts/container-publish-multiarch.sh" +hide = true + +["container:cleanup"] +description = "Remove stale images, volumes, and build cache not used by the current cluster" +run = "scripts/container-cleanup.sh --force" + +["container:cleanup:dry-run"] +description = "Preview what container:cleanup would remove without deleting anything" +run = "scripts/container-cleanup.sh --dry-run" diff --git a/tasks/docker.toml b/tasks/docker.toml index 78796d868..350de672a 100644 --- a/tasks/docker.toml +++ b/tasks/docker.toml @@ -1,20 +1,17 @@ # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -# Docker image build tasks +# Backwards-compatible aliases: build:docker:* → build:container:* +# These exist so CI workflows and muscle memory keep working. ["build:docker"] -description = "Build all Docker images" -depends = [ - "build:docker:gateway", - "build:docker:cluster", - "build:docker:supervisor", -] +description = "Alias → build:container (all container images)" +depends = ["build:container"] hide = true ["build:docker:ci"] -description = "Build the CI Docker image" -run = "tasks/scripts/docker-build-ci.sh" +description = "Alias → build:container:ci" +depends = ["build:container:ci"] hide = true ["build:docker:prebuilt"] @@ -23,49 +20,49 @@ run = "tasks/scripts/stage-prebuilt-binaries.sh all" hide = true ["build:docker:gateway"] -description = "Build the gateway Docker image" -run = "tasks/scripts/docker-build-image.sh gateway" +description = "Alias → build:container:gateway" +depends = ["build:container:gateway"] hide = true ["build:docker:supervisor"] -description = "Build the supervisor image (FROM scratch, binary only)" -run = "tasks/scripts/docker-build-image.sh supervisor" +description = "Alias → build:container:supervisor" +depends = ["build:container:supervisor"] hide = true ["build:docker:cluster"] -description = "Build the k3s cluster image (component images pulled at runtime from registry)" -run = "tasks/scripts/docker-build-image.sh cluster" +description = "Alias → build:container:cluster" +depends = ["build:container:cluster"] +hide = true + +["build:docker:cluster:multiarch"] +description = "Alias → build:container:cluster:multiarch" +depends = ["build:container:cluster:multiarch"] hide = true ["docker:build:gateway"] -description = "Alias for build:docker:gateway" -depends = ["build:docker:gateway"] +description = "Alias → build:container:gateway" +depends = ["build:container:gateway"] hide = true ["docker:build:supervisor"] -description = "Alias for build:docker:supervisor" -depends = ["build:docker:supervisor"] +description = "Alias → build:container:supervisor" +depends = ["build:container:supervisor"] hide = true ["docker:build:cluster"] -description = "Alias for build:docker:cluster" -depends = ["build:docker:cluster"] -hide = true - -["build:docker:cluster:multiarch"] -description = "Build multi-arch cluster image and push to a registry" -run = "tasks/scripts/docker-publish-multiarch.sh" +description = "Alias → build:container:cluster" +depends = ["build:container:cluster"] hide = true ["docker:build:cluster:multiarch"] -description = "Alias for build:docker:cluster:multiarch" -depends = ["build:docker:cluster:multiarch"] +description = "Alias → build:container:cluster:multiarch" +depends = ["build:container:cluster:multiarch"] hide = true ["docker:cleanup"] -description = "Remove stale images, volumes, and build cache not used by the current cluster" -run = "scripts/docker-cleanup.sh --force" +description = "Alias → container:cleanup" +depends = ["container:cleanup"] ["docker:cleanup:dry-run"] -description = "Preview what docker:cleanup would remove without deleting anything" -run = "scripts/docker-cleanup.sh --dry-run" +description = "Alias → container:cleanup:dry-run" +depends = ["container:cleanup:dry-run"] diff --git a/tasks/python.toml b/tasks/python.toml index b95d96671..0d4054836 100644 --- a/tasks/python.toml +++ b/tasks/python.toml @@ -174,8 +174,10 @@ CARGO_TARGET_CACHE_SCOPE=$(printf '%s' "$CACHE_SCOPE_INPUT" | sha256_16_stdin) mkdir -p target/wheels +CONTAINERFILE=$(ce_resolve_containerfile deploy/docker python-wheels-macos) + ce build \ - -f deploy/docker/Dockerfile.python-wheels-macos \ + -f "${CONTAINERFILE}" \ --target wheels \ --build-arg "OSXCROSS_IMAGE=${OSXCROSS_IMAGE_REF}" \ --build-arg "CARGO_TARGET_CACHE_SCOPE=${CARGO_TARGET_CACHE_SCOPE}" \ diff --git a/tasks/scripts/cluster-bootstrap.sh b/tasks/scripts/cluster-bootstrap.sh index 7f4fcf175..a9e105d2f 100755 --- a/tasks/scripts/cluster-bootstrap.sh +++ b/tasks/scripts/cluster-bootstrap.sh @@ -240,12 +240,12 @@ fi # and entrypoint from the working tree. This ensures the k3s container # always starts with the correct chart version. if [ "${SKIP_CLUSTER_IMAGE_BUILD:-}" != "1" ]; then - tasks/scripts/docker-build-image.sh cluster + tasks/scripts/container-build-image.sh cluster fi # In fast/build modes, use the locally-built cluster image rather than the # remote distribution registry image. The local image is built by -# `docker-build-image.sh cluster` and contains the bundled Helm chart and +# `container-build-image.sh cluster` and contains the bundled Helm chart and # manifests from the current working tree. if [ -z "${OPENSHELL_CLUSTER_IMAGE:-}" ]; then export OPENSHELL_CLUSTER_IMAGE="openshell/cluster:${IMAGE_TAG}" diff --git a/tasks/scripts/cluster-deploy-fast.sh b/tasks/scripts/cluster-deploy-fast.sh index da3e6494f..0b490987d 100755 --- a/tasks/scripts/cluster-deploy-fast.sh +++ b/tasks/scripts/cluster-deploy-fast.sh @@ -152,7 +152,7 @@ matches_gateway() { Cargo.toml|Cargo.lock|proto/*|tasks/scripts/stage-prebuilt-binaries.sh) return 0 ;; - deploy/docker/Dockerfile.images|tasks/scripts/docker-build-image.sh) + deploy/docker/Dockerfile.images|deploy/docker/Containerfile.images|tasks/scripts/container-build-image.sh) return 0 ;; crates/openshell-core/*|crates/openshell-driver-kubernetes/*|crates/openshell-ocsf/*|crates/openshell-policy/*|crates/openshell-providers/*) @@ -173,7 +173,7 @@ matches_supervisor() { Cargo.toml|Cargo.lock|proto/*|tasks/scripts/stage-prebuilt-binaries.sh) return 0 ;; - deploy/docker/Dockerfile.images|tasks/scripts/docker-build-image.sh) + deploy/docker/Dockerfile.images|deploy/docker/Containerfile.images|tasks/scripts/container-build-image.sh) return 0 ;; crates/openshell-core/*|crates/openshell-policy/*|crates/openshell-router/*) @@ -210,13 +210,13 @@ compute_fingerprint() { # hashes. This ensures that committed changes (e.g. after `git pull` # or amend) are detected even when there are no uncommitted edits. local committed_trees="" - case "${component}" in - gateway) - committed_trees=$(git ls-tree HEAD Cargo.toml Cargo.lock proto/ deploy/docker/Dockerfile.images tasks/scripts/docker-build-image.sh tasks/scripts/stage-prebuilt-binaries.sh crates/openshell-core/ crates/openshell-driver-kubernetes/ crates/openshell-ocsf/ crates/openshell-policy/ crates/openshell-providers/ crates/openshell-router/ crates/openshell-server/ 2>/dev/null || true) - ;; - supervisor) - committed_trees=$(git ls-tree HEAD Cargo.toml Cargo.lock proto/ deploy/docker/Dockerfile.images tasks/scripts/docker-build-image.sh tasks/scripts/stage-prebuilt-binaries.sh crates/openshell-core/ crates/openshell-policy/ crates/openshell-router/ crates/openshell-sandbox/ 2>/dev/null || true) - ;; + case "${component}" in + gateway) + committed_trees=$(git ls-tree HEAD Cargo.toml Cargo.lock proto/ deploy/docker/cross-build.sh deploy/docker/Dockerfile.images deploy/docker/Containerfile.images tasks/scripts/container-build-image.sh crates/openshell-core/ crates/openshell-driver-kubernetes/ crates/openshell-ocsf/ crates/openshell-policy/ crates/openshell-providers/ crates/openshell-router/ crates/openshell-server/ 2>/dev/null || true) + ;; + supervisor) + committed_trees=$(git ls-tree HEAD Cargo.toml Cargo.lock proto/ deploy/docker/cross-build.sh deploy/docker/Dockerfile.images deploy/docker/Containerfile.images tasks/scripts/container-build-image.sh crates/openshell-core/ crates/openshell-policy/ crates/openshell-router/ crates/openshell-sandbox/ 2>/dev/null || true) + ;; helm) committed_trees=$(git ls-tree HEAD deploy/helm/openshell/ 2>/dev/null || true) ;; @@ -305,10 +305,10 @@ build_start=$(date +%s) declare -a built_components=() if [[ "${build_gateway}" == "1" ]]; then - tasks/scripts/docker-build-image.sh gateway + tasks/scripts/container-build-image.sh gateway fi -# Build the supervisor binary and docker cp it into the running k3s cluster. +# Build the supervisor binary and copy it into the running k3s cluster. # The binary lives at /opt/openshell/bin/openshell-sandbox on the node # filesystem and is mounted into sandbox pods via a hostPath volume. if [[ "${build_supervisor}" == "1" ]]; then @@ -340,19 +340,20 @@ if [[ "${build_supervisor}" == "1" ]]; then _cargo_version=$(uv run python tasks/scripts/release.py get-version --cargo 2>/dev/null || true) fi - # Only set DOCKER_PLATFORM when the cluster architecture differs from the - # local container engine architecture. Omitting it for native builds lets - # docker-build-image.sh pick the fast default builder. + # Only set CONTAINER_PLATFORM when actually cross-compiling. Omitting it + # for native builds lets container-build-image.sh pick the fast "docker" + # driver (same as gateway), which shares BuildKit cache mounts (sccache, + # cargo registry/target) and avoids docker-container IPC overhead. _platform_env=() if [[ "${CLUSTER_ARCH}" != "${HOST_ARCH}" ]]; then - _platform_env=(DOCKER_PLATFORM="linux/${CLUSTER_ARCH}") + _platform_env=(CONTAINER_PLATFORM="linux/${CLUSTER_ARCH}") fi env \ "${_platform_env[@]+"${_platform_env[@]}"}" \ - DOCKER_OUTPUT="type=local,dest=${SUPERVISOR_BUILD_DIR}" \ + CONTAINER_OUTPUT="type=local,dest=${SUPERVISOR_BUILD_DIR}" \ OPENSHELL_CARGO_VERSION="${_cargo_version}" \ - tasks/scripts/docker-build-image.sh supervisor-output + tasks/scripts/container-build-image.sh supervisor-output # Copy the built binary into the running k3s container ce exec "${CONTAINER_NAME}" mkdir -p /opt/openshell/bin diff --git a/tasks/scripts/cluster-push-component.sh b/tasks/scripts/cluster-push-component.sh index f94f83923..482f0ad22 100755 --- a/tasks/scripts/cluster-push-component.sh +++ b/tasks/scripts/cluster-push-component.sh @@ -52,7 +52,7 @@ done if [ -z "${resolved_source_image}" ]; then echo "Local image not found for ${component}:${IMAGE_TAG}, building..." - tasks/scripts/docker-build-image.sh "${component}" + tasks/scripts/container-build-image.sh "${component}" resolved_source_image="openshell/${component}:${IMAGE_TAG}" fi diff --git a/tasks/scripts/docker-build-ci.sh b/tasks/scripts/container-build-ci.sh similarity index 56% rename from tasks/scripts/docker-build-ci.sh rename to tasks/scripts/container-build-ci.sh index 56ff7148f..c73751356 100755 --- a/tasks/scripts/docker-build-ci.sh +++ b/tasks/scripts/container-build-ci.sh @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -# Build the CI Docker image (deploy/docker/Dockerfile.ci). +# Build the CI container image (deploy/docker/Dockerfile.ci or Containerfile.ci). # This is a standalone build, separate from the main image build graph. set -euo pipefail @@ -11,10 +11,17 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/container-engine.sh" +# Backwards-compatible env var fallbacks: accept CONTAINER_* or DOCKER_* +CONTAINER_BUILDER="${CONTAINER_BUILDER:-${DOCKER_BUILDER:-}}" +CONTAINER_PLATFORM="${CONTAINER_PLATFORM:-${DOCKER_PLATFORM:-}}" +CONTAINER_PUSH="${CONTAINER_PUSH:-${DOCKER_PUSH:-}}" + +CONTAINERFILE=$(ce_resolve_containerfile deploy/docker ci) + OUTPUT_ARGS=(--load) -if [[ "${DOCKER_PUSH:-}" == "1" ]]; then +if [[ "${CONTAINER_PUSH}" == "1" ]]; then OUTPUT_ARGS=(--push) -elif [[ "${DOCKER_PLATFORM:-}" == *","* ]]; then +elif [[ "${CONTAINER_PLATFORM}" == *","* ]]; then OUTPUT_ARGS=(--push) fi @@ -25,11 +32,11 @@ elif [[ -n "${GITHUB_TOKEN:-}" ]]; then SECRET_ARGS=(--secret id=MISE_GITHUB_TOKEN,env=GITHUB_TOKEN) fi -exec ce_build \ - ${DOCKER_BUILDER:+--builder ${DOCKER_BUILDER}} \ - ${DOCKER_PLATFORM:+--platform ${DOCKER_PLATFORM}} \ +ce_build \ + ${CONTAINER_BUILDER:+--builder ${CONTAINER_BUILDER}} \ + ${CONTAINER_PLATFORM:+--platform ${CONTAINER_PLATFORM}} \ ${SECRET_ARGS[@]+"${SECRET_ARGS[@]}"} \ - -f deploy/docker/Dockerfile.ci \ + -f "${CONTAINERFILE}" \ -t "openshell/ci:${IMAGE_TAG:-dev}" \ --provenance=false \ "$@" \ diff --git a/tasks/scripts/container-build-image.sh b/tasks/scripts/container-build-image.sh new file mode 100755 index 000000000..3bb3885bd --- /dev/null +++ b/tasks/scripts/container-build-image.sh @@ -0,0 +1,224 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/container-engine.sh" + +# Backwards-compatible env var fallbacks: accept CONTAINER_* or DOCKER_* +CONTAINER_BUILD_CACHE_DIR="${CONTAINER_BUILD_CACHE_DIR:-${DOCKER_BUILD_CACHE_DIR:-.cache/buildkit}}" +CONTAINER_BUILDER="${CONTAINER_BUILDER:-${DOCKER_BUILDER:-}}" +CONTAINER_PLATFORM="${CONTAINER_PLATFORM:-${DOCKER_PLATFORM:-}}" +CONTAINER_OUTPUT="${CONTAINER_OUTPUT:-${DOCKER_OUTPUT:-}}" +CONTAINER_PUSH="${CONTAINER_PUSH:-${DOCKER_PUSH:-}}" + +sha256_16() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{print substr($1, 1, 16)}' + else + shasum -a 256 "$1" | awk '{print substr($1, 1, 16)}' + fi +} + +sha256_16_stdin() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum | awk '{print substr($1, 1, 16)}' + else + shasum -a 256 | awk '{print substr($1, 1, 16)}' + fi +} + +detect_rust_scope() { + local dockerfile="$1" + local rust_from + rust_from=$(grep -E '^FROM --platform=\$BUILDPLATFORM rust:[^ ]+' "$dockerfile" | head -n1 | sed -E 's/^FROM --platform=\$BUILDPLATFORM rust:([^ ]+).*/\1/' || true) + if [[ -n "${rust_from}" ]]; then + echo "rust-${rust_from}" + return + fi + + if grep -q "rustup.rs" "$dockerfile"; then + echo "rustup-stable" + return + fi + + echo "no-rust" +} + +TARGET=${1:?"Usage: container-build-image.sh [extra-args...]"} +shift + +CONTAINERFILE=$(ce_resolve_containerfile deploy/docker images) + +IS_FINAL_IMAGE=0 +IMAGE_NAME="" +BUILD_TARGET="" +case "${TARGET}" in + gateway) + IS_FINAL_IMAGE=1 + IMAGE_NAME="openshell/gateway" + BUILD_TARGET="gateway" + ;; + supervisor) + IS_FINAL_IMAGE=1 + IMAGE_NAME="openshell/supervisor" + BUILD_TARGET="supervisor" + ;; + cluster) + IS_FINAL_IMAGE=1 + IMAGE_NAME="openshell/cluster" + BUILD_TARGET="cluster" + ;; + supervisor-builder) + BUILD_TARGET="supervisor-builder" + ;; + supervisor-output) + # Backward-compat alias: same as "supervisor". + IS_FINAL_IMAGE=1 + IMAGE_NAME="openshell/supervisor" + BUILD_TARGET="supervisor" + ;; + *) + echo "Error: unsupported target '${TARGET}'" >&2 + exit 1 + ;; +esac + +if [[ -n "${IMAGE_REGISTRY:-}" && "${IS_FINAL_IMAGE}" == "1" ]]; then + IMAGE_NAME="${IMAGE_REGISTRY}/${IMAGE_NAME#openshell/}" +fi + +IMAGE_TAG=${IMAGE_TAG:-dev} +CACHE_PATH="${CONTAINER_BUILD_CACHE_DIR}/images" +mkdir -p "${CACHE_PATH}" + +BUILDER_ARGS=() +if ce_is_docker; then + if [[ -n "${CONTAINER_BUILDER}" ]]; then + BUILDER_ARGS=(--builder "${CONTAINER_BUILDER}") + elif [[ -z "${CONTAINER_PLATFORM}" && -z "${CI:-}" ]]; then + _ctx=$(ce_context_name) + BUILDER_ARGS=(--builder "${_ctx}") + fi +fi + +CACHE_ARGS=() +if [[ -z "${CI:-}" ]]; then + if ce_is_docker; then + if ce_buildx_inspect ${BUILDER_ARGS[@]+"${BUILDER_ARGS[@]}"} 2>/dev/null | grep -q "Driver: docker-container"; then + CACHE_ARGS=( + --cache-from "type=local,src=${CACHE_PATH}" + --cache-to "type=local,dest=${CACHE_PATH},mode=max" + ) + fi + fi +fi + +SCCACHE_ARGS=() +if [[ -n "${SCCACHE_MEMCACHED_ENDPOINT:-}" ]]; then + SCCACHE_ARGS=(--build-arg "SCCACHE_MEMCACHED_ENDPOINT=${SCCACHE_MEMCACHED_ENDPOINT}") +fi + +VERSION_ARGS=() +if [[ -n "${OPENSHELL_CARGO_VERSION:-}" ]]; then + VERSION_ARGS=(--build-arg "OPENSHELL_CARGO_VERSION=${OPENSHELL_CARGO_VERSION}") +elif [[ -n "${CI:-}" ]]; then + CARGO_VERSION=$(uv run python tasks/scripts/release.py get-version --cargo 2>/dev/null || true) + if [[ -n "${CARGO_VERSION}" ]]; then + VERSION_ARGS=(--build-arg "OPENSHELL_CARGO_VERSION=${CARGO_VERSION}") + fi +fi + +LOCK_HASH=$(sha256_16 Cargo.lock) +RUST_SCOPE=${RUST_TOOLCHAIN_SCOPE:-$(detect_rust_scope "${CONTAINERFILE}")} +CACHE_SCOPE_INPUT="v2|shared|release|${LOCK_HASH}|${RUST_SCOPE}" +CARGO_TARGET_CACHE_SCOPE=$(printf '%s' "${CACHE_SCOPE_INPUT}" | sha256_16_stdin) + +# The cluster image embeds the packaged Helm chart. +if [[ "${TARGET}" == "cluster" ]]; then + mkdir -p deploy/docker/.build/charts + helm package deploy/helm/openshell -d deploy/docker/.build/charts/ >/dev/null +fi + +K3S_ARGS=() +if [[ "${TARGET}" == "cluster" && -n "${K3S_VERSION:-}" ]]; then + K3S_ARGS=(--build-arg "K3S_VERSION=${K3S_VERSION}") +fi + +# CI builds use codegen-units=1 for maximum optimization; local builds omit +# the arg so cargo uses the Cargo.toml default (parallel codegen, fast links). +CODEGEN_ARGS=() +if [[ -n "${CI:-}" ]]; then + CODEGEN_ARGS=(--build-arg "CARGO_CODEGEN_UNITS=1") +fi + +# OS-128 Phase 4: opt in to consuming pre-built Rust binaries instead of +# compiling inside Docker. Default path (`build`) is unchanged. When +# USE_PREBUILT_BINARIES=true, the Dockerfile's BINARY_SOURCE=prebuilt stages +# are selected, which COPY from deploy/docker/.build/prebuilt-binaries// +# in the build context. Callers must stage the binaries before invoking. +BINARY_SOURCE_ARGS=() +if [[ "${USE_PREBUILT_BINARIES:-}" == "true" ]]; then + case "${TARGET}" in + gateway|supervisor|cluster|supervisor-output) + if [[ ! -d deploy/docker/.build/prebuilt-binaries ]]; then + echo "Error: USE_PREBUILT_BINARIES=true but deploy/docker/.build/prebuilt-binaries/ does not exist" >&2 + echo " Stage binaries at deploy/docker/.build/prebuilt-binaries//openshell-{gateway,sandbox}" >&2 + exit 1 + fi + BINARY_SOURCE_ARGS=(--build-arg "BINARY_SOURCE=prebuilt") + ;; + esac +fi + +TAG_ARGS=() +if [[ "${IS_FINAL_IMAGE}" == "1" ]]; then + TAG_ARGS=(-t "${IMAGE_NAME}:${IMAGE_TAG}") +fi + +OUTPUT_ARGS=() +if [[ -n "${CONTAINER_OUTPUT}" ]]; then + OUTPUT_ARGS=(--output "${CONTAINER_OUTPUT}") +elif [[ "${IS_FINAL_IMAGE}" == "1" ]]; then + if [[ "${CONTAINER_PUSH}" == "1" ]]; then + OUTPUT_ARGS=(--push) + elif [[ "${CONTAINER_PLATFORM}" == *","* ]]; then + OUTPUT_ARGS=(--push) + else + OUTPUT_ARGS=(--load) + fi +else + echo "Error: CONTAINER_OUTPUT must be set when building target '${TARGET}'" >&2 + exit 1 +fi + +# Default to dev-settings so local builds include test-only settings +# (dummy_bool, dummy_int) that e2e tests depend on, matching CI behaviour. +EXTRA_CARGO_FEATURES="${EXTRA_CARGO_FEATURES:-openshell-core/dev-settings}" + +FEATURE_ARGS=() +if [[ -n "${EXTRA_CARGO_FEATURES}" ]]; then + FEATURE_ARGS=(--build-arg "EXTRA_CARGO_FEATURES=${EXTRA_CARGO_FEATURES}") +fi + +ce_build \ + ${BUILDER_ARGS[@]+"${BUILDER_ARGS[@]}"} \ + ${CONTAINER_PLATFORM:+--platform ${CONTAINER_PLATFORM}} \ + ${CACHE_ARGS[@]+"${CACHE_ARGS[@]}"} \ + ${SCCACHE_ARGS[@]+"${SCCACHE_ARGS[@]}"} \ + ${VERSION_ARGS[@]+"${VERSION_ARGS[@]}"} \ + ${K3S_ARGS[@]+"${K3S_ARGS[@]}"} \ + ${CODEGEN_ARGS[@]+"${CODEGEN_ARGS[@]}"} \ + ${BINARY_SOURCE_ARGS[@]+"${BINARY_SOURCE_ARGS[@]}"} \ + ${FEATURE_ARGS[@]+"${FEATURE_ARGS[@]}"} \ + --build-arg "CARGO_TARGET_CACHE_SCOPE=${CARGO_TARGET_CACHE_SCOPE}" \ + -f "${CONTAINERFILE}" \ + --target "${BUILD_TARGET}" \ + ${TAG_ARGS[@]+"${TAG_ARGS[@]}"} \ + --provenance=false \ + "$@" \ + ${OUTPUT_ARGS[@]+"${OUTPUT_ARGS[@]}"} \ + . diff --git a/tasks/scripts/container-engine.sh b/tasks/scripts/container-engine.sh index 2cb0dd7ff..4cf37bdb9 100755 --- a/tasks/scripts/container-engine.sh +++ b/tasks/scripts/container-engine.sh @@ -270,7 +270,7 @@ ce_imagetools_create() { # Podman fallback: parse -t and the trailing source image, then # use skopeo or podman tag. This is a best-effort shim for simple # re-tagging; full multi-arch manifest manipulation should use the - # podman-native code path in docker-publish-multiarch.sh. + # podman-native code path in container-publish-multiarch.sh. # # Argument parsing uses a sentinel ("__next__") to capture the value # that follows a two-token -t / --tag flag. --prefer-index is accepted @@ -305,6 +305,32 @@ ce_imagetools_create() { fi } +# --------------------------------------------------------------------------- +# ce_resolve_containerfile — find the container build file for a given target. +# +# Probes for Containerfile.{suffix} first (Podman/OCI convention), then +# Dockerfile.{suffix} (Docker convention). Returns the first match. +# +# Usage: ce_resolve_containerfile +# dir — directory containing the build file (e.g. deploy/docker) +# suffix — file suffix (e.g. images, ci, python-wheels-macos) +# +# Prints the resolved path on stdout. Returns 1 if neither file exists. +# --------------------------------------------------------------------------- +ce_resolve_containerfile() { + local dir="${1:?Usage: ce_resolve_containerfile }" + local suffix="${2:?Usage: ce_resolve_containerfile }" + + if [[ -f "${dir}/Containerfile.${suffix}" ]]; then + echo "${dir}/Containerfile.${suffix}" + elif [[ -f "${dir}/Dockerfile.${suffix}" ]]; then + echo "${dir}/Dockerfile.${suffix}" + else + echo "Error: no Containerfile.${suffix} or Dockerfile.${suffix} found in ${dir}" >&2 + return 1 + fi +} + # --------------------------------------------------------------------------- # Log the detected engine so developers always know which tool is active. # Emitted once per script invocation (the double-source guard at the top diff --git a/tasks/scripts/docker-publish-multiarch.sh b/tasks/scripts/container-publish-multiarch.sh similarity index 79% rename from tasks/scripts/docker-publish-multiarch.sh rename to tasks/scripts/container-publish-multiarch.sh index 18b7ab889..5cf88d759 100755 --- a/tasks/scripts/docker-publish-multiarch.sh +++ b/tasks/scripts/container-publish-multiarch.sh @@ -4,23 +4,29 @@ # SPDX-License-Identifier: Apache-2.0 # Build multi-arch gateway + cluster images and push to a container registry. -# Requires DOCKER_REGISTRY to be set (e.g. ghcr.io/myorg). +# Requires CONTAINER_REGISTRY (or DOCKER_REGISTRY) to be set (e.g. ghcr.io/myorg). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/container-engine.sh" -REGISTRY=${DOCKER_REGISTRY:?Set DOCKER_REGISTRY to push multi-arch images (e.g. ghcr.io/myorg)} +# Backwards-compatible env var fallbacks: accept CONTAINER_* or DOCKER_* +CONTAINER_REGISTRY="${CONTAINER_REGISTRY:-${DOCKER_REGISTRY:-}}" +CONTAINER_PLATFORMS="${CONTAINER_PLATFORMS:-${DOCKER_PLATFORMS:-linux/amd64,linux/arm64}}" +CONTAINER_BUILDER="${CONTAINER_BUILDER:-${DOCKER_BUILDER:-}}" +CONTAINER_PUSH="${CONTAINER_PUSH:-${DOCKER_PUSH:-}}" +EXTRA_CONTAINER_TAGS="${EXTRA_CONTAINER_TAGS:-${EXTRA_DOCKER_TAGS:-}}" + +REGISTRY=${CONTAINER_REGISTRY:?Set CONTAINER_REGISTRY (or DOCKER_REGISTRY) to push multi-arch images (e.g. ghcr.io/myorg)} IMAGE_TAG=${IMAGE_TAG:-dev} -PLATFORMS=${DOCKER_PLATFORMS:-linux/amd64,linux/arm64} +PLATFORMS=${CONTAINER_PLATFORMS} TAG_LATEST=${TAG_LATEST:-false} -EXTRA_DOCKER_TAGS_RAW=${EXTRA_DOCKER_TAGS:-} EXTRA_TAGS=() -if [[ -n "${EXTRA_DOCKER_TAGS_RAW}" ]]; then - EXTRA_DOCKER_TAGS_RAW=${EXTRA_DOCKER_TAGS_RAW//,/ } - for tag in ${EXTRA_DOCKER_TAGS_RAW}; do +if [[ -n "${EXTRA_CONTAINER_TAGS}" ]]; then + EXTRA_TAGS_RAW=${EXTRA_CONTAINER_TAGS//,/ } + for tag in ${EXTRA_TAGS_RAW}; do [[ -n "${tag}" ]] && EXTRA_TAGS+=("${tag}") done fi @@ -29,7 +35,7 @@ fi # Docker path: use buildx builders + imagetools for multi-arch # --------------------------------------------------------------------------- _publish_multiarch_docker() { - BUILDER_NAME=${DOCKER_BUILDER:-multiarch} + BUILDER_NAME=${CONTAINER_BUILDER:-multiarch} if ce buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then echo "Using existing buildx builder: ${BUILDER_NAME}" ce buildx use "${BUILDER_NAME}" @@ -38,17 +44,17 @@ _publish_multiarch_docker() { ce buildx create --name "${BUILDER_NAME}" --use --bootstrap fi - export DOCKER_BUILDER="${BUILDER_NAME}" - export DOCKER_PLATFORM="${PLATFORMS}" - export DOCKER_PUSH=1 + export CONTAINER_BUILDER="${BUILDER_NAME}" + export CONTAINER_PLATFORM="${PLATFORMS}" + export CONTAINER_PUSH=1 export IMAGE_REGISTRY="${REGISTRY}" echo "Building multi-arch gateway image..." - tasks/scripts/docker-build-image.sh gateway + tasks/scripts/container-build-image.sh gateway echo echo "Building multi-arch cluster image..." - tasks/scripts/docker-build-image.sh cluster + tasks/scripts/container-build-image.sh cluster TAGS_TO_APPLY=("${EXTRA_TAGS[@]}") if [[ "${TAG_LATEST}" == "true" ]]; then @@ -91,12 +97,12 @@ _publish_multiarch_podman() { for platform in "${PLATFORM_LIST[@]}"; do echo " Building ${component} for ${platform}..." # Build for each platform and add to the manifest list. - # docker-build-image.sh sources container-engine.sh itself, + # container-build-image.sh sources container-engine.sh itself, # so ce_build is used internally. - DOCKER_PLATFORM="${platform}" \ - DOCKER_PUSH="" \ + CONTAINER_PLATFORM="${platform}" \ + CONTAINER_PUSH="" \ IMAGE_TAG="${IMAGE_TAG}" \ - tasks/scripts/docker-build-image.sh "${component}" + tasks/scripts/container-build-image.sh "${component}" # Tag with a platform-specific suffix for manifest assembly. local platform_tag="${IMAGE_TAG}-${platform//\//-}" diff --git a/tasks/scripts/docker-build-image.sh b/tasks/scripts/docker-build-image.sh deleted file mode 100755 index 997a631ad..000000000 --- a/tasks/scripts/docker-build-image.sh +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/container-engine.sh" - -normalize_arch() { - case "$1" in - x86_64|amd64) echo "amd64" ;; - aarch64|arm64) echo "arm64" ;; - *) echo "$1" ;; - esac -} - -prebuilt_arches() { - if [[ -n "${DOCKER_PLATFORM:-}" ]]; then - local raw_platforms=${DOCKER_PLATFORM//[[:space:]]/} - local platform - IFS=',' read -r -a platforms <<< "${raw_platforms}" - for platform in "${platforms[@]}"; do - case "${platform}" in - linux/amd64) echo "amd64" ;; - linux/arm64) echo "arm64" ;; - *) - echo "Error: unsupported DOCKER_PLATFORM '${platform}'" >&2 - echo "Supported platforms: linux/amd64, linux/arm64" >&2 - exit 1 - ;; - esac - done - return - fi - - normalize_arch "$(ce_info_arch)" -} - -required_prebuilt_binaries() { - case "$1" in - gateway) - echo "openshell-gateway" - ;; - supervisor|cluster|supervisor-sideload|supervisor-output) - echo "openshell-sandbox" - ;; - esac -} - -missing_prebuilt_paths() { - local target=$1 - local arch - local binary - local path - - mapfile -t arches < <(prebuilt_arches) - read -r -a binaries <<< "$(required_prebuilt_binaries "${target}")" - - for arch in "${arches[@]}"; do - for binary in "${binaries[@]}"; do - path="deploy/docker/.build/prebuilt-binaries/${arch}/${binary}" - if [[ ! -f "${path}" ]]; then - echo "${path}" - fi - done - done -} - -ensure_prebuilt_binaries() { - local target=$1 - local missing - local arch - - if [[ -z "${CI:-}" && "${PREBUILT_AUTO_STAGE:-1}" != "0" ]]; then - echo "Staging prebuilt Rust binaries for Docker target '${target}'..." - mapfile -t arches < <(prebuilt_arches) - for arch in "${arches[@]}"; do - PREBUILT_ARCH="${arch}" "${SCRIPT_DIR}/stage-prebuilt-binaries.sh" "${target}" - done - fi - - missing="$(missing_prebuilt_paths "${target}")" - if [[ -n "${missing}" ]]; then - echo "Error: missing prebuilt Rust binaries required by Docker target '${target}':" >&2 - printf ' %s\n' ${missing} >&2 - echo "Stage binaries at deploy/docker/.build/prebuilt-binaries// before building." >&2 - exit 1 - fi -} - -TARGET=${1:?"Usage: docker-build-image.sh [extra-args...]"} -shift - -DOCKERFILE="deploy/docker/Dockerfile.images" -if [[ ! -f "${DOCKERFILE}" ]]; then - echo "Error: Dockerfile not found: ${DOCKERFILE}" >&2 - exit 1 -fi - -IS_FINAL_IMAGE=0 -IMAGE_NAME="" -DOCKER_TARGET="" -case "${TARGET}" in - gateway) - IS_FINAL_IMAGE=1 - IMAGE_NAME="openshell/gateway" - DOCKER_TARGET="gateway" - ;; - supervisor) - IS_FINAL_IMAGE=1 - IMAGE_NAME="openshell/supervisor" - DOCKER_TARGET="supervisor" - ;; - cluster) - IS_FINAL_IMAGE=1 - IMAGE_NAME="openshell/cluster" - DOCKER_TARGET="cluster" - ;; - supervisor-builder) - DOCKER_TARGET="supervisor-builder" - ;; - supervisor-output) - # Backward-compat alias: same as "supervisor". - IS_FINAL_IMAGE=1 - IMAGE_NAME="openshell/supervisor" - DOCKER_TARGET="supervisor" - ;; - *) - echo "Error: unsupported target '${TARGET}'" >&2 - exit 1 - ;; -esac - -if [[ -n "${IMAGE_REGISTRY:-}" && "${IS_FINAL_IMAGE}" == "1" ]]; then - IMAGE_NAME="${IMAGE_REGISTRY}/${IMAGE_NAME#openshell/}" -fi - -IMAGE_TAG=${IMAGE_TAG:-dev} -DOCKER_BUILD_CACHE_DIR=${DOCKER_BUILD_CACHE_DIR:-.cache/buildkit} -CACHE_PATH="${DOCKER_BUILD_CACHE_DIR}/images" -mkdir -p "${CACHE_PATH}" - -BUILDER_ARGS=() -if ce_is_docker; then - if [[ -n "${DOCKER_BUILDER:-}" ]]; then - BUILDER_ARGS=(--builder "${DOCKER_BUILDER}") - elif [[ -z "${DOCKER_PLATFORM:-}" && -z "${CI:-}" ]]; then - _ctx=$(ce_context_name) - BUILDER_ARGS=(--builder "${_ctx}") - fi -fi - -CACHE_ARGS=() -if [[ -z "${CI:-}" ]]; then - if ce_is_docker; then - if ce_buildx_inspect ${BUILDER_ARGS[@]+"${BUILDER_ARGS[@]}"} 2>/dev/null | grep -q "Driver: docker-container"; then - CACHE_ARGS=( - --cache-from "type=local,src=${CACHE_PATH}" - --cache-to "type=local,dest=${CACHE_PATH},mode=max" - ) - fi - fi -fi - -# The cluster image embeds the packaged Helm chart. -if [[ "${TARGET}" == "cluster" ]]; then - mkdir -p deploy/docker/.build/charts - helm package deploy/helm/openshell -d deploy/docker/.build/charts/ >/dev/null -fi - -K3S_ARGS=() -if [[ "${TARGET}" == "cluster" && -n "${K3S_VERSION:-}" ]]; then - K3S_ARGS=(--build-arg "K3S_VERSION=${K3S_VERSION}") -fi - -ensure_prebuilt_binaries "${TARGET}" - -TAG_ARGS=() -if [[ "${IS_FINAL_IMAGE}" == "1" ]]; then - TAG_ARGS=(-t "${IMAGE_NAME}:${IMAGE_TAG}") -fi - -OUTPUT_ARGS=() -if [[ -n "${DOCKER_OUTPUT:-}" ]]; then - OUTPUT_ARGS=(--output "${DOCKER_OUTPUT}") -elif [[ "${IS_FINAL_IMAGE}" == "1" ]]; then - if [[ "${DOCKER_PUSH:-}" == "1" ]]; then - OUTPUT_ARGS=(--push) - elif [[ "${DOCKER_PLATFORM:-}" == *","* ]]; then - OUTPUT_ARGS=(--push) - else - OUTPUT_ARGS=(--load) - fi -else - echo "Error: DOCKER_OUTPUT must be set when building target '${TARGET}'" >&2 - exit 1 -fi - -ce_build \ - ${BUILDER_ARGS[@]+"${BUILDER_ARGS[@]}"} \ - ${DOCKER_PLATFORM:+--platform ${DOCKER_PLATFORM}} \ - ${CACHE_ARGS[@]+"${CACHE_ARGS[@]}"} \ - ${K3S_ARGS[@]+"${K3S_ARGS[@]}"} \ - -f "${DOCKERFILE}" \ - --target "${DOCKER_TARGET}" \ - ${TAG_ARGS[@]+"${TAG_ARGS[@]}"} \ - --provenance=false \ - "$@" \ - ${OUTPUT_ARGS[@]+"${OUTPUT_ARGS[@]}"} \ - . diff --git a/tasks/scripts/gateway-docker.sh b/tasks/scripts/gateway-docker.sh index 269933c96..fc881d5b4 100644 --- a/tasks/scripts/gateway-docker.sh +++ b/tasks/scripts/gateway-docker.sh @@ -146,7 +146,7 @@ else CONTAINER_ENGINE=docker \ DOCKER_PLATFORM="linux/${DAEMON_ARCH}" \ DOCKER_OUTPUT="type=local,dest=${SUPERVISOR_OUT_DIR}" \ - bash "${ROOT}/tasks/scripts/docker-build-image.sh" supervisor-output + bash "${ROOT}/tasks/scripts/container-build-image.sh" supervisor-output fi if [[ ! -f "${SUPERVISOR_BIN}" ]]; then diff --git a/tasks/scripts/gateway.sh b/tasks/scripts/gateway.sh index 608eabbf2..7627adac2 100644 --- a/tasks/scripts/gateway.sh +++ b/tasks/scripts/gateway.sh @@ -38,7 +38,7 @@ Environment: OPENSHELL_SERVER_PORT Gateway port. Defaults to 8080 for Kubernetes, 18080 for Podman/Docker, and 18081 for VM. OPENSHELL_SUPERVISOR_IMAGE - Podman supervisor sideload image. Defaults to + Podman supervisor image. Defaults to openshell/supervisor:dev and is built on demand. Docker and VM runs delegate to gateway:docker and gateway:vm setup scripts. @@ -175,9 +175,9 @@ ensure_podman_supervisor_image() { exit 1 fi - echo "Building Podman supervisor sideload image (${supervisor_image})..." + echo "Building Podman supervisor image (${supervisor_image})..." require_mise - CONTAINER_ENGINE=podman IMAGE_TAG=dev mise run build:docker:supervisor-sideload + CONTAINER_ENGINE=podman IMAGE_TAG=dev mise run build:container:supervisor if ! podman image exists "${supervisor_image}" >/dev/null 2>&1; then echo "ERROR: expected supervisor image '${supervisor_image}' after build" >&2