#!/usr/bin/env bash # Manual publish of opencode-devbox v1.15.12 — bypasses broken Gitea-runner # Hub push by building & pushing from a developer host (Orbstack/Docker Desktop). # # Mirrors what .gitea/workflows/docker-publish-split.yml would do: # 1. Build & push Dockerfile.base → joakimp/opencode-devbox:base- # 2. Promote → joakimp/opencode-devbox:base-latest # 3. Build & push 4 variants on top of base-: # :v1.15.12 :latest (INSTALL_OPENCODE only) # :v1.15.12-omos :latest-omos (+ OMOS) # :v1.15.12-with-pi :latest-with-pi (+ pi) # :v1.15.12-omos-with-pi :latest-omos-with-pi (+ both) # # Usage on your host: # 1. Make sure Orbstack/Docker Desktop is running with multi-arch enabled # (docker buildx ls should show linux/amd64,linux/arm64). # 2. docker login docker.io (joakimp account) # 3. cd ~/path/to/opencode-devbox && git fetch && git checkout v1.15.12 # 4. bash /path/to/this/script.sh # # Total expected time: ~25-40 min on a recent Mac (4 multi-arch builds, base # layers cache after the first variant). set -euo pipefail IMAGE="joakimp/opencode-devbox" RELEASE_TAG="v1.15.12" BASE_HASH="8d72a9e44796" # sha256 of Dockerfile.base + rootfs/* + entrypoints (computed by CI logic) BASE_TAG="base-${BASE_HASH}" PI_VERSION="0.76.0" # resolved from npm @earendil-works/pi-coding-agent latest (2026-05-28) OMOS_VERSION="1.1.1" # resolved from npm oh-my-opencode-slim latest (2026-05-28) PLATFORMS="linux/amd64,linux/arm64" # -------- preflight -------- echo "==> Preflight" docker buildx version >/dev/null || { echo "buildx not available"; exit 1; } git rev-parse --verify "$RELEASE_TAG" >/dev/null 2>&1 || { echo "Tag $RELEASE_TAG not found locally. git fetch && git checkout $RELEASE_TAG first."; exit 1; } [[ "$(git rev-parse HEAD)" == "$(git rev-parse "${RELEASE_TAG}^{commit}")" ]] || { echo "HEAD is not at $RELEASE_TAG. git checkout $RELEASE_TAG first."; exit 1; } docker buildx inspect default >/dev/null 2>&1 || docker buildx create --use --name multi --driver docker-container # Probe whether base- already exists on Hub (CI does this; saves 10 min if yes) if docker manifest inspect "${IMAGE}:${BASE_TAG}" >/dev/null 2>&1; then echo "==> Base tag ${IMAGE}:${BASE_TAG} already exists on Hub — skipping base rebuild" SKIP_BASE=1 else echo "==> Base tag ${IMAGE}:${BASE_TAG} missing — will build" SKIP_BASE=0 fi # -------- 1. base (if needed) -------- if [[ "$SKIP_BASE" == "0" ]]; then echo "==> [1/5] Build & push Dockerfile.base → ${IMAGE}:${BASE_TAG}" docker buildx build \ --platform "$PLATFORMS" \ -f Dockerfile.base \ -t "${IMAGE}:${BASE_TAG}" \ --push \ . fi # -------- 2. promote base-latest -------- echo "==> [2/5] Promote ${IMAGE}:${BASE_TAG} → ${IMAGE}:base-latest" docker buildx imagetools create -t "${IMAGE}:base-latest" "${IMAGE}:${BASE_TAG}" # -------- 3-5. variants -------- build_variant() { local suffix="$1" # "" | "-omos" | "-with-pi" | "-omos-with-pi" local install_omos="$2" local install_pi="$3" local extra_args=() [[ "$install_pi" == "true" ]] && extra_args+=(--build-arg "PI_VERSION=${PI_VERSION}") [[ "$install_omos" == "true" ]] && extra_args+=(--build-arg "OMOS_VERSION=${OMOS_VERSION}") local versioned="${IMAGE}:${RELEASE_TAG}${suffix}" local floating="${IMAGE}:latest${suffix}" echo "==> Build & push variant${suffix:-(default)} → ${versioned} + ${floating}" docker buildx build \ --platform "$PLATFORMS" \ -f Dockerfile.variant \ --build-arg "BASE_IMAGE=${IMAGE}:${BASE_TAG}" \ --build-arg "INSTALL_OPENCODE=true" \ --build-arg "INSTALL_OMOS=${install_omos}" \ --build-arg "INSTALL_PI=${install_pi}" \ ${extra_args[@]+"${extra_args[@]}"} \ -t "${versioned}" \ -t "${floating}" \ --push \ . } echo "==> [3/5] Variant: base (opencode only)" build_variant "" false false echo "==> [4/5] Variant: omos" build_variant "-omos" true false echo "==> [4/5] Variant: with-pi" build_variant "-with-pi" false true echo "==> [5/5] Variant: omos-with-pi" build_variant "-omos-with-pi" true true echo echo "==> Done. Verifying tags on Hub:" for t in \ "${RELEASE_TAG}" "latest" \ "${RELEASE_TAG}-omos" "latest-omos" \ "${RELEASE_TAG}-with-pi" "latest-with-pi" \ "${RELEASE_TAG}-omos-with-pi" "latest-omos-with-pi" \ "${BASE_TAG}" "base-latest" do d=$(docker manifest inspect "${IMAGE}:${t}" 2>/dev/null | python3 -c "import json,sys,hashlib; m=json.load(sys.stdin); print(m.get('digest','-'))" 2>/dev/null || echo "MISSING") printf " %-32s %s\n" "$t" "$d" done