From 29a2a175e1d24be2074e6cb374f84a827bd14b63 Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Wed, 1 Apr 2026 21:25:39 -0700 Subject: [PATCH 1/3] fix: harden upgrade builds and shared agent mounts - pass resolved tool versions into rust image builds and cover it in CI - normalize .agents mount targets so overrides and container reuse work --- .github/workflows/ci.yml | 4 + Dockerfile.rust | 1 + deva.sh | 25 ++++++ scripts/install-agent-tooling.sh | 1 + scripts/release-utils.sh | 12 +-- scripts/version-upgrade.sh | 4 + tests/version-upgrade.sh | 146 +++++++++++++++++++++++++++++++ 7 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 tests/version-upgrade.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b054a96..63c2aba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,10 @@ jobs: chmod +x scripts/version-check.sh ./scripts/version-check.sh + - name: Test versions-up build args + shell: bash + run: bash ./tests/version-upgrade.sh + smoke: name: Installer Smoke Test runs-on: ubuntu-latest diff --git a/Dockerfile.rust b/Dockerfile.rust index 9f5c2c8..9dd1175 100644 --- a/Dockerfile.rust +++ b/Dockerfile.rust @@ -80,6 +80,7 @@ RUN echo 'export PATH="/opt/cargo/bin:$PATH"' >> "$DEVA_HOME/.zshrc" && \ echo 'alias cl="cargo clippy"' >> "$DEVA_HOME/.zshrc" USER $DEVA_USER +WORKDIR $DEVA_HOME COPY --chown=deva:deva scripts/install-agent-tooling.sh /tmp/install-agent-tooling.sh diff --git a/deva.sh b/deva.sh index 3356f9c..2e53afd 100755 --- a/deva.sh +++ b/deva.sh @@ -416,9 +416,12 @@ user_volume_mounts_target() { local target="$1" local spec remainder dest + target="$(normalize_container_bind_target "$target")" + for spec in "${USER_VOLUMES[@]+"${USER_VOLUMES[@]}"}"; do remainder="${spec#*:}" dest="${remainder%%:*}" + dest="$(normalize_container_bind_target "$dest")" if [ "$dest" = "$target" ]; then return 0 fi @@ -427,6 +430,27 @@ user_volume_mounts_target() { return 1 } +normalize_container_bind_target() { + local path="$1" + + while [ "$path" != "/" ] && [[ "$path" == */ ]]; do + path="${path%/}" + done + + printf '%s' "$path" +} + +append_shared_agents_mount() { + local target="/home/deva/.agents" + + [ "$QUICK_MODE" = false ] || return 0 + [ -d "$HOME/.agents" ] || return 0 + + if ! user_volume_mounts_target "$target"; then + USER_VOLUMES+=("$HOME/.agents:$target") + fi +} + prepare_claude_chrome_detection_mount() { local profile_path="" local user_data_dir="" @@ -2425,6 +2449,7 @@ autolink_legacy_into_deva_root() { check_agent "$ACTIVE_AGENT" prepare_claude_chrome_bridge +append_shared_agents_mount if [ -n "$CONFIG_HOME" ] && [ "$DRY_RUN" != true ]; then if [ ! -d "$CONFIG_HOME" ]; then diff --git a/scripts/install-agent-tooling.sh b/scripts/install-agent-tooling.sh index a1cf099..682639c 100644 --- a/scripts/install-agent-tooling.sh +++ b/scripts/install-agent-tooling.sh @@ -22,5 +22,6 @@ npm cache clean --force "$DEVA_HOME/.npm-global/bin/claude-trace" --help >/dev/null (npm list -g --depth=0 @anthropic-ai/claude-code @openai/codex @google/gemini-cli || true) +cd "$DEVA_HOME" curl -fsSL "https://raw.githubusercontent.com/lroolle/atlas-cli/${ATLAS_CLI_VERSION}/install.sh" \ | bash -s -- --skill-dir "$DEVA_HOME/.skills" diff --git a/scripts/release-utils.sh b/scripts/release-utils.sh index db8b4ec..b572a93 100755 --- a/scripts/release-utils.sh +++ b/scripts/release-utils.sh @@ -203,13 +203,13 @@ for section in sections: if cur_v < v <= lat_v: lines = section.strip().split("\n") output = [lines[0].replace("## ", "")] - content_lines = [l for l in lines[1:] if l.strip()][:10] + content_lines = [l for l in lines[1:] if l.strip()] output.extend(content_lines) changes.append("\n".join(output)) except: continue -for change in reversed(changes[-3:]): +for change in reversed(changes): print(change) print() ' "$current" "$latest" <<< "$data" 2>/dev/null || true @@ -256,10 +256,10 @@ for rel in releases: except: continue -for v, ver, body in sorted(changes, key=lambda x: x[0], reverse=True)[:3]: +for v, ver, body in sorted(changes, key=lambda x: x[0], reverse=True): print(f"{ver}") if body: - for line in [l.rstrip() for l in body.split("\n") if l.strip()][:15]: + for line in [l.rstrip() for l in body.split("\n") if l.strip()]: print(f" {line}") print() ' "$current" "$latest" <<< "$json" 2>/dev/null || true @@ -467,7 +467,7 @@ for section in sections: continue lines = section.strip().split("\n") print(lines[0].replace("## ", "")) - for line in [l for l in lines[1:] if l.strip()][:8]: + for line in [l for l in lines[1:] if l.strip()]: print(line) print() printed += 1 @@ -497,7 +497,7 @@ for rel in releases: body = (rel.get("body") or "").replace("\r\n", "\n").strip() print(ver) if body: - for line in [l.rstrip() for l in body.split("\n") if l.strip()][:12]: + for line in [l.rstrip() for l in body.split("\n") if l.strip()]: print(f" {line}") print() printed += 1 diff --git a/scripts/version-upgrade.sh b/scripts/version-upgrade.sh index e3c18ee..e0b9385 100755 --- a/scripts/version-upgrade.sh +++ b/scripts/version-upgrade.sh @@ -107,6 +107,10 @@ main() { section "Building Rust Image" docker build -f "$RUST_DOCKERFILE" \ --build-arg BASE_IMAGE="$BUILD_IMAGE" \ + --build-arg CLAUDE_CODE_VERSION="$claude_ver" \ + --build-arg CODEX_VERSION="$codex_ver" \ + --build-arg GEMINI_CLI_VERSION="$gemini_ver" \ + --build-arg ATLAS_CLI_VERSION="$atlas_ver" \ -t "$RUST_IMAGE" . echo "" diff --git a/tests/version-upgrade.sh b/tests/version-upgrade.sh new file mode 100644 index 0000000..c046149 --- /dev/null +++ b/tests/version-upgrade.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +TMP_ROOT="$(mktemp -d)" +FAKE_BIN="$TMP_ROOT/bin" +DOCKER_BUILD_LOG="$TMP_ROOT/docker-build.log" +mkdir -p "$FAKE_BIN" + +cleanup() { + rm -rf "$TMP_ROOT" +} +trap cleanup EXIT + +cat >"$FAKE_BIN/docker" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +case "${1:-}" in +inspect) + cat <<'JSON' +[{"Config":{"Labels":{ + "org.opencontainers.image.claude_code_version":"2.1.81", + "org.opencontainers.image.codex_version":"0.116.0", + "org.opencontainers.image.gemini_cli_version":"0.35.0", + "org.opencontainers.image.atlas_cli_version":"v0.1.4", + "org.opencontainers.image.copilot_api_version":"0ea08febdd7e3e055b03dd298bf57e669500b5c1" +}}}] +JSON + ;; +build) + printf '%s\n' "$*" >>"$DOCKER_BUILD_LOG" + ;; +*) + echo "unexpected docker invocation: $*" >&2 + exit 1 + ;; +esac +EOF + +cat >"$FAKE_BIN/npm" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +if [[ "${1:-}" != "view" ]]; then + echo "unexpected npm invocation: $*" >&2 + exit 1 +fi + +case "${2:-}" in +@anthropic-ai/claude-code) + echo "2.1.87" + ;; +@anthropic-ai/claude-code@2.1.87) + echo '{"2.1.87":"2026-03-29T01:40:00Z"}' + ;; +@openai/codex) + echo "0.117.0" + ;; +@openai/codex@0.117.0) + echo '{"0.117.0":"2026-03-26T22:28:00Z"}' + ;; +@google/gemini-cli) + echo "0.35.3" + ;; +@google/gemini-cli@0.35.3) + echo '{"0.35.3":"2026-03-28T03:17:00Z"}' + ;; +*) + echo "unexpected npm view args: $*" >&2 + exit 1 + ;; +esac +EOF + +cat >"$FAKE_BIN/gh" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +if [[ "${1:-}" != "api" ]]; then + echo "unexpected gh invocation: $*" >&2 + exit 1 +fi + +case "${2:-}" in +repos/lroolle/atlas-cli/releases/latest) + echo "v0.1.4" + ;; +repos/ericc-ch/copilot-api/branches/master) + echo "0ea08febdd7e3e055b03dd298bf57e669500b5c1" + ;; +repos/lroolle/atlas-cli/releases/tags/v0.1.4) + echo "2026-01-16T05:42:00Z" + ;; +repos/ericc-ch/copilot-api/commits/0ea08febdd7e3e055b03dd298bf57e669500b5c1) + echo "2025-10-05T03:49:00Z" + ;; +repos/openai/codex/releases) + echo '[]' + ;; +*) + echo "unexpected gh api args: $*" >&2 + exit 1 + ;; +esac +EOF + +cat >"$FAKE_BIN/curl" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +printf '' +EOF + +chmod +x "$FAKE_BIN/docker" "$FAKE_BIN/npm" "$FAKE_BIN/gh" "$FAKE_BIN/curl" + +PATH="$FAKE_BIN:$PATH" \ +DOCKER_BUILD_LOG="$DOCKER_BUILD_LOG" \ +AUTO_YES=1 \ +CHECK_IMAGE="ghcr.io/thevibeworks/deva:rust" \ +BUILD_IMAGE="ghcr.io/thevibeworks/deva:latest" \ +RUST_IMAGE="ghcr.io/thevibeworks/deva:rust" \ +"$REPO_ROOT/scripts/version-upgrade.sh" >/dev/null + +main_build="$(sed -n '1p' "$DOCKER_BUILD_LOG")" +rust_build="$(sed -n '2p' "$DOCKER_BUILD_LOG")" + +[[ -n "$main_build" ]] || { echo "missing main build invocation" >&2; exit 1; } +[[ -n "$rust_build" ]] || { echo "missing rust build invocation" >&2; exit 1; } + +for expected in \ + "--build-arg CLAUDE_CODE_VERSION=2.1.87" \ + "--build-arg CODEX_VERSION=0.117.0" \ + "--build-arg GEMINI_CLI_VERSION=0.35.3" \ + "--build-arg ATLAS_CLI_VERSION=v0.1.4" +do + [[ "$main_build" == *"$expected"* ]] || { + echo "main build missing expected arg: $expected" >&2 + exit 1 + } + [[ "$rust_build" == *"$expected"* ]] || { + echo "rust build missing expected arg: $expected" >&2 + exit 1 + } +done From 03e9aa2764d492b67b6c6cf7c1e96a888617ae55 Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Sun, 12 Apr 2026 20:12:06 -0700 Subject: [PATCH 2/3] feat: rebuild rust image with browser tooling --- .github/workflows/ci.yml | 19 ++++++- .github/workflows/nightly-images.yml | 71 ++++++++++++++++++++++++-- .github/workflows/release.yml | 74 ++++++++++++++++++++++++++-- Dockerfile | 9 ++-- Dockerfile.rust | 28 ++++++++++- Makefile | 55 ++++++++++++++++----- docs/custom-images.md | 24 +++++++++ docs/troubleshooting.md | 29 +++++++++++ scripts/install-browser-tooling.sh | 20 ++++++++ 9 files changed, 300 insertions(+), 29 deletions(-) create mode 100644 scripts/install-browser-tooling.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63c2aba..f153830 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: "22" + node-version: "24" - name: Resolve tool versions id: versions @@ -89,6 +89,23 @@ jobs: ATLAS_CLI_VERSION="${{ steps.versions.outputs.atlas_cli_version }}" \ COPILOT_API_VERSION="${{ steps.versions.outputs.copilot_api_version }}" + - name: Smoke rust runtime and browser tooling + shell: bash + run: | + set -euo pipefail + docker run --rm deva-smoke:ci-rust bash -lc ' + bwrap --version + go version | grep "go1.26.2" + playwright --version + playwright-mcp --help >/dev/null + playwright install --list + if command -v google-chrome >/dev/null 2>&1; then + google-chrome --version + else + echo "google-chrome not installed on $(dpkg --print-architecture)" + fi + ' + - name: Install and launch each agent without a TTY shell: bash run: | diff --git a/.github/workflows/nightly-images.yml b/.github/workflows/nightly-images.yml index 2222215..3ccb64f 100644 --- a/.github/workflows/nightly-images.yml +++ b/.github/workflows/nightly-images.yml @@ -8,6 +8,9 @@ on: env: REGISTRY: ghcr.io IMAGE_NAME: thevibeworks/deva + GO_VERSION: 1.26.2 + PLAYWRIGHT_VERSION: 1.59.1 + PLAYWRIGHT_MCP_VERSION: 0.0.70 permissions: contents: read @@ -35,7 +38,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: "22" + node-version: "24" - name: Resolve versions id: versions @@ -59,7 +62,7 @@ jobs: build-base: name: Build Nightly Base Image - needs: resolve-versions + needs: [resolve-versions, build-core] runs-on: ubuntu-latest steps: - name: Checkout @@ -107,10 +110,60 @@ jobs: GEMINI_CLI_VERSION=${{ needs.resolve-versions.outputs.gemini_cli_version }} ATLAS_CLI_VERSION=${{ needs.resolve-versions.outputs.atlas_cli_version }} COPILOT_API_VERSION=${{ needs.resolve-versions.outputs.copilot_api_version }} + GO_VERSION=${{ env.GO_VERSION }} + + build-core: + name: Build Nightly Core Image + needs: resolve-versions + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta-core + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=nightly-core + type=raw,value=nightly-${{ needs.resolve-versions.outputs.stamp }}-core + labels: | + org.opencontainers.image.title=deva-nightly-core + org.opencontainers.image.description=Nightly deva core image for downstream profile builds + + - name: Build and push core image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + target: agent-base + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta-core.outputs.tags }} + labels: ${{ steps.meta-core.outputs.labels }} + cache-from: type=gha,scope=nightly-core + cache-to: type=gha,mode=max,scope=nightly-core + build-args: | + COPILOT_API_VERSION=${{ needs.resolve-versions.outputs.copilot_api_version }} + GO_VERSION=${{ env.GO_VERSION }} build-rust: name: Build Nightly Rust Image - needs: [resolve-versions, build-base] + needs: [resolve-versions, build-core] runs-on: ubuntu-latest steps: - name: Checkout @@ -153,11 +206,17 @@ jobs: cache-from: type=gha,scope=nightly-rust cache-to: type=gha,mode=max,scope=nightly-rust build-args: | - BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly-${{ needs.resolve-versions.outputs.stamp }} + BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly-${{ needs.resolve-versions.outputs.stamp }}-core + CLAUDE_CODE_VERSION=${{ needs.resolve-versions.outputs.claude_code_version }} + CODEX_VERSION=${{ needs.resolve-versions.outputs.codex_version }} + GEMINI_CLI_VERSION=${{ needs.resolve-versions.outputs.gemini_cli_version }} + ATLAS_CLI_VERSION=${{ needs.resolve-versions.outputs.atlas_cli_version }} + PLAYWRIGHT_VERSION=${{ env.PLAYWRIGHT_VERSION }} + PLAYWRIGHT_MCP_VERSION=${{ env.PLAYWRIGHT_MCP_VERSION }} summary: name: Nightly Summary - needs: [resolve-versions, build-base, build-rust] + needs: [resolve-versions, build-core, build-base, build-rust] runs-on: ubuntu-latest steps: - name: Publish summary @@ -165,6 +224,8 @@ jobs: cat <> "$GITHUB_STEP_SUMMARY" ## Published Nightly Images + - \`ghcr.io/thevibeworks/deva:nightly-core\` + - \`ghcr.io/thevibeworks/deva:nightly-${{ needs.resolve-versions.outputs.stamp }}-core\` - \`ghcr.io/thevibeworks/deva:nightly\` - \`ghcr.io/thevibeworks/deva:nightly-${{ needs.resolve-versions.outputs.stamp }}\` - \`ghcr.io/thevibeworks/deva:nightly-rust\` diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 17b2a9f..0d3dc0e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,9 @@ on: env: REGISTRY: ghcr.io IMAGE_NAME: thevibeworks/deva + GO_VERSION: 1.26.2 + PLAYWRIGHT_VERSION: 1.59.1 + PLAYWRIGHT_MCP_VERSION: 0.0.70 jobs: prepare: @@ -51,7 +54,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: "22" + node-version: "24" - name: Resolve versions id: versions @@ -73,7 +76,7 @@ jobs: build-and-push: name: Build and Push Docker Image - needs: [prepare, resolve-versions] + needs: [prepare, resolve-versions, build-and-push-core] runs-on: ubuntu-latest permissions: contents: read @@ -123,11 +126,66 @@ jobs: GEMINI_CLI_VERSION=${{ needs.resolve-versions.outputs.gemini_cli_version }} ATLAS_CLI_VERSION=${{ needs.resolve-versions.outputs.atlas_cli_version }} COPILOT_API_VERSION=${{ needs.resolve-versions.outputs.copilot_api_version }} + GO_VERSION=${{ env.GO_VERSION }} + + build-and-push-core: + name: Build and Push Core Image + needs: [prepare, resolve-versions] + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ needs.prepare.outputs.release_tag }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for core image + id: meta-core + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=${{ needs.prepare.outputs.release_tag }}-core + type=raw,value=core + labels: | + org.opencontainers.image.title=deva-core + org.opencontainers.image.description=Stable deva core image for downstream profile builds + + - name: Build and push core image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + target: agent-base + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta-core.outputs.tags }} + labels: ${{ steps.meta-core.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + COPILOT_API_VERSION=${{ needs.resolve-versions.outputs.copilot_api_version }} + GO_VERSION=${{ env.GO_VERSION }} build-and-push-rust: name: Build and Push Rust Profile Image runs-on: ubuntu-latest - needs: [prepare, build-and-push] + needs: [prepare, resolve-versions, build-and-push-core] permissions: contents: read packages: write @@ -171,12 +229,18 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max build-args: | - BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.release_tag }} + BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.release_tag }}-core + CLAUDE_CODE_VERSION=${{ needs.resolve-versions.outputs.claude_code_version }} + CODEX_VERSION=${{ needs.resolve-versions.outputs.codex_version }} + GEMINI_CLI_VERSION=${{ needs.resolve-versions.outputs.gemini_cli_version }} + ATLAS_CLI_VERSION=${{ needs.resolve-versions.outputs.atlas_cli_version }} + PLAYWRIGHT_VERSION=${{ env.PLAYWRIGHT_VERSION }} + PLAYWRIGHT_MCP_VERSION=${{ env.PLAYWRIGHT_MCP_VERSION }} release: name: Create GitHub Release runs-on: ubuntu-latest - needs: [prepare, build-and-push, build-and-push-rust] + needs: [prepare, build-and-push-core, build-and-push, build-and-push-rust] permissions: contents: write steps: diff --git a/Dockerfile b/Dockerfile index fb92507..08e3ab0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ openssh-client rsync \ shellcheck bat fd-find silversearcher-ag \ vim \ - procps psmisc zsh socat \ + procps psmisc zsh socat bubblewrap \ libevent-dev libncurses-dev bison # Prevent noisy setlocale warnings at shell startup @@ -43,7 +43,7 @@ RUN git lfs install --system # Install language runtimes in parallel-friendly layers FROM base AS runtimes -ARG NODE_MAJOR=22 +ARG NODE_MAJOR=24 RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - && \ @@ -60,12 +60,13 @@ RUN curl -LsSf https://astral.sh/uv/install.sh | sh # Pre-install Python 3.14t (free-threaded) for uv RUN /root/.local/bin/uv python install 3.14t +ARG GO_VERSION=1.26.2 RUN --mount=type=cache,target=/tmp/go-cache,sharing=locked \ ARCH=$(dpkg --print-architecture) && \ GO_ARCH=$([ "$ARCH" = "amd64" ] && echo "amd64" || echo "arm64") && \ cd /tmp/go-cache && \ - wget -q https://go.dev/dl/go1.22.0.linux-${GO_ARCH}.tar.gz && \ - tar -C /usr/local -xzf go1.22.0.linux-${GO_ARCH}.tar.gz + wget -q "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" && \ + tar -C /usr/local -xzf "go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" # Install Copilot API (ericc-ch fork with latest features) # Placed at end of runtimes stage to avoid invalidating cache for stable runtimes diff --git a/Dockerfile.rust b/Dockerfile.rust index 9dd1175..357c4c6 100644 --- a/Dockerfile.rust +++ b/Dockerfile.rust @@ -12,6 +12,8 @@ ARG CLAUDE_CODE_VERSION=2.1.81 ARG CODEX_VERSION=0.116.0 ARG GEMINI_CLI_VERSION=0.35.0 ARG ATLAS_CLI_VERSION=v0.1.4 +ARG PLAYWRIGHT_VERSION=1.59.1 +ARG PLAYWRIGHT_MCP_VERSION=0.0.70 ARG RUST_TOOLCHAINS="stable" ARG RUST_TARGETS="wasm32-unknown-unknown" @@ -19,12 +21,15 @@ LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION} LABEL org.opencontainers.image.codex_version=${CODEX_VERSION} LABEL org.opencontainers.image.gemini_cli_version=${GEMINI_CLI_VERSION} LABEL org.opencontainers.image.atlas_cli_version=${ATLAS_CLI_VERSION} +LABEL org.opencontainers.image.playwright_version=${PLAYWRIGHT_VERSION} +LABEL org.opencontainers.image.playwright_mcp_version=${PLAYWRIGHT_MCP_VERSION} SHELL ["/bin/bash", "-o", "pipefail", "-c"] ENV RUSTUP_HOME=/opt/rustup \ CARGO_HOME=/opt/cargo \ - PATH=/opt/cargo/bin:$PATH + PATH=/opt/cargo/bin:$PATH \ + PLAYWRIGHT_BROWSERS_PATH=/home/deva/.cache/ms-playwright USER root @@ -47,6 +52,20 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ apt-get update && \ apt-get install -y --no-install-recommends clickhouse-client +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + set -euxo pipefail && \ + ARCH=$(dpkg --print-architecture) && \ + if [ "$ARCH" = "amd64" ]; then \ + mkdir -p /etc/apt/keyrings && \ + curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /etc/apt/keyrings/google-chrome.gpg && \ + echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.gpg] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends google-chrome-stable; \ + else \ + echo "Skipping google-chrome-stable on unsupported architecture: $ARCH"; \ + fi + RUN --mount=type=cache,target=/tmp/rust-cache,sharing=locked \ set -euxo pipefail && \ mkdir -p "$RUSTUP_HOME" "$CARGO_HOME" && \ @@ -82,8 +101,15 @@ RUN echo 'export PATH="/opt/cargo/bin:$PATH"' >> "$DEVA_HOME/.zshrc" && \ USER $DEVA_USER WORKDIR $DEVA_HOME +COPY --chown=deva:deva scripts/install-browser-tooling.sh /tmp/install-browser-tooling.sh COPY --chown=deva:deva scripts/install-agent-tooling.sh /tmp/install-agent-tooling.sh +RUN --mount=type=cache,target=/home/deva/.npm,uid=${DEVA_UID},gid=${DEVA_GID},sharing=locked \ + --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + bash /tmp/install-browser-tooling.sh && \ + rm -f /tmp/install-browser-tooling.sh + RUN --mount=type=cache,target=/home/deva/.npm,uid=${DEVA_UID},gid=${DEVA_GID},sharing=locked \ bash /tmp/install-agent-tooling.sh && \ rm -f /tmp/install-agent-tooling.sh diff --git a/Makefile b/Makefile index accae25..cbeeb42 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ CODEX_VERSION := $(shell npm view @openai/codex version 2>/dev/null || echo "0.1 GEMINI_CLI_VERSION := $(shell npm view @google/gemini-cli version 2>/dev/null || echo "0.35.0") ATLAS_CLI_VERSION := $(shell gh api repos/lroolle/atlas-cli/releases/latest --jq '.tag_name' 2>/dev/null || echo "v0.1.4") COPILOT_API_VERSION := $(shell gh api repos/ericc-ch/copilot-api/branches/master --jq '.commit.sha' 2>/dev/null || echo "0ea08febdd7e3e055b03dd298bf57e669500b5c1") +PLAYWRIGHT_VERSION := $(shell npm view playwright version 2>/dev/null || echo "1.59.1") +PLAYWRIGHT_MCP_VERSION := $(shell npm view @playwright/mcp version 2>/dev/null || echo "0.0.70") +GO_VERSION := 1.26.2 export DOCKER_BUILDKIT := 1 @@ -70,21 +73,21 @@ build-main: echo "Gemini: $$curG -> $$tgtG"; \ fi; \ fi - @echo "Hint: override via CLAUDE_CODE_VERSION=... CODEX_VERSION=... GEMINI_CLI_VERSION=... ATLAS_CLI_VERSION=... COPILOT_API_VERSION=... or run 'make bump-versions' to pin" - docker build -f $(DOCKERFILE) --build-arg CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) --build-arg CODEX_VERSION=$(CODEX_VERSION) --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) -t $(MAIN_IMAGE) . + @echo "Hint: override via CLAUDE_CODE_VERSION=... CODEX_VERSION=... GEMINI_CLI_VERSION=... ATLAS_CLI_VERSION=... COPILOT_API_VERSION=... GO_VERSION=... PLAYWRIGHT_VERSION=... PLAYWRIGHT_MCP_VERSION=..." + docker build -f $(DOCKERFILE) --build-arg CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) --build-arg CODEX_VERSION=$(CODEX_VERSION) --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) --build-arg GO_VERSION=$(GO_VERSION) -t $(MAIN_IMAGE) . @echo "โœ… Build completed: $(MAIN_IMAGE)" .PHONY: rebuild rebuild: @echo "๐Ÿ”จ Rebuilding Docker image (no cache) with $(DOCKERFILE)..." - docker build -f $(DOCKERFILE) --no-cache --build-arg CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) --build-arg CODEX_VERSION=$(CODEX_VERSION) --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) -t $(MAIN_IMAGE) . + docker build -f $(DOCKERFILE) --no-cache --build-arg CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) --build-arg CODEX_VERSION=$(CODEX_VERSION) --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) --build-arg GO_VERSION=$(GO_VERSION) -t $(MAIN_IMAGE) . @echo "โœ… Rebuild completed: $(MAIN_IMAGE)" .PHONY: build-core build-core: @echo "๐Ÿ”จ Building stable core image..." - docker build -f $(DOCKERFILE) --target agent-base --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) -t $(CORE_IMAGE) . + docker build -f $(DOCKERFILE) --target agent-base --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) --build-arg GO_VERSION=$(GO_VERSION) -t $(CORE_IMAGE) . @echo "โœ… Core build completed: $(CORE_IMAGE)" .PHONY: build-rust-image @@ -96,6 +99,8 @@ build-rust-image: --build-arg CODEX_VERSION=$(CODEX_VERSION) \ --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) \ --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) \ + --build-arg PLAYWRIGHT_VERSION=$(PLAYWRIGHT_VERSION) \ + --build-arg PLAYWRIGHT_MCP_VERSION=$(PLAYWRIGHT_MCP_VERSION) \ -t $(RUST_IMAGE) . @echo "โœ… Rust build completed: $(RUST_IMAGE)" @@ -104,16 +109,16 @@ build-rust: build-core build-rust-image .PHONY: build-all build-all: - @echo "๐Ÿ”จ Building all images with versions: Claude $(CLAUDE_CODE_VERSION), Codex $(CODEX_VERSION), Gemini $(GEMINI_CLI_VERSION), Atlas $(ATLAS_CLI_VERSION), Copilot-API $(COPILOT_API_VERSION)..." - @$(MAKE) build-core COPILOT_API_VERSION=$(COPILOT_API_VERSION) - @$(MAKE) build-main CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) CODEX_VERSION=$(CODEX_VERSION) GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) COPILOT_API_VERSION=$(COPILOT_API_VERSION) - @$(MAKE) build-rust-image CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) CODEX_VERSION=$(CODEX_VERSION) GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) COPILOT_API_VERSION=$(COPILOT_API_VERSION) + @echo "๐Ÿ”จ Building all images with versions: Claude $(CLAUDE_CODE_VERSION), Codex $(CODEX_VERSION), Gemini $(GEMINI_CLI_VERSION), Atlas $(ATLAS_CLI_VERSION), Copilot-API $(COPILOT_API_VERSION), Go $(GO_VERSION), Playwright $(PLAYWRIGHT_VERSION), Playwright MCP $(PLAYWRIGHT_MCP_VERSION)..." + @$(MAKE) build-core COPILOT_API_VERSION=$(COPILOT_API_VERSION) GO_VERSION=$(GO_VERSION) + @$(MAKE) build-main CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) CODEX_VERSION=$(CODEX_VERSION) GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) COPILOT_API_VERSION=$(COPILOT_API_VERSION) GO_VERSION=$(GO_VERSION) + @$(MAKE) build-rust-image CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) CODEX_VERSION=$(CODEX_VERSION) GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) COPILOT_API_VERSION=$(COPILOT_API_VERSION) PLAYWRIGHT_VERSION=$(PLAYWRIGHT_VERSION) PLAYWRIGHT_MCP_VERSION=$(PLAYWRIGHT_MCP_VERSION) @echo "โœ… All images built successfully" .PHONY: buildx buildx: @echo "๐Ÿ”จ Building with docker buildx..." - docker buildx build -f $(DOCKERFILE) --load --build-arg CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) --build-arg CODEX_VERSION=$(CODEX_VERSION) --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) -t $(MAIN_IMAGE) . + docker buildx build -f $(DOCKERFILE) --load --build-arg CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) --build-arg CODEX_VERSION=$(CODEX_VERSION) --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) --build-arg GO_VERSION=$(GO_VERSION) -t $(MAIN_IMAGE) . @echo "โœ… Buildx completed: $(MAIN_IMAGE)" .PHONY: buildx-multi @@ -125,14 +130,30 @@ buildx-multi: --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) \ --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) \ --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) \ + --build-arg GO_VERSION=$(GO_VERSION) \ --push -t $(MAIN_IMAGE) . @echo "โœ… Multi-arch build completed and pushed: $(MAIN_IMAGE)" +.PHONY: buildx-multi-core +buildx-multi-core: + @echo "๐Ÿ”จ Building multi-arch core image for amd64 and arm64..." + docker buildx build -f $(DOCKERFILE) --target agent-base --platform linux/amd64,linux/arm64 \ + --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) \ + --build-arg GO_VERSION=$(GO_VERSION) \ + --push -t $(CORE_IMAGE) . + @echo "โœ… Multi-arch core build completed and pushed: $(CORE_IMAGE)" + .PHONY: buildx-multi-rust -buildx-multi-rust: +buildx-multi-rust: buildx-multi-core @echo "๐Ÿ”จ Building multi-arch Rust images for amd64 and arm64..." docker buildx build -f $(RUST_DOCKERFILE) --platform linux/amd64,linux/arm64 \ - --build-arg BASE_IMAGE=$(MAIN_IMAGE) \ + --build-arg BASE_IMAGE=$(CORE_IMAGE) \ + --build-arg CLAUDE_CODE_VERSION=$(CLAUDE_CODE_VERSION) \ + --build-arg CODEX_VERSION=$(CODEX_VERSION) \ + --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) \ + --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) \ + --build-arg PLAYWRIGHT_VERSION=$(PLAYWRIGHT_VERSION) \ + --build-arg PLAYWRIGHT_MCP_VERSION=$(PLAYWRIGHT_MCP_VERSION) \ --push -t $(RUST_IMAGE) . @echo "โœ… Multi-arch Rust build completed and pushed: $(RUST_IMAGE)" @@ -145,6 +166,7 @@ buildx-multi-local: --build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) \ --build-arg ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) \ --build-arg COPILOT_API_VERSION=$(COPILOT_API_VERSION) \ + --build-arg GO_VERSION=$(GO_VERSION) \ -t $(MAIN_IMAGE) . @echo "โœ… Multi-arch build completed locally: $(MAIN_IMAGE)" @@ -159,6 +181,9 @@ versions-up: CODEX_VERSION=$(CODEX_VERSION) \ GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) \ ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) \ + PLAYWRIGHT_VERSION=$(PLAYWRIGHT_VERSION) \ + PLAYWRIGHT_MCP_VERSION=$(PLAYWRIGHT_MCP_VERSION) \ + GO_VERSION=$(GO_VERSION) \ COPILOT_API_VERSION=$(COPILOT_API_VERSION) \ ./scripts/version-upgrade.sh @@ -168,6 +193,9 @@ versions: CODEX_VERSION=$(CODEX_VERSION) \ GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) \ ATLAS_CLI_VERSION=$(ATLAS_CLI_VERSION) \ + PLAYWRIGHT_VERSION=$(PLAYWRIGHT_VERSION) \ + PLAYWRIGHT_MCP_VERSION=$(PLAYWRIGHT_MCP_VERSION) \ + GO_VERSION=$(GO_VERSION) \ COPILOT_API_VERSION=$(COPILOT_API_VERSION) \ MAIN_IMAGE=$(DETECTED_IMAGE) \ ./scripts/version-report.sh @@ -237,8 +265,8 @@ test-rust: @echo "๐Ÿงช Testing $(RUST_IMAGE)..." @echo "Testing Rust toolchain..." docker run --rm $(RUST_IMAGE) bash -c 'rustc --version && cargo --version && rustfmt --version && clippy-driver --version' - @echo "Testing Rust tools..." - docker run --rm $(RUST_IMAGE) bash -c 'cargo-watch --version && wasm-pack --version' + @echo "Testing Rust and browser tools..." + docker run --rm $(RUST_IMAGE) bash -c 'cargo-watch --version && wasm-pack --version && bwrap --version && go version && playwright --version && playwright-mcp --help >/dev/null && playwright install --list && if command -v google-chrome >/dev/null 2>&1; then google-chrome --version; else echo "google-chrome not installed on $$(dpkg --print-architecture)"; fi' @echo "โœ… Rust tests passed" .PHONY: test-local @@ -324,6 +352,7 @@ help: @echo " rebuild Rebuild without cache" @echo " buildx Build with buildx" @echo " buildx-multi Build multi-arch and push" + @echo " buildx-multi-core Build multi-arch core image and push" @echo " buildx-multi-rust Build multi-arch Rust and push" @echo " versions Compare built vs latest versions with changelogs" @echo " versions-up Upgrade both images to latest npm versions" diff --git a/docs/custom-images.md b/docs/custom-images.md index 9eb65b2..611b65b 100644 --- a/docs/custom-images.md +++ b/docs/custom-images.md @@ -67,6 +67,7 @@ docker build -t deva-local:latest \ --build-arg GEMINI_CLI_VERSION= \ --build-arg ATLAS_CLI_VERSION= \ --build-arg COPILOT_API_VERSION= \ + --build-arg GO_VERSION=1.26.2 \ . docker build -f Dockerfile --target agent-base -t deva-local:core . @@ -77,6 +78,8 @@ docker build -f Dockerfile.rust -t deva-local:rust \ --build-arg CODEX_VERSION= \ --build-arg GEMINI_CLI_VERSION= \ --build-arg ATLAS_CLI_VERSION= \ + --build-arg PLAYWRIGHT_VERSION= \ + --build-arg PLAYWRIGHT_MCP_VERSION= \ . ``` @@ -119,6 +122,10 @@ deva.sh codex That is the whole trick. This is not Kubernetes. It is just an image name plus a tag. +If you want the same fast layering in your own registry for downstream +profile builds, publish a `core` tag too and make the Rust image inherit +from that instead of from the full `latest` image. + ## Keep It Personal If this is only for you, put the override in `.deva.local`: @@ -166,6 +173,23 @@ DEVA_DOCKER_IMAGE=deva-local DEVA_DOCKER_TAG=extras deva.sh gemini That is usually the sane move. +## Rust Image Includes Browser Tooling + +The rebuilt `rust` profile now bakes in: + +- `bubblewrap` for Claude Code subprocess isolation on Linux +- Go `1.26.2` +- Playwright CLI and Playwright MCP +- Playwright browsers installed in-image +- Google Chrome stable on `linux/amd64` + +Important detail: + +- Google Chrome's official Linux `.deb` is available for `amd64` +- on `arm64`, the image still has Playwright Chromium/Firefox/WebKit, but not `google-chrome-stable` + +If you only bump `claude-code`, `codex`, or `gemini`, the Rust build should mostly reuse cached lower layers. The browser/toolchain layers sit below the volatile agent install layer on purpose. + ## What Still Comes From The Wrapper Changing the image does not change the wrapper model. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index ce2e666..072006e 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -167,6 +167,35 @@ Reality check: - if the host socket file is absent, the extension may be installed but not yet connected - the published image still needs to be rebuilt with the patched `docker-entrypoint.sh`; otherwise the socket symlink is never created +## Claude Fails With `bubblewrap is required` + +Symptom: + +- Claude exits early on Linux with: + - `bubblewrap is required for subprocess env scrubbing and isolation` + +What it means: + +- newer Claude Code on Linux expects `bwrap` for subprocess isolation +- this is not a fake warning; without it, Claude may refuse to start that path + +What to do: + +- use an image rebuilt from current `Dockerfile` so `bubblewrap` is present +- verify inside the container: + +```bash +bwrap --version +claude --version +``` + +Fallback: + +- `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=0` disables that isolation path +- that is a downgrade, not a fix + +If `bwrap` exists and Claude still fails, you are debugging Claude's runtime behavior, not a missing package. + ## Dry-Run Looks Fine But Runtime Fails That is normal in at least three cases: diff --git a/scripts/install-browser-tooling.sh b/scripts/install-browser-tooling.sh new file mode 100644 index 0000000..88eb9df --- /dev/null +++ b/scripts/install-browser-tooling.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -euo pipefail + +: "${DEVA_HOME:?DEVA_HOME is required}" +: "${PLAYWRIGHT_VERSION:?PLAYWRIGHT_VERSION is required}" +: "${PLAYWRIGHT_MCP_VERSION:?PLAYWRIGHT_MCP_VERSION is required}" +: "${PLAYWRIGHT_BROWSERS_PATH:?PLAYWRIGHT_BROWSERS_PATH is required}" + +npm config set prefix "$DEVA_HOME/.npm-global" +mkdir -p "$PLAYWRIGHT_BROWSERS_PATH" + +npm install -g --no-audit --no-fund \ + "playwright@${PLAYWRIGHT_VERSION}" \ + "@playwright/mcp@${PLAYWRIGHT_MCP_VERSION}" + +"$DEVA_HOME/.npm-global/bin/playwright" install --with-deps chromium firefox webkit +"$DEVA_HOME/.npm-global/bin/playwright" --version +"$DEVA_HOME/.npm-global/bin/playwright-mcp" --help >/dev/null + +npm cache clean --force From 946efa83decb909c3946ad1440c6148cc96fc81f Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Sun, 12 Apr 2026 20:16:25 -0700 Subject: [PATCH 3/3] fix: keep versions-up aligned with core rust builds --- Makefile | 1 + scripts/version-upgrade.sh | 23 +++++++++++++++++++++-- tests/version-upgrade.sh | 37 ++++++++++++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index cbeeb42..4c0a38a 100644 --- a/Makefile +++ b/Makefile @@ -174,6 +174,7 @@ buildx-multi-local: versions-up: @MAIN_IMAGE=$(DETECTED_IMAGE) \ BUILD_IMAGE=$(MAIN_IMAGE) \ + CORE_IMAGE=$(CORE_IMAGE) \ RUST_IMAGE=$(RUST_IMAGE) \ DOCKERFILE=$(DOCKERFILE) \ RUST_DOCKERFILE=$(RUST_DOCKERFILE) \ diff --git a/scripts/version-upgrade.sh b/scripts/version-upgrade.sh index e0b9385..19532bd 100755 --- a/scripts/version-upgrade.sh +++ b/scripts/version-upgrade.sh @@ -10,9 +10,13 @@ source "$SCRIPT_DIR/release-utils.sh" # Defaults CHECK_IMAGE=${MAIN_IMAGE:-ghcr.io/thevibeworks/deva:latest} BUILD_IMAGE=${BUILD_IMAGE:-ghcr.io/thevibeworks/deva:latest} +CORE_IMAGE=${CORE_IMAGE:-ghcr.io/thevibeworks/deva:core} RUST_IMAGE=${RUST_IMAGE:-ghcr.io/thevibeworks/deva:rust} DOCKERFILE=${DOCKERFILE:-Dockerfile} RUST_DOCKERFILE=${RUST_DOCKERFILE:-Dockerfile.rust} +GO_VERSION=${GO_VERSION:-1.26.2} +PLAYWRIGHT_VERSION=${PLAYWRIGHT_VERSION:-1.59.1} +PLAYWRIGHT_MCP_VERSION=${PLAYWRIGHT_MCP_VERSION:-0.0.70} COUNTDOWN=${COUNTDOWN:-5} AUTO_YES=${AUTO_YES:-} @@ -26,12 +30,16 @@ Options: Environment: MAIN_IMAGE Main image name (default: ghcr.io/thevibeworks/deva:latest) + CORE_IMAGE Core image name (default: ghcr.io/thevibeworks/deva:core) RUST_IMAGE Rust image name (default: ghcr.io/thevibeworks/deva:rust) CLAUDE_CODE_VERSION Override claude-code version CODEX_VERSION Override codex version GEMINI_CLI_VERSION Override gemini-cli version ATLAS_CLI_VERSION Override atlas-cli version COPILOT_API_VERSION Override copilot-api version + GO_VERSION Override Go version for base/core builds + PLAYWRIGHT_VERSION Override Playwright version for rust builds + PLAYWRIGHT_MCP_VERSION Override Playwright MCP version for rust builds EOF } @@ -48,7 +56,7 @@ main() { echo -e "${CYAN}${BOLD}โ•‘ Upgrading to Latest Versions โ•‘${RESET}" echo -e "${CYAN}${BOLD}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${RESET}" echo -e "${DIM}Time: $(date '+%Y-%m-%d %H:%M:%S')${RESET}" - echo -e "${DIM}Check: ${CHECK_IMAGE} Build: ${BUILD_IMAGE}${RESET}" + echo -e "${DIM}Check: ${CHECK_IMAGE} Build: ${BUILD_IMAGE} Core: ${CORE_IMAGE}${RESET}" echo "" load_versions "$CHECK_IMAGE" @@ -94,6 +102,14 @@ main() { echo "" fi + section "Building Core Image" + docker build -f "$DOCKERFILE" \ + --target agent-base \ + --build-arg COPILOT_API_VERSION="$copilot_ver" \ + --build-arg GO_VERSION="$GO_VERSION" \ + -t "$CORE_IMAGE" . + + echo "" section "Building Main Image" docker build -f "$DOCKERFILE" \ --build-arg CLAUDE_CODE_VERSION="$claude_ver" \ @@ -101,16 +117,19 @@ main() { --build-arg GEMINI_CLI_VERSION="$gemini_ver" \ --build-arg ATLAS_CLI_VERSION="$atlas_ver" \ --build-arg COPILOT_API_VERSION="$copilot_ver" \ + --build-arg GO_VERSION="$GO_VERSION" \ -t "$BUILD_IMAGE" . echo "" section "Building Rust Image" docker build -f "$RUST_DOCKERFILE" \ - --build-arg BASE_IMAGE="$BUILD_IMAGE" \ + --build-arg BASE_IMAGE="$CORE_IMAGE" \ --build-arg CLAUDE_CODE_VERSION="$claude_ver" \ --build-arg CODEX_VERSION="$codex_ver" \ --build-arg GEMINI_CLI_VERSION="$gemini_ver" \ --build-arg ATLAS_CLI_VERSION="$atlas_ver" \ + --build-arg PLAYWRIGHT_VERSION="$PLAYWRIGHT_VERSION" \ + --build-arg PLAYWRIGHT_MCP_VERSION="$PLAYWRIGHT_MCP_VERSION" \ -t "$RUST_IMAGE" . echo "" diff --git a/tests/version-upgrade.sh b/tests/version-upgrade.sh index c046149..7666623 100644 --- a/tests/version-upgrade.sh +++ b/tests/version-upgrade.sh @@ -120,25 +120,56 @@ DOCKER_BUILD_LOG="$DOCKER_BUILD_LOG" \ AUTO_YES=1 \ CHECK_IMAGE="ghcr.io/thevibeworks/deva:rust" \ BUILD_IMAGE="ghcr.io/thevibeworks/deva:latest" \ +CORE_IMAGE="ghcr.io/thevibeworks/deva:core" \ RUST_IMAGE="ghcr.io/thevibeworks/deva:rust" \ +GO_VERSION="1.26.2" \ +PLAYWRIGHT_VERSION="1.59.1" \ +PLAYWRIGHT_MCP_VERSION="0.0.70" \ "$REPO_ROOT/scripts/version-upgrade.sh" >/dev/null -main_build="$(sed -n '1p' "$DOCKER_BUILD_LOG")" -rust_build="$(sed -n '2p' "$DOCKER_BUILD_LOG")" +core_build="$(sed -n '1p' "$DOCKER_BUILD_LOG")" +main_build="$(sed -n '2p' "$DOCKER_BUILD_LOG")" +rust_build="$(sed -n '3p' "$DOCKER_BUILD_LOG")" +[[ -n "$core_build" ]] || { echo "missing core build invocation" >&2; exit 1; } [[ -n "$main_build" ]] || { echo "missing main build invocation" >&2; exit 1; } [[ -n "$rust_build" ]] || { echo "missing rust build invocation" >&2; exit 1; } +for expected in \ + "--target agent-base" \ + "--build-arg COPILOT_API_VERSION=0ea08febdd7e3e055b03dd298bf57e669500b5c1" \ + "--build-arg GO_VERSION=1.26.2" \ + "-t ghcr.io/thevibeworks/deva:core ." +do + [[ "$core_build" == *"$expected"* ]] || { + echo "core build missing expected arg: $expected" >&2 + exit 1 + } +done + for expected in \ "--build-arg CLAUDE_CODE_VERSION=2.1.87" \ "--build-arg CODEX_VERSION=0.117.0" \ "--build-arg GEMINI_CLI_VERSION=0.35.3" \ - "--build-arg ATLAS_CLI_VERSION=v0.1.4" + "--build-arg ATLAS_CLI_VERSION=v0.1.4" \ + "--build-arg COPILOT_API_VERSION=0ea08febdd7e3e055b03dd298bf57e669500b5c1" \ + "--build-arg GO_VERSION=1.26.2" do [[ "$main_build" == *"$expected"* ]] || { echo "main build missing expected arg: $expected" >&2 exit 1 } +done + +for expected in \ + "--build-arg BASE_IMAGE=ghcr.io/thevibeworks/deva:core" \ + "--build-arg CLAUDE_CODE_VERSION=2.1.87" \ + "--build-arg CODEX_VERSION=0.117.0" \ + "--build-arg GEMINI_CLI_VERSION=0.35.3" \ + "--build-arg ATLAS_CLI_VERSION=v0.1.4" \ + "--build-arg PLAYWRIGHT_VERSION=1.59.1" \ + "--build-arg PLAYWRIGHT_MCP_VERSION=0.0.70" +do [[ "$rust_build" == *"$expected"* ]] || { echo "rust build missing expected arg: $expected" >&2 exit 1