6cc2670a93
Validate / docs-check (push) Successful in 6s
Validate / base-change-warning (push) Successful in 12s
Validate / validate-with-pi (push) Successful in 4m5s
Validate / validate-omos (push) Successful in 4m27s
Validate / validate-base (push) Successful in 5m33s
Validate / validate-omos-with-pi (push) Successful in 12m18s
Captures the escape-hatch procedure used to ship v1.15.12 on 2026-05-28 when buildkit cache-export mode=max started returning HTTP 400 from the Hub CDN, breaking five consecutive CI publishes (runs #332/333/334/336 + a rerun). - docs/manual-host-publish.sh: the literal script that shipped v1.15.12 from a developer Mac via Orbstack, preserved as-is for future reference. - docs/manual-host-publish.md: runbook explaining when to reach for it, the four constants to edit, three ways to source BASE_HASH (CI log / Hub probe / local recompute matching base-decide's exact recipe including __pycache__/.DS_Store junk filters), and adaptations for pi-devbox / letter-suffix rebuilds / partial-failure recovery. - AGENTS.md: new Critical conventions bullet documenting the cache-from /cache-to disablement, failure shape, repo-specificity, why action pinning didn't help, the trade-off, and the re-enable condition. Cross-references CHANGELOG v1.15.12 Unreleased + the new runbook.
118 lines
4.6 KiB
Bash
Executable File
118 lines
4.6 KiB
Bash
Executable File
#!/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-<hash>
|
|
# 2. Promote → joakimp/opencode-devbox:base-latest
|
|
# 3. Build & push 4 variants on top of base-<hash>:
|
|
# :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-<hash> 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
|