#!/usr/bin/env bash # Manual publish of opencode-devbox — bypasses a 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 2 variants on top of base-: # :vX.Y.Z :latest (INSTALL_OPENCODE only) # :vX.Y.Z-omos :latest-omos (+ OMOS) # # pi was removed in v2.0.0 — there are no pi variants here anymore. # # 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 # 4. Edit RELEASE_TAG / BASE_HASH / OMOS_VERSION below to match the release. # 5. bash /path/to/this/script.sh # # Total expected time: ~15-25 min on a recent Mac (2 multi-arch builds, base # layers cache after the first variant). set -euo pipefail IMAGE="joakimp/opencode-devbox" RELEASE_TAG="v2.0.0" # EDIT per release BASE_HASH="REPLACE_ME" # sha256 of Dockerfile.base + rootfs/* + entrypoints (computed by CI logic) BASE_TAG="base-${BASE_HASH}" OMOS_VERSION="latest" # resolve from npm oh-my-opencode-slim latest, then pin 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/7] 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/7] Promote ${IMAGE}:${BASE_TAG} → ${IMAGE}:base-latest" docker buildx imagetools create -t "${IMAGE}:base-latest" "${IMAGE}:${BASE_TAG}" # -------- 3-4. variants -------- build_variant() { local suffix="$1" # "" | "-omos" local install_omos="$2" local install_opencode="${3:-true}" local extra_args=() [[ "$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=${install_opencode}" \ --build-arg "INSTALL_OMOS=${install_omos}" \ ${extra_args[@]+"${extra_args[@]}"} \ -t "${versioned}" \ -t "${floating}" \ --push \ . } echo "==> [3/4] Variant: base (opencode only)" build_variant "" false echo "==> [4/4] Variant: omos" build_variant "-omos" true echo echo "==> Done. Verifying tags on Hub:" for t in \ "${RELEASE_TAG}" "latest" \ "${RELEASE_TAG}-omos" "latest-omos" \ "${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