ci: centralize docker build wrapper

This commit is contained in:
Peter Steinberger
2026-04-26 22:14:33 +01:00
parent 11c46893f4
commit 306cfe42b5
13 changed files with 115 additions and 40 deletions

View File

@@ -2,6 +2,7 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
COMPOSE_FILE="$ROOT_DIR/docker-compose.yml"
EXTRA_COMPOSE_FILE="$ROOT_DIR/docker-compose.extra.yml"
IMAGE_NAME="${OPENCLAW_IMAGE:-openclaw:local}"
@@ -27,7 +28,7 @@ require_cmd() {
run_docker_build() {
# Dockerfile uses BuildKit-only syntax (RUN --mount=type=cache). Force
# BuildKit so hosts defaulting to the legacy builder do not fail.
DOCKER_BUILDKIT=1 docker build "$@"
docker_build_exec "$@"
}
is_truthy_value() {

View File

@@ -39,7 +39,7 @@ RUN apt-get update \\
USER appuser
EOF
echo "Building Docker image: $IMAGE_NAME"
run_logged browser-cdp-snapshot-build docker build -t "$IMAGE_NAME" -f "$build_dir/Dockerfile" "$build_dir"
docker_build_run browser-cdp-snapshot-build -t "$IMAGE_NAME" -f "$build_dir/Dockerfile" "$build_dir"
fi
echo "Starting browser CDP snapshot container..."

View File

@@ -2,7 +2,7 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
source "$ROOT_DIR/scripts/lib/docker-e2e-logs.sh"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
IMAGE_NAME="${OPENCLAW_QR_SMOKE_IMAGE:-openclaw-qr-smoke}"
DOCKER_BUILD_ARGS=()
@@ -15,16 +15,11 @@ if [[ "${OPENCLAW_QR_SMOKE_FORCE_INSTALL:-0}" == "1" ]]; then
fi
echo "Building Docker image..."
DOCKER_BUILD_CMD=(docker build)
if ((${#DOCKER_BUILD_ARGS[@]} > 0)); then
DOCKER_BUILD_CMD+=("${DOCKER_BUILD_ARGS[@]}")
fi
DOCKER_BUILD_CMD+=(
-t "$IMAGE_NAME"
-f "$ROOT_DIR/scripts/e2e/Dockerfile.qr-import"
docker_build_run qr-import-build \
"${DOCKER_BUILD_ARGS[@]}" \
-t "$IMAGE_NAME" \
-f "$ROOT_DIR/scripts/e2e/Dockerfile.qr-import" \
"$ROOT_DIR"
)
run_logged qr-import-build "${DOCKER_BUILD_CMD[@]}"
echo "Running qrcode-tui import smoke..."
run_logged qr-import-run docker run --rm -t "$IMAGE_NAME" node -e "import('@vincentkoc/qrcode-tui').then(async (m)=>{process.stdout.write(await m.renderTerminal('qr-smoke',{small:true}))})"

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
DOCKER_BUILD_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if ! declare -F run_logged >/dev/null 2>&1; then
source "$DOCKER_BUILD_LIB_DIR/docker-e2e-logs.sh"
fi
docker_build_exec() {
local build_cmd=(docker build)
if [ "${OPENCLAW_DOCKER_BUILD_USE_BUILDX:-0}" = "1" ]; then
build_cmd=(docker buildx build --load)
if [ -n "${OPENCLAW_DOCKER_BUILD_CACHE_FROM:-}" ]; then
build_cmd+=(--cache-from "${OPENCLAW_DOCKER_BUILD_CACHE_FROM}")
fi
if [ -n "${OPENCLAW_DOCKER_BUILD_CACHE_TO:-}" ]; then
build_cmd+=(--cache-to "${OPENCLAW_DOCKER_BUILD_CACHE_TO}")
fi
fi
env DOCKER_BUILDKIT=1 "${build_cmd[@]}" "$@"
}
docker_build_run() {
local label="$1"
shift
local build_cmd=(docker build)
if [ "${OPENCLAW_DOCKER_BUILD_USE_BUILDX:-0}" = "1" ]; then
build_cmd=(docker buildx build --load)
if [ -n "${OPENCLAW_DOCKER_BUILD_CACHE_FROM:-}" ]; then
build_cmd+=(--cache-from "${OPENCLAW_DOCKER_BUILD_CACHE_FROM}")
fi
if [ -n "${OPENCLAW_DOCKER_BUILD_CACHE_TO:-}" ]; then
build_cmd+=(--cache-to "${OPENCLAW_DOCKER_BUILD_CACHE_TO}")
fi
fi
run_logged "$label" env DOCKER_BUILDKIT=1 "${build_cmd[@]}" "$@"
}

View File

@@ -4,6 +4,7 @@ DOCKER_E2E_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="${ROOT_DIR:-$(cd "$DOCKER_E2E_LIB_DIR/../.." && pwd)}"
source "$DOCKER_E2E_LIB_DIR/docker-e2e-logs.sh"
source "$DOCKER_E2E_LIB_DIR/docker-build.sh"
docker_e2e_resolve_image() {
local default_image="$1"
@@ -48,10 +49,10 @@ docker_e2e_build_or_reuse() {
fi
echo "Building Docker image: $image_name"
local build_cmd=(docker build)
local build_args=()
if [ -n "$target" ]; then
build_cmd+=(--target "$target")
build_args+=(--target "$target")
fi
build_cmd+=(-t "$image_name" -f "$dockerfile" "$context")
run_logged "$label-build" "${build_cmd[@]}"
build_args+=(-t "$image_name" -f "$dockerfile" "$context")
docker_build_run "$label-build" "${build_args[@]}"
}

View File

@@ -1,7 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
IMAGE_NAME="openclaw-sandbox-browser:bookworm-slim"
docker build -t "${IMAGE_NAME}" -f Dockerfile.sandbox-browser .
docker_build_exec -t "${IMAGE_NAME}" -f "$ROOT_DIR/Dockerfile.sandbox-browser" "$ROOT_DIR"
echo "Built ${IMAGE_NAME}"

View File

@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
BASE_IMAGE="${BASE_IMAGE:-openclaw-sandbox:bookworm-slim}"
TARGET_IMAGE="${TARGET_IMAGE:-openclaw-sandbox-common:bookworm-slim}"
PACKAGES="${PACKAGES:-curl wget jq coreutils grep nodejs npm python3 git ca-certificates golang-go rustc cargo unzip pkg-config libasound2-dev build-essential file}"
@@ -17,25 +20,14 @@ OPENCLAW_DOCKER_BUILD_CACHE_TO="${OPENCLAW_DOCKER_BUILD_CACHE_TO:-}"
if ! docker image inspect "${BASE_IMAGE}" >/dev/null 2>&1; then
echo "Base image missing: ${BASE_IMAGE}"
echo "Building base image via scripts/sandbox-setup.sh..."
scripts/sandbox-setup.sh
"$ROOT_DIR/scripts/sandbox-setup.sh"
fi
echo "Building ${TARGET_IMAGE} with: ${PACKAGES}"
build_cmd=(docker build)
if [ "${OPENCLAW_DOCKER_BUILD_USE_BUILDX}" = "1" ]; then
build_cmd=(docker buildx build --load)
if [ -n "${OPENCLAW_DOCKER_BUILD_CACHE_FROM}" ]; then
build_cmd+=(--cache-from "${OPENCLAW_DOCKER_BUILD_CACHE_FROM}")
fi
if [ -n "${OPENCLAW_DOCKER_BUILD_CACHE_TO}" ]; then
build_cmd+=(--cache-to "${OPENCLAW_DOCKER_BUILD_CACHE_TO}")
fi
fi
"${build_cmd[@]}" \
docker_build_exec \
-t "${TARGET_IMAGE}" \
-f Dockerfile.sandbox-common \
-f "$ROOT_DIR/Dockerfile.sandbox-common" \
--build-arg BASE_IMAGE="${BASE_IMAGE}" \
--build-arg PACKAGES="${PACKAGES}" \
--build-arg INSTALL_PNPM="${INSTALL_PNPM}" \
@@ -44,7 +36,7 @@ fi
--build-arg INSTALL_BREW="${INSTALL_BREW}" \
--build-arg BREW_INSTALL_DIR="${BREW_INSTALL_DIR}" \
--build-arg FINAL_USER="${FINAL_USER}" \
.
"$ROOT_DIR"
cat <<NOTE
Built ${TARGET_IMAGE}.

View File

@@ -1,7 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
IMAGE_NAME="openclaw-sandbox:bookworm-slim"
docker build -t "${IMAGE_NAME}" -f Dockerfile.sandbox .
docker_build_exec -t "${IMAGE_NAME}" -f "$ROOT_DIR/Dockerfile.sandbox" "$ROOT_DIR"
echo "Built ${IMAGE_NAME}"

View File

@@ -2,12 +2,12 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$ROOT_DIR/scripts/lib/docker-e2e-logs.sh"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
IMAGE_NAME="${OPENCLAW_CLEANUP_SMOKE_IMAGE:-openclaw-cleanup-smoke:local}"
PLATFORM="${OPENCLAW_CLEANUP_SMOKE_PLATFORM:-linux/amd64}"
echo "==> Build image: $IMAGE_NAME"
run_logged cleanup-build docker build \
docker_build_run cleanup-build \
-t "$IMAGE_NAME" \
-f "$ROOT_DIR/scripts/docker/cleanup-smoke/Dockerfile" \
"$ROOT_DIR"

View File

@@ -4,6 +4,7 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=./docker/install-sh-common/version-parse.sh
source "$ROOT_DIR/scripts/docker/install-sh-common/version-parse.sh"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
resolve_default_smoke_platform() {
local host_os
@@ -358,7 +359,7 @@ if [[ "$SKIP_SMOKE_IMAGE_BUILD" == "1" ]]; then
echo "==> Reuse prebuilt smoke image: $SMOKE_IMAGE"
else
echo "==> Build smoke image (upgrade, root, ${SMOKE_PLATFORM}): $SMOKE_IMAGE"
docker build \
docker_build_run install-smoke-build \
--platform "$SMOKE_PLATFORM" \
-t "$SMOKE_IMAGE" \
-f "$ROOT_DIR/scripts/docker/install-sh-smoke/Dockerfile" \
@@ -441,7 +442,7 @@ else
echo "==> Reuse prebuilt non-root image: $NONROOT_IMAGE"
else
echo "==> Build non-root image (${NONROOT_PLATFORM}): $NONROOT_IMAGE"
docker build \
docker_build_run install-nonroot-build \
--platform "$NONROOT_PLATFORM" \
-t "$NONROOT_IMAGE" \
-f "$ROOT_DIR/scripts/docker/install-sh-nonroot/Dockerfile" \

View File

@@ -2,6 +2,7 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
IMAGE_NAME="${OPENCLAW_INSTALL_E2E_IMAGE:-openclaw-install-e2e:local}"
INSTALL_URL="${OPENCLAW_INSTALL_URL:-https://openclaw.bot/install.sh}"
@@ -11,7 +12,7 @@ ANTHROPIC_API_TOKEN="${ANTHROPIC_API_TOKEN:-}"
OPENCLAW_E2E_MODELS="${OPENCLAW_E2E_MODELS:-}"
echo "==> Build image: $IMAGE_NAME"
docker build \
docker_build_run install-e2e-build \
-t "$IMAGE_NAME" \
-f "$ROOT_DIR/scripts/docker/install-sh-e2e/Dockerfile" \
"$ROOT_DIR/scripts/docker"

View File

@@ -2,7 +2,7 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$ROOT_DIR/scripts/lib/docker-e2e-logs.sh"
source "$ROOT_DIR/scripts/lib/docker-build.sh"
IMAGE_NAME="${OPENCLAW_IMAGE:-openclaw:local}"
LIVE_IMAGE_NAME="${OPENCLAW_LIVE_IMAGE:-${IMAGE_NAME}-live}"
DOCKER_BUILD_EXTENSIONS="${OPENCLAW_DOCKER_BUILD_EXTENSIONS:-${OPENCLAW_EXTENSIONS:-}}"
@@ -27,4 +27,4 @@ fi
echo "==> Build live-test image: $LIVE_IMAGE_NAME (target=build)"
echo "==> Bundled plugin deps: ${DOCKER_BUILD_EXTENSIONS}"
run_logged live-build docker build "${DOCKER_BUILD_ARGS[@]}" --target build -t "$LIVE_IMAGE_NAME" -f "$ROOT_DIR/Dockerfile" "$ROOT_DIR"
docker_build_run live-build "${DOCKER_BUILD_ARGS[@]}" --target build -t "$LIVE_IMAGE_NAME" -f "$ROOT_DIR/Dockerfile" "$ROOT_DIR"

View File

@@ -0,0 +1,38 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const HELPER_PATH = "scripts/lib/docker-build.sh";
const CENTRALIZED_BUILD_SCRIPTS = [
"scripts/docker/setup.sh",
"scripts/e2e/browser-cdp-snapshot-docker.sh",
"scripts/e2e/qr-import-docker.sh",
"scripts/lib/docker-e2e-image.sh",
"scripts/sandbox-browser-setup.sh",
"scripts/sandbox-common-setup.sh",
"scripts/sandbox-setup.sh",
"scripts/test-cleanup-docker.sh",
"scripts/test-install-sh-docker.sh",
"scripts/test-install-sh-e2e-docker.sh",
"scripts/test-live-build-docker.sh",
] as const;
describe("docker build helper", () => {
it("forces BuildKit for centralized Docker builds", () => {
const helper = readFileSync(HELPER_PATH, "utf8");
expect(helper).toContain("DOCKER_BUILDKIT=1");
expect(helper).toContain("docker_build_exec()");
expect(helper).toContain("docker_build_run()");
expect(helper).toContain("docker buildx build --load");
});
it("keeps shell-script Docker builds behind the helper", () => {
for (const path of CENTRALIZED_BUILD_SCRIPTS) {
const script = readFileSync(path, "utf8");
expect(script, path).toMatch(/docker-build\.sh|docker-e2e-image\.sh/);
expect(script, path).not.toMatch(/\bdocker build\b/);
expect(script, path).not.toMatch(/run_logged\s+\S+\s+docker\s+build/);
}
});
});