Compare commits

..

11 Commits

Author SHA1 Message Date
joakimp 19f8c043bd Bump opencode 1.15.4 -> 1.15.6 + cut v1.15.6
Validate / docs-check (push) Successful in 14s
Validate / base-change-warning (push) Successful in 10s
Validate / validate-base (push) Successful in 3m33s
Validate / validate-omos-with-pi (push) Successful in 4m57s
Validate / validate-with-pi (push) Successful in 6m18s
Validate / validate-omos (push) Successful in 12m13s
Publish Docker Image / base-decide (push) Successful in 13s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke-base (push) Successful in 3m27s
Publish Docker Image / smoke-omos (push) Successful in 4m29s
Publish Docker Image / smoke-with-pi (push) Successful in 6m13s
Publish Docker Image / smoke-omos-with-pi (push) Successful in 12m31s
Publish Docker Image / build-variant-base (push) Successful in 14m33s
Publish Docker Image / build-variant-omos (push) Successful in 19m38s
Publish Docker Image / build-variant-with-pi (push) Successful in 19m0s
Publish Docker Image / build-variant-omos-with-pi (push) Successful in 30m37s
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Successful in 9s
Two upstream patch releases since v1.15.4b. Plus this release picks up
two workflow improvements that landed on main between v1.15.4b and
now (b6e4d89 pycache/DS_Store filter in base-decide, 90e5a1f doc-drift
sweep clause in AGENTS.md). No image-content changes beyond the
version bump; cache hit expected on base-35ee5fe7861a since neither
Dockerfile.base nor rootfs/ have changed.

The with-pi and omos-with-pi variants will also implicitly bump pi
0.75.3 -> 0.75.4 because PI_VERSION=latest resolves at build time.
The omos-with-pi smoke threshold (3700 MB after the v1.15.4b bump)
should accommodate two opencode patch versions plus one pi patch
without recurrence of the trip; a future bump-bump pattern would
push it again.

First release on the new CI path that exercises:
- pinned crane install (T14, v1.15.3) - only fires on real base rebuild,
  cache-hit on base means it stays unexercised this run too
- skip promote-base-latest on cache-hit (T15, v1.15.4) - active
- update-description always-and-success-of-base wrap (v1.15.4b) -
  active, will run since base variant publishes
2026-05-21 00:09:15 +02:00
joakimp 90e5a1f5d0 AGENTS.md: documentation-drift sweep as explicit pre-commit step
Validate / docs-check (push) Successful in 8s
Validate / base-change-warning (push) Successful in 12s
Validate / validate-omos (push) Successful in 4m20s
Validate / validate-omos-with-pi (push) Successful in 7m32s
Validate / validate-base (push) Successful in 9m25s
Validate / validate-with-pi (push) Failing after 14m46s
Companion to the same addition in the cloud-init and ansible repos.
Caught real drift in those repos in a recent session only because
the user explicitly asked. Codify the sweep with concrete, repo-
specific drift hotspots rather than a vague 'watch for drift' rule
that gets ignored.

Each AGENTS.md addition lists the doc files most likely to fall
behind code changes here, plus a quick-triage one-liner using
'git diff --name-only HEAD | xargs grep -l ...' so the rule is
actionable not aspirational.
2026-05-20 23:11:57 +02:00
joakimp b6e4d89a2c ci: filter __pycache__ and macOS metadata from base hash compute
Validate / docs-check (push) Successful in 14s
Validate / base-change-warning (push) Successful in 18s
Validate / validate-omos (push) Successful in 4m34s
Validate / validate-omos-with-pi (push) Successful in 4m57s
Validate / validate-with-pi (push) Successful in 6m9s
Validate / validate-base (push) Successful in 14m48s
Defensive against local-vs-CI hash divergence. `find rootfs -type f`
includes gitignored junk like rootfs/__pycache__/*.pyc and macOS
.DS_Store/._AppleDouble files, which CI's clean checkout never sees.

This bit us during v1.15.4 debugging when a stale generate-config.cpython-314.pyc
on the local rootfs/ produced base-3605aa6b6ab1 while CI computed
base-35ee5fe7861a. Took meaningful time to track down because git status
doesn't surface gitignored files.

Verified: same filter applied to current clean tree still produces
35ee5fe7861a (the published v1.15.4b base digest).
2026-05-20 22:45:27 +02:00
joakimp 8f2c9f5112 v1.15.4b: omos-with-pi threshold bump + update-description partial-publish fix
Validate / docs-check (push) Successful in 7s
Validate / base-change-warning (push) Successful in 20s
Validate / validate-base (push) Successful in 3m36s
Publish Docker Image / base-decide (push) Successful in 13s
Publish Docker Image / build-base (push) Has been skipped
Validate / validate-with-pi (push) Successful in 4m14s
Validate / validate-omos (push) Successful in 7m1s
Publish Docker Image / smoke-base (push) Successful in 3m37s
Publish Docker Image / smoke-omos (push) Successful in 4m39s
Publish Docker Image / smoke-omos-with-pi (push) Successful in 5m7s
Publish Docker Image / smoke-with-pi (push) Successful in 6m24s
Validate / validate-omos-with-pi (push) Successful in 15m59s
Publish Docker Image / build-variant-base (push) Successful in 14m12s
Publish Docker Image / build-variant-omos (push) Successful in 19m29s
Publish Docker Image / build-variant-with-pi (push) Successful in 23m7s
Publish Docker Image / build-variant-omos-with-pi (push) Successful in 26m16s
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Successful in 8s
Recovery for v1.15.4's partial publish (omos-with-pi exceeded 3500 MB
smoke threshold; other 3 variants published cleanly). Two changes:

1. omos-with-pi threshold 3500 -> 3700 MB. Compounded growth from
   opencode 1.15.0 -> 1.15.4 (4 patch versions) plus pi 0.74.0 -> 0.75.3
   (minor + 3 patches) summed in the omos-with-pi variant, just over
   the existing limit. Same pattern as prior threshold bumps (v1.14.31c,
   v1.15.0b). Restores ~150 MB headroom for routine apt-upgrade drift.

2. update-description workflow bug fix. Pre-existing latent bug exposed
   by v1.15.4's partial publish: update-description.needs includes all 4
   build-variant-* jobs, and gitea Actions' default behavior is
   'skipped need => skip dependent' \u2014 even when the job's own if:
   condition is satisfied. So when build-variant-omos-with-pi was
   skipped (because its smoke failed), update-description cascaded into
   a skip too, and Hub description didn't refresh on v1.15.4 despite
   3 variants publishing.

   Fix: wrap if: in always() + explicit success check on the base
   variant. Same fix applied to promote-base-latest preemptively (it
   has the same latent bug, currently masked by the cache-hit gate).

No image-side changes \u2014 cache hit on base-35ee5fe7861a.
2026-05-18 22:30:59 +02:00
joakimp 60eb49469e v1.15.4: bump opencode 1.15.3 -> 1.15.4
Publish Docker Image / base-decide (push) Successful in 14s
Validate / docs-check (push) Successful in 8s
Validate / base-change-warning (push) Successful in 7s
Validate / validate-base (push) Successful in 3m30s
Publish Docker Image / build-base (push) Has been skipped
Validate / validate-with-pi (push) Successful in 4m12s
Validate / validate-omos (push) Successful in 7m8s
Validate / validate-omos-with-pi (push) Successful in 5m7s
Publish Docker Image / smoke-omos (push) Successful in 4m23s
Publish Docker Image / smoke-base (push) Successful in 8m17s
Publish Docker Image / smoke-with-pi (push) Successful in 6m24s
Publish Docker Image / smoke-omos-with-pi (push) Failing after 11m14s
Publish Docker Image / build-variant-base (push) Successful in 14m38s
Publish Docker Image / build-variant-omos-with-pi (push) Has been skipped
Publish Docker Image / build-variant-omos (push) Successful in 19m40s
Publish Docker Image / build-variant-with-pi (push) Successful in 19m49s
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Bundles with the CI hardening landed on main since v1.15.3 (T14/T15 in
the operator backlog):

- Pinned crane install in promote-base-latest (replaces flaky
  imjasonh/setup-crane@v0.4 that depends on api.github.com/releases/latest
  at runtime and periodically rate-limits)
- Skip promote-base-latest on cache-hit base builds (need_build='false')

These will be exercised on this release run \u2014 if the base hash hasn't
drifted since v1.15.3 (likely cache hit), promote-base-latest should
SKIP rather than RUN, and update-description picks up the new tag.
2026-05-18 21:51:15 +02:00
joakimp 18b9c9c549 CI: harden promote-base-latest (pinned crane + skip on cache-hit)
Validate / docs-check (push) Successful in 10s
Validate / base-change-warning (push) Successful in 16s
Validate / validate-with-pi (push) Successful in 4m10s
Validate / validate-omos (push) Successful in 4m34s
Validate / validate-base (push) Has been cancelled
Validate / validate-omos-with-pi (push) Has been cancelled
Two workflow-only changes for promote-base-latest, no image-side impact:

T14 \u2014 replace imjasonh/setup-crane@v0.4 with direct pinned crane install.
The action's bootstrap script calls api.github.com/.../releases/latest
at every run to discover the crane version. That call periodically
rate-limits and returns JSON without .tag_name, jq emits 'null', the
action then downloads .../releases/download/null/... \u2192 404 \u2192 'gzip:
unexpected end of file' \u2192 exit 2. We hit this on the v1.15.3 release
(2026-05-16) where it was cosmetic only \u2014 base-latest was already
correct from cache hit \u2014 but the red-X is annoying.

Replaced with curl + tar pinned to crane v0.21.6 (latest at time of
change). Same pattern as other GitHub-sourced binaries in the
Dockerfile layer (gosu, fzf, eza etc.); operator bumps CRANE_VERSION
deliberately when wanting updates.

T15 \u2014 gate promote-base-latest on need_build == 'true'. When the base
layer's content hash hasn't changed (cache hit on existing base-<hash>
from a prior run), base-latest already points at the correct digest.
The retag is a tautology, and any transient failure of it produces a
red-X for an operation that didn't need to happen. Skipping the job
entirely on cache-hit is correct and removes a whole class of cosmetic
failure. Manual workflow_dispatch with promote_latest=true still bypasses
the gate as an escape hatch (e.g., if base-latest got hand-deleted and
needs regeneration without rebuilding the base).

This will not trigger a CI publish run (main-branch commit, no tag).
2026-05-18 21:45:10 +02:00
joakimp ad4a12b3ab v1.15.3: bump opencode 1.15.0 -> 1.15.3
Validate / base-change-warning (push) Successful in 10s
Validate / docs-check (push) Successful in 17s
Validate / validate-omos (push) Successful in 4m29s
Validate / validate-with-pi (push) Successful in 4m17s
Validate / validate-omos-with-pi (push) Successful in 8m13s
Validate / validate-base (push) Successful in 8m48s
Publish Docker Image / base-decide (push) Successful in 12s
Publish Docker Image / promote-base-latest (push) Failing after 6s
Publish Docker Image / build-base (push) Has been cancelled
Publish Docker Image / smoke-base (push) Has been cancelled
Publish Docker Image / smoke-omos (push) Has been cancelled
Publish Docker Image / smoke-with-pi (push) Has been cancelled
Publish Docker Image / smoke-omos-with-pi (push) Has been cancelled
Publish Docker Image / build-variant-base (push) Has been cancelled
Publish Docker Image / build-variant-omos (push) Has been cancelled
Publish Docker Image / build-variant-with-pi (push) Has been cancelled
Publish Docker Image / build-variant-omos-with-pi (push) Has been cancelled
Publish Docker Image / update-description (push) Has been cancelled
2026-05-16 19:54:15 +02:00
joakimp fde5a89e8b README + DOCKER_HUB: lead with no-git-clone curl-template path
Validate / base-change-warning (push) Successful in 27s
Validate / docs-check (push) Successful in 39s
Validate / validate-omos (push) Successful in 4m39s
Validate / validate-with-pi (push) Successful in 4m14s
Validate / validate-omos-with-pi (push) Successful in 8m7s
Validate / validate-base (push) Successful in 9m50s
The previous Quick Start in both surfaces led with 'git clone',
which is overkill for users who just want to run the published image.
Match pi-devbox's pattern: lead with 'mkdir; curl docker-compose.yml;
curl .env.example; edit .env; docker compose run --rm devbox'. Keep
the git-clone path as 'for hackers/forkers'.

Required pre-step: make the gitea repo public so unauthenticated
curl to the raw URL works (done out of band — repo was private until
this commit landed).
2026-05-15 18:02:37 +02:00
joakimp 034830710c workflow: use github.ref_type directly in promote/update-description if-conditions
Validate / docs-check (push) Successful in 8s
Validate / base-change-warning (push) Successful in 10s
Validate / validate-with-pi (push) Successful in 4m23s
Validate / validate-omos-with-pi (push) Successful in 5m10s
Validate / validate-omos (push) Successful in 7m5s
Validate / validate-base (push) Successful in 10m5s
Gitea Actions evaluates 'env.PROMOTE_LATEST' as empty in YAML 'if:'
contexts even though the same env var substitutes correctly in
shell run: blocks. Result: on v1.15.0/v1.15.0b tag pushes, the
build-variant-* jobs correctly pushed latest-* aliases (shell context),
but promote-base-latest and update-description got skipped (YAML
context), so the Hub README description wasn't refreshed.

Switch to evaluating github.ref_type directly in the if-conditions —
matches the production-trigger semantics and avoids the env-var
indirection that gitea evaluates inconsistently.
2026-05-15 13:50:46 +02:00
joakimp d293ddc202 v1.15.0b: bump omos smoke threshold 3200->3300, omos-with-pi 3400->3500
Validate / base-change-warning (push) Successful in 9s
Validate / docs-check (push) Successful in 18s
Validate / validate-omos (push) Successful in 4m22s
Validate / validate-with-pi (push) Successful in 4m10s
Publish Docker Image / base-decide (push) Successful in 15s
Publish Docker Image / build-base (push) Has been skipped
Validate / validate-base (push) Successful in 5m20s
Publish Docker Image / smoke-base (push) Successful in 3m34s
Publish Docker Image / smoke-with-pi (push) Successful in 4m12s
Publish Docker Image / smoke-omos (push) Successful in 7m2s
Publish Docker Image / smoke-omos-with-pi (push) Successful in 4m58s
Validate / validate-omos-with-pi (push) Successful in 17m33s
Publish Docker Image / build-variant-base (push) Successful in 14m18s
Publish Docker Image / build-variant-with-pi (push) Successful in 19m22s
Publish Docker Image / build-variant-omos (push) Successful in 18m50s
Publish Docker Image / build-variant-omos-with-pi (push) Successful in 31m58s
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
opencode 1.15.0 grew the omos image to 3206 MB, 6 MB over the existing
3200 MB threshold, causing smoke-omos to fail and build-variant-omos
to be skipped in v1.15.0. Bump thresholds with ~100 MB headroom for
routine apt-get upgrade drift.

No image-side changes — pure smoke threshold update. v1.15.0b will hit
the base hash cache and run only the variant deltas.
2026-05-15 10:35:08 +02:00
joakimp 910378fe06 v1.15.0: opencode bump + git clone retry + pi-devbox sibling mention
Validate / docs-check (push) Successful in 11s
Validate / base-change-warning (push) Successful in 56s
Publish Docker Image / base-decide (push) Successful in 17s
Publish Docker Image / build-base (push) Has been skipped
Validate / validate-base (push) Successful in 3m23s
Publish Docker Image / smoke-base (push) Successful in 3m34s
Validate / validate-omos (push) Successful in 6m52s
Publish Docker Image / smoke-with-pi (push) Successful in 4m10s
Publish Docker Image / smoke-omos-with-pi (push) Successful in 4m58s
Validate / validate-omos-with-pi (push) Failing after 10m27s
Validate / validate-with-pi (push) Failing after 10m38s
Publish Docker Image / smoke-omos (push) Failing after 9m35s
Publish Docker Image / build-variant-omos (push) Has been skipped
Publish Docker Image / build-variant-base (push) Successful in 15m36s
Publish Docker Image / build-variant-with-pi (push) Successful in 16m52s
Publish Docker Image / build-variant-omos-with-pi (push) Successful in 22m5s
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
- Bump OPENCODE_VERSION 1.14.50 -> 1.15.0 in Dockerfile.variant.
- Wrap pi-toolkit/pi-extensions git clone in Dockerfile.variant in a
  5-attempt retry loop with linear backoff (matches pi-devbox pattern).
  gitea.jordbo.se occasionally returns transient HTTP 500s that
  previously broke with-pi/omos-with-pi variant builds.
- Add 'Sibling images' section to DOCKER_HUB.md mentioning
  joakimp/pi-devbox as the pi-only counterpart.
- CHANGELOG entry for v1.15.0 with full notes.
2026-05-15 09:56:01 +02:00
9 changed files with 197 additions and 47 deletions
+10 -1
View File
@@ -81,11 +81,20 @@ content:
```sh
{
cat Dockerfile.base
find rootfs -type f -print0 | sort -z | xargs -0 cat
find rootfs -type f \
! -path '*/__pycache__/*' \
! -name '*.pyc' \
! -name '.DS_Store' \
! -name '._*' \
-print0 | sort -z | xargs -0 cat
cat entrypoint.sh entrypoint-user.sh
} | sha256sum | cut -c1-12
```
Junk filters keep the local recompute reproducible against CI's clean
checkout — `__pycache__/*.pyc` and macOS metadata files (`.DS_Store`,
`._AppleDouble`) are gitignored but still walked by `find -type f`.
The 12-character truncated hash becomes `base-<hash>`. Probe Docker Hub
for this tag via `docker manifest inspect`:
+51 -4
View File
@@ -63,10 +63,19 @@ jobs:
run: |
# Hash inputs that determine the base image's contents.
# Order is fixed via `find -print0 | sort -z` for reproducibility.
# Junk filters: __pycache__/*.pyc and macOS metadata (.DS_Store,
# ._AppleDouble) are gitignored locally but still picked up by
# `find rootfs -type f`, which would diverge the local hash from
# CI's clean checkout. Exclude them defensively here.
HASH=$(
{
cat Dockerfile.base
find rootfs -type f -print0 2>/dev/null | sort -z | xargs -0 cat 2>/dev/null
find rootfs -type f \
! -path '*/__pycache__/*' \
! -name '*.pyc' \
! -name '.DS_Store' \
! -name '._*' \
-print0 2>/dev/null | sort -z | xargs -0 cat 2>/dev/null
cat entrypoint.sh entrypoint-user.sh
} | sha256sum | cut -c1-12
)
@@ -526,12 +535,40 @@ jobs:
- build-variant-omos
- build-variant-with-pi
- build-variant-omos-with-pi
if: env.PROMOTE_LATEST == 'true'
# Skip on cache-hit base builds: when need_build=false, base-latest
# already points at the same digest as base-<hash>, so the retag is
# a tautology and any transient failure of it is purely cosmetic.
# Manual workflow_dispatch with promote_latest=true overrides this
# gate as an escape hatch (e.g., if base-latest got hand-deleted).
#
# `always()` wrapper + explicit base-variant success check protects
# against the gitea-Actions default of "skipped need => skip dependent":
# a partial-publish run (e.g., omos-with-pi smoke fails) shouldn't
# prevent the base-latest alias from advancing on a real base rebuild.
if: |
always() &&
needs.build-variant-base.result == 'success' &&
(inputs.promote_latest == 'true' ||
(github.ref_type == 'tag' && needs.base-decide.outputs.need_build == 'true'))
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- uses: imjasonh/setup-crane@v0.4
# Direct pinned install instead of imjasonh/setup-crane@v0.4. The
# action's bootstrap script calls api.github.com/.../releases/latest
# to discover the crane version, which periodically rate-limits and
# produces tag=null → download from .../download/null/... → 404 →
# 'gzip: unexpected end of file' → exit 2. Pinning removes the
# runtime dependency on GitHub API entirely. Bump CRANE_VERSION
# deliberately when you want updates.
- name: Install crane (pinned)
env:
CRANE_VERSION: v0.21.6
run: |
set -eux
curl -fsSL "https://github.com/google/go-containerregistry/releases/download/${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" \
| tar -xz -C /usr/local/bin crane
crane version
- name: Login (crane)
run: |
crane auth login docker.io \
@@ -550,7 +587,17 @@ jobs:
- build-variant-omos
- build-variant-with-pi
- build-variant-omos-with-pi
if: env.PROMOTE_LATEST == 'true'
# Run when at least the base variant published — don't let a single
# variant failure (e.g., omos-with-pi smoke threshold) prevent Hub
# description refresh for the other variants that did publish.
# Without this `always()` wrapper, gitea Actions' default behavior
# of "skipped need => skip dependent" cascades from any failed/
# skipped build-variant-* into update-description, and the Hub
# description goes stale on partial-publish releases.
if: |
always() &&
needs.build-variant-base.result == 'success' &&
(github.ref_type == 'tag' || inputs.promote_latest == 'true')
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
+2
View File
@@ -43,6 +43,8 @@ When bumping the opencode version, bump `OPENCODE_VERSION` in `Dockerfile.varian
- `.env.example` must be hand-updated to match Dockerfile/entrypoint behavior — it is not auto-generated.
Release-day checklist: README → (regenerate DOCKER_HUB.md only if HUB_TEMPLATE changed) → promote CHANGELOG Unreleased → grep AGENTS.md for stale counts → commit → tag → push tag.
**Between releases the same coupling applies.** Doc drift is not just a release-day concern — a workflow tweak, entrypoint change, or `generate-config.py` refactor can leave any of these four files lying. Before committing a non-release change, grep the docs for references to what you touched: `git diff --name-only HEAD | xargs -I{} grep -l 'thing-you-changed' README.md AGENTS.md DOCKER_HUB.md .gitea/README.md .env.example`. If a doc says "four variants" / "two phases" / "runs on amd64 only" and your change made that no longer true, fix it in the same commit.
- **GitHub/Gitea-sourced binaries float by default** — gosu, fzf, git-lfs, nvim, bat, eza, zoxide, uv, gitea-mcp, Go, oh-my-opencode-slim all default to `latest`. Each build-time install step reads the `/releases/latest` Location redirect (or the go.dev JSON feed for Go) and derives the concrete version. Use the same `ARCH` case-switch pattern for multi-arch support (amd64/arm64). Intentional pins: `OPENCODE_VERSION` (drives the image tag), `NODE_VERSION=22` (major pin), `DEBIAN_VERSION=trixie-slim` (OS base). Adding a new upstream tool: follow the existing floated-version pattern, don't hardcode a specific tag.
- **Resolved versions are logged by the smoke test** — `scripts/smoke-test.sh` prints a "Resolved component versions" table as its first step. CI logs always capture what got baked into a given image even when ARGs default to `latest`.
- **Shell scripts use `set -euo pipefail`** — both entrypoints are strict. Errors in volume chown or SSH permission operations are intentionally suppressed with `|| true`.
+49
View File
@@ -8,6 +8,55 @@ Tags follow `v{opencode_version}[letter]` — bare tag for the first build on a
## Unreleased
## v1.15.6 — 2026-05-21
opencode 1.15.4 → 1.15.6 bump (two upstream patch releases) plus two workflow improvements that landed on `main` between v1.15.4b and now. No image-content changes beyond the version bump; cache hit expected on `base-35ee5fe7861a` (no `Dockerfile.base` or `rootfs/` edits).
- **Bump:** opencode 1.15.4 → 1.15.6 (`OPENCODE_VERSION` in `Dockerfile.variant`). The `with-pi` and `omos-with-pi` variants will also implicitly pick up pi 0.75.3 → 0.75.4 since `PI_VERSION=latest` resolves at build time.
- **CI: defensive `__pycache__` and macOS-metadata filter in `base-decide` hash compute.** `find rootfs -type f` previously included gitignored junk like `rootfs/__pycache__/*.pyc`, `.DS_Store`, and `._AppleDouble` files — which CI's clean checkout never sees. This bit us during v1.15.4 debugging when a stale `generate-config.cpython-314.pyc` on the local rootfs/ produced `base-3605aa6b6ab1` while CI computed `base-35ee5fe7861a`. The filter is a no-op on a clean tree (verified to still produce `35ee5fe7861a` post-filter), but defends against future stale-pyc / Finder-touched-rootfs hash mismatches. `.gitea/README.md` updated in lockstep. (commit `b6e4d89`)
- **AGENTS.md: documentation drift sweep as explicit pre-commit workflow step.** Codifies the rule that non-release commits must also grep docs for stale claims about behaviour they change, with concrete repo-specific drift hotspots. Companion clause added across the wider repo set (cloud-init, ansible, pi-devbox, pi-extensions, pi-toolkit, cli_utils, proxmox) the same day. (commit `90e5a1f`)
- **First release that exercises both the pinned-crane install (T14, v1.15.3) and the skip-promote-on-cache-hit guard (T15, v1.15.4) on this CI run path** — still cache-hit on base, so `promote-base-latest` should remain skipped via T15 and the pinned crane install will only fire when a real base rebuild happens.
## v1.15.4b — 2026-05-18
Recovery release for v1.15.4 — the `omos-with-pi` variant landed at >3500 MB and tripped the smoke threshold, so `smoke-omos-with-pi` and `build-variant-omos-with-pi` were skipped. The other three variants (base, omos, with-pi) published cleanly. Plus a latent workflow bug fix exposed by the partial publish.
- **Smoke threshold bump:** `omos-with-pi` 3500 → 3700 MB. Compounded growth: opencode 1.15.0 → 1.15.4 (4 patch versions) plus pi 0.74.0 → 0.75.3 (minor + 3 patches) both added a few MB each, and they sum in the omos-with-pi variant. Same pattern as previous threshold bumps (v1.14.31c, v1.15.0b); restores ~150 MB headroom.
- **Workflow fix — `update-description` no longer skips on partial publish.** Pre-existing latent bug: `update-description.needs` includes all four `build-variant-*` jobs, and gitea Actions' default behavior is "skipped need ⇒ skip dependent". When `build-variant-omos-with-pi` got skipped (because its smoke failed), `update-description` cascaded into a skip even though the job's `if:` condition (`tag pushed`) was true. Result: Hub description wasn't refreshed on v1.15.4 despite three variants publishing. Fix: wrap the `if:` in `always() && needs.build-variant-base.result == 'success' && ...` so the job runs as long as the base variant published, regardless of what other variants did.
- **Same fix applied to `promote-base-latest`** — had the identical latent bug. Currently masked by the cache-hit skip, but would have surfaced on a real-base-rebuild release with a single failed variant.
- No image-side changes from v1.15.4. Cache hit on the same base hash (`base-35ee5fe7861a`).
## v1.15.4 — 2026-05-18
opencode 1.15.3 → 1.15.4 bump (one upstream patch release), bundled with the CI hardening that landed on main between v1.15.3 and now.
- **Bump:** opencode 1.15.3 → 1.15.4 (`OPENCODE_VERSION` in `Dockerfile.variant`).
- **CI: pinned crane install in `promote-base-latest`.** Replaced `imjasonh/setup-crane@v0.4` with a direct `curl + tar` install pinned to crane v0.21.6. The action's bootstrap script calls `api.github.com/.../releases/latest` to discover what crane version to install. That call periodically rate-limits and produces `tag=null` → the action downloads `releases/download/null/...` → 404 → `gzip: unexpected end of file` → exit 2. We hit this on v1.15.3 (cosmetic failure since base-latest was already correct from cache hit). Pinned install removes the runtime GitHub API dependency entirely. Bump `CRANE_VERSION` deliberately when wanting updates, same pattern as the other GitHub-sourced binaries in the Dockerfile layer.
- **CI: skip `promote-base-latest` on cache-hit base builds.** When the base layer hash hasn't changed (cache-hit on the existing `base-<hash>` from a previous run), `base-latest` already points at the correct digest, so the retag is a tautology. Job now skipped entirely when `needs.base-decide.outputs.need_build == 'false'`. Manual `workflow_dispatch` with `promote_latest: true` overrides the gate as an escape hatch for hand-recovery scenarios.
- No image-side changes from the v1.15.3 baseline beyond the opencode npm version. Smoke thresholds unchanged.
## v1.15.3 — 2026-05-16
opencode 1.15.0 → 1.15.3 bump (three upstream patch releases).
- **Bump:** opencode 1.15.0 → 1.15.3 (`OPENCODE_VERSION` in `Dockerfile.variant`).
- No container-side changes. Smoke thresholds from v1.15.0b unchanged.
## v1.15.0b — 2026-05-15
Rebuild of v1.15.0 with one fix — v1.15.0's `omos` variant landed at 3206 MB, 6 MB over the 3200 MB smoke threshold, so `smoke-omos` failed and `build-variant-omos` was skipped. opencode 1.15.0 grew slightly vs 1.14.50, leaving zero headroom on the existing threshold.
- **Smoke threshold bump:** `omos` 3200 → 3300 MB, `omos-with-pi` 3400 → 3500 MB. Restores ~100 MB headroom for routine apt-get upgrade drift between releases. Documented inline in `scripts/smoke-test.sh`. No image-side changes — cache hits across the board, just a re-publish on the bumped threshold.
## v1.15.0 — 2026-05-15
opencode 1.14.50 → 1.15.0 bump (upstream minor release).
- **Bump:** opencode 1.14.50 → 1.15.0 (`OPENCODE_VERSION` in `Dockerfile.variant`).
- **Resilience:** `git clone` for pi-toolkit and pi-extensions in `Dockerfile.variant` is now wrapped in a 5-attempt retry loop with linear backoff (5s, 10s, 15s, 20s, 25s = up to ~75s total). gitea.jordbo.se occasionally returns transient HTTP 500s on the first request after idle, which previously broke the with-pi and omos-with-pi variant builds. Same pattern landed in pi-devbox repo concurrently.
- **Docs:** `DOCKER_HUB.md` mentions `joakimp/pi-devbox` as a sibling image — the pi-only build that uses this image's base layer as its parent. Generator template (`scripts/generate-dockerhub-md.py`) updated and regenerated. Hub size: 5905 bytes (well under the 25 kB limit).
- **Recovery from v1.14.50c partial publish:** the `latest-omos`, `v1.14.50c-omos` Hub gap is closed by this release — `latest-omos` will move forward to v1.15.0 once all four variants publish cleanly. Users on the floating tag were unaffected (still pointing at v1.14.41b until now).
## v1.14.50c — 2026-05-14
Recovery release for v1.14.50b's missing variants. v1.14.50b shipped only the `base` variant; `omos`, `with-pi`, and `omos-with-pi` were lost to a runner-fleet incident (see postmortem below).
+20 -16
View File
@@ -17,6 +17,21 @@ All variants support `linux/amd64` and `linux/arm64`.
## Quick Start
For a fully-configured environment with persistent state (opencode config, mempalace memory, neovim plugins, bash history) surviving container recreation, use docker-compose. **You don't need to clone the repo** — just grab two template files:
```bash
mkdir -p ~/opencode-devbox && cd ~/opencode-devbox
curl -O https://gitea.jordbo.se/joakimp/opencode-devbox/raw/branch/main/docker-compose.yml
curl -fsSL https://gitea.jordbo.se/joakimp/opencode-devbox/raw/branch/main/.env.example -o .env
# Edit .env — set OPENCODE_PROVIDER, the matching API key,
# WORKSPACE_PATH, GIT_USER_NAME, GIT_USER_EMAIL.
docker compose run --rm devbox
```
This drops you straight into opencode with your project mounted at `/workspace`. Use `bash` as the command (e.g. `docker compose run --rm devbox bash`) to land in a shell first — useful for `aws sso login`, `pi` (on `*-with-pi` variants), or multi-harness workflows.
**One-shot run, no persistence:**
```bash
docker run -it --rm \
-e ANTHROPIC_API_KEY=your-key \
@@ -28,22 +43,7 @@ docker run -it --rm \
joakimp/opencode-devbox:latest
```
Drops you straight into opencode with your project mounted at `/workspace`.
For an interactive shell first (useful for AWS SSO login, multi-harness workflows, or just `bash`):
```bash
docker run -it --rm \
-e ANTHROPIC_API_KEY=your-key \
-e OPENCODE_PROVIDER=anthropic \
-v ~/projects:/workspace \
-v ~/.ssh:/home/developer/.ssh:ro \
joakimp/opencode-devbox:latest bash
```
Then run `opencode`, `pi` (on `*-with-pi` variants), or `aws sso login` from the shell.
For docker-compose users, the source repo provides `docker-compose.yml`, `.env.example`, and a one-liner `docker compose up -d` workflow with named volumes pre-wired.
Full setup guide — authentication for each provider (Anthropic, OpenAI, Bedrock SSO + static), persistence model, build args, troubleshooting: <https://gitea.jordbo.se/joakimp/opencode-devbox#readme>
## What's Inside
@@ -86,6 +86,10 @@ Full persistence reference, including multi-user (`SIGNUM`) isolation and host b
- **Issues / source / docker-compose templates:** <https://gitea.jordbo.se/joakimp/opencode-devbox>
- **Agent-facing internals** (for future maintainers / coding agents working in the repo): <https://gitea.jordbo.se/joakimp/opencode-devbox/src/branch/main/AGENTS.md>
## Sibling images
- **[`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox)** — pi-only image built on top of this image's base layer. Smaller (~700 MB) and version-tracks the [pi npm package](https://www.npmjs.com/package/@earendil-works/pi-coding-agent) directly. Use this if you want pi without opencode. Source: <https://gitea.jordbo.se/joakimp/pi-devbox>
## License
MIT. See <https://gitea.jordbo.se/joakimp/opencode-devbox/src/branch/main/LICENSE>.
+14 -5
View File
@@ -32,7 +32,7 @@ ARG USER_NAME=developer
# ── Install opencode via npm ─────────────────────────────────────────
ARG INSTALL_OPENCODE=true
ARG OPENCODE_VERSION=1.14.50
ARG OPENCODE_VERSION=1.15.6
RUN if [ "${INSTALL_OPENCODE}" = "true" ]; then \
NPM_CONFIG_PREFIX=/usr npm install -g opencode-ai@${OPENCODE_VERSION} && \
opencode --version ; \
@@ -47,16 +47,25 @@ ARG PI_VERSION=latest
ARG PI_TOOLKIT_REF=main
ARG PI_EXTENSIONS_REF=main
RUN if [ "${INSTALL_PI}" = "true" ]; then \
set -e && \
git_clone_retry() { \
url="$1"; ref="$2"; dest="$3"; \
for i in 1 2 3 4 5; do \
if git clone --depth 1 --branch "$ref" "$url" "$dest"; then return 0; fi; \
rm -rf "$dest"; \
echo "git clone $url failed (attempt $i/5), retrying in $((i*5))s..."; \
sleep $((i*5)); \
done; \
return 1; \
} && \
if [ "${PI_VERSION}" = "latest" ]; then \
NPM_CONFIG_PREFIX=/usr npm install -g @earendil-works/pi-coding-agent ; \
else \
NPM_CONFIG_PREFIX=/usr npm install -g @earendil-works/pi-coding-agent@${PI_VERSION} ; \
fi && \
pi --version && \
git clone --depth 1 --branch "${PI_TOOLKIT_REF}" \
https://gitea.jordbo.se/joakimp/pi-toolkit.git /opt/pi-toolkit && \
git clone --depth 1 --branch "${PI_EXTENSIONS_REF}" \
https://gitea.jordbo.se/joakimp/pi-extensions.git /opt/pi-extensions && \
git_clone_retry https://gitea.jordbo.se/joakimp/pi-toolkit.git "${PI_TOOLKIT_REF}" /opt/pi-toolkit && \
git_clone_retry https://gitea.jordbo.se/joakimp/pi-extensions.git "${PI_EXTENSIONS_REF}" /opt/pi-extensions && \
echo "pi-toolkit at $(cd /opt/pi-toolkit && git rev-parse --short HEAD)" && \
echo "pi-extensions at $(cd /opt/pi-extensions && git rev-parse --short HEAD)" ; \
fi
+22 -2
View File
@@ -8,8 +8,28 @@ The official `ghcr.io/anomalyco/opencode` image (now archived) was Alpine-based
## Quick Start
**Just want to run it?** No git clone needed — grab the two template files:
```bash
mkdir -p ~/opencode-devbox && cd ~/opencode-devbox
# Pull docker-compose.yml and the .env template
curl -O https://gitea.jordbo.se/joakimp/opencode-devbox/raw/branch/main/docker-compose.yml
curl -fsSL https://gitea.jordbo.se/joakimp/opencode-devbox/raw/branch/main/.env.example -o .env
# Edit .env — at minimum: OPENCODE_PROVIDER, the matching API key,
# WORKSPACE_PATH, GIT_USER_NAME, GIT_USER_EMAIL.
$EDITOR .env
# Pull and run
docker compose run --rm devbox
```
This pulls `joakimp/opencode-devbox:latest` from Docker Hub, mounts `WORKSPACE_PATH` at `/workspace`, and drops you straight into opencode. Use `bash` instead of (no command) to land in a shell first — useful for `aws sso login`, `pi`, `omos`, etc.
**Want to hack on the image itself, follow upstream changes, or rebuild from source?** Clone the repo:
```bash
# Clone
git clone ssh://gitea.jordbo.se:2222/joakimp/opencode-devbox.git
cd opencode-devbox
@@ -17,7 +37,7 @@ cd opencode-devbox
cp .env.example .env
# Edit .env with your provider, API key, workspace path, git config
# Install git hooks (secret scanning)
# Install git hooks (secret scanning) before committing
brew install gitleaks # macOS / Linuxbrew
./setup-hooks.sh
+20 -16
View File
@@ -71,6 +71,21 @@ All variants support `linux/amd64` and `linux/arm64`.
## Quick Start
For a fully-configured environment with persistent state (opencode config, mempalace memory, neovim plugins, bash history) surviving container recreation, use docker-compose. **You don't need to clone the repo** — just grab two template files:
```bash
mkdir -p ~/opencode-devbox && cd ~/opencode-devbox
curl -O https://gitea.jordbo.se/joakimp/opencode-devbox/raw/branch/main/docker-compose.yml
curl -fsSL https://gitea.jordbo.se/joakimp/opencode-devbox/raw/branch/main/.env.example -o .env
# Edit .env — set OPENCODE_PROVIDER, the matching API key,
# WORKSPACE_PATH, GIT_USER_NAME, GIT_USER_EMAIL.
docker compose run --rm devbox
```
This drops you straight into opencode with your project mounted at `/workspace`. Use `bash` as the command (e.g. `docker compose run --rm devbox bash`) to land in a shell first — useful for `aws sso login`, `pi` (on `*-with-pi` variants), or multi-harness workflows.
**One-shot run, no persistence:**
```bash
docker run -it --rm \\
-e ANTHROPIC_API_KEY=your-key \\
@@ -82,22 +97,7 @@ docker run -it --rm \\
joakimp/opencode-devbox:latest
```
Drops you straight into opencode with your project mounted at `/workspace`.
For an interactive shell first (useful for AWS SSO login, multi-harness workflows, or just `bash`):
```bash
docker run -it --rm \\
-e ANTHROPIC_API_KEY=your-key \\
-e OPENCODE_PROVIDER=anthropic \\
-v ~/projects:/workspace \\
-v ~/.ssh:/home/developer/.ssh:ro \\
joakimp/opencode-devbox:latest bash
```
Then run `opencode`, `pi` (on `*-with-pi` variants), or `aws sso login` from the shell.
For docker-compose users, the source repo provides `docker-compose.yml`, `.env.example`, and a one-liner `docker compose up -d` workflow with named volumes pre-wired.
Full setup guide — authentication for each provider (Anthropic, OpenAI, Bedrock SSO + static), persistence model, build args, troubleshooting: <{GITEA}#readme>
## What's Inside
@@ -140,6 +140,10 @@ Full persistence reference, including multi-user (`SIGNUM`) isolation and host b
- **Issues / source / docker-compose templates:** <{GITEA}>
- **Agent-facing internals** (for future maintainers / coding agents working in the repo): <{GITEA}/src/branch/main/AGENTS.md>
## Sibling images
- **[`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox)** — pi-only image built on top of this image's base layer. Smaller (~700 MB) and version-tracks the [pi npm package](https://www.npmjs.com/package/@earendil-works/pi-coding-agent) directly. Use this if you want pi without opencode. Source: <https://gitea.jordbo.se/joakimp/pi-devbox>
## License
MIT. See <{GITEA}/src/branch/main/LICENSE>.
+9 -3
View File
@@ -289,14 +289,20 @@ SIZE_BYTES=$(docker image inspect --format='{{.Size}}' "$IMAGE")
SIZE_MB=$((SIZE_BYTES / 1024 / 1024))
echo " Uncompressed size: ${SIZE_MB} MB"
# Thresholds (uncompressed): base 2500 MB, omos 3200 MB, with-pi adds ~150 MB.
# Thresholds (uncompressed): base 2500 MB, omos 3300 MB, with-pi adds ~150 MB.
# omos bumped 3000→3200 on v1.14.31c — mempalace-toolkit bake-in pushed the
# baseline; bumped 3200→3300 on v1.15.0 — opencode 1.15.0 came in at
# 3206 MB, leaving zero headroom for routine apt-get upgrade drift.
# omos-with-pi bumped 3400→3500 on v1.15.0 alongside the omos bump.
# omos-with-pi bumped 3500→3700 on v1.15.4b — omos+pi compounded as both
# upstream packages grew (opencode 1.15.0→1.15.4, pi 0.74.0→0.75.3) and
# the variant landed just over 3500 in v1.15.4's smoke.
# omos variant to ~3.1 GB. Functional smoke checks all pass; this is a
# guardrail, not a performance limit.
THRESHOLD=2500
[ "$VARIANT" = "omos" ] && THRESHOLD=3200
[ "$VARIANT" = "omos" ] && THRESHOLD=3300
[ "$VARIANT" = "with-pi" ] && THRESHOLD=2700
[ "$VARIANT" = "omos-with-pi" ] && THRESHOLD=3400
[ "$VARIANT" = "omos-with-pi" ] && THRESHOLD=3700
if [ "$SIZE_MB" -gt "$THRESHOLD" ]; then
fail "image size ${SIZE_MB} MB exceeds threshold ${THRESHOLD} MB for variant=$VARIANT"
else