name: Validate # Lightweight validation on pushes to main. Builds single-arch (amd64), # runs the smoke test, and checks image size — without pushing anything # to Docker Hub. Tag pushes are handled by docker-publish-split.yml which # does the full multi-arch split-base build-and-push. # # Trade-off: variant builds here use the published `base-latest` image # from Docker Hub as their parent, NOT a locally-built base. This is # because `docker/build-push-action@v7` runs each invocation in its own # buildx container context, so an image loaded into the host docker # daemon by step N is not visible to step N+1's buildx invocation. # Building base + variant in the same job would require either pushing # the base to a registry or sharing a buildx instance across steps — both # significantly more complex than just using the published base. # # Consequence: PRs/pushes that change Dockerfile.base, rootfs/, or # entrypoint*.sh are NOT exercised by this workflow. The release path # (docker-publish-split.yml on tag push) does build the new base, so # release tags are the gate that fully validates base-image changes. # The base-change-warning job below surfaces a runtime warning when this # blind-spot applies. # # Because of this, the fork/recall *registration* smoke checks (which depend on # the base entrypoint running `pi install /opt/`) are warn-only here: # smoke-test.sh leaves STRICT_REGISTRATION unset on this path, so a base-latest # that lags the entrypoint in the current commit can't red the run with a false # negative. The release smoke jobs build the base fresh and set # STRICT_REGISTRATION=1 to enforce those checks. The build-time /opt + # node_modules checks stay hard in both paths. on: push: branches: - main paths-ignore: - 'CHANGELOG.md' - 'README.md' - 'DOCKER_HUB.md' - 'deploy/**' - '.gitleaks.toml' pull_request: branches: - main jobs: docs-check: # Fails if DOCKER_HUB.md is out of sync with what generate-dockerhub-md.py # would produce from README.md. Keeps the two docs from drifting. runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Check DOCKER_HUB.md is in sync with README.md run: | python3 scripts/generate-dockerhub-md.py --check base-change-warning: # Surfaces a warning when this commit changes base-image inputs # (Dockerfile.base, rootfs/, entrypoint*.sh). validate.yml uses # Hub's base-latest as the parent for variant builds, so changes to # those files are NOT exercised here — only release tags rebuild the # base via docker-publish-split.yml. runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 2 - name: Detect base-input changes run: | set -e if ! git diff --name-only HEAD~1 HEAD 2>/dev/null \ | grep -qE '^(Dockerfile\.base|rootfs/|entrypoint.*\.sh)$'; then echo "No base-image inputs changed in this commit — validate.yml fully exercises the published base-latest." exit 0 fi echo "::warning::This commit changes base-image inputs (Dockerfile.base, rootfs/, or entrypoint*.sh). validate.yml uses Hub's base-latest as the parent for variant builds, so the new base is NOT exercised by this workflow. Cut a release tag, or run a workflow_dispatch of docker-publish-split.yml against a test tag (e.g. v0.0.0-base-test, promote_latest=false) for end-to-end validation of the new base." echo "Changed base-input files:" git diff --name-only HEAD~1 HEAD | grep -E '^(Dockerfile\.base|rootfs/|entrypoint.*\.sh)$' validate-base: runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Force IPv4 for Docker Hub run: | echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf # The runner's overlay disk starts ~70% full. `load: true` peak disk # is tarball + unpacked image + buildx cache, which tips it over # once the image crosses ~3 GB. Strip catthehacker-resident # toolchains we never use and any stale docker state up front. - name: Reclaim runner disk run: | set -x df -h / || true rm -rf \ /opt/hostedtoolcache \ /opt/microsoft \ /opt/az \ /opt/ghc \ /usr/local/.ghcup \ /usr/share/dotnet \ /usr/share/swift \ /usr/local/lib/android \ /usr/local/share/powershell \ /usr/local/share/chromium \ /usr/local/share/boost \ /usr/lib/jvm 2>/dev/null || true apt-get clean || true rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* || true docker system df || true docker system prune -af --volumes || true docker builder prune -af || true df -h / || true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 with: driver-opts: network=host - name: Build base image (amd64, load to local daemon) uses: docker/build-push-action@v7 with: context: . file: Dockerfile.variant platforms: linux/amd64 push: false load: true build-args: | BASE_IMAGE=joakimp/opencode-devbox:base-latest tags: opencode-devbox:ci-base - name: Smoke test run: | bash scripts/smoke-test.sh opencode-devbox:ci-base --variant base validate-omos: runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Force IPv4 for Docker Hub run: | echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf - name: Reclaim runner disk run: | set -x df -h / || true rm -rf \ /opt/hostedtoolcache \ /opt/microsoft \ /opt/az \ /opt/ghc \ /usr/local/.ghcup \ /usr/share/dotnet \ /usr/share/swift \ /usr/local/lib/android \ /usr/local/share/powershell \ /usr/local/share/chromium \ /usr/local/share/boost \ /usr/lib/jvm 2>/dev/null || true apt-get clean || true rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* || true docker system df || true docker system prune -af --volumes || true docker builder prune -af || true df -h / || true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 with: driver-opts: network=host - name: Build omos image (amd64, load to local daemon) uses: docker/build-push-action@v7 with: context: . file: Dockerfile.variant platforms: linux/amd64 push: false load: true build-args: | BASE_IMAGE=joakimp/opencode-devbox:base-latest INSTALL_OMOS=true tags: opencode-devbox:ci-omos - name: Smoke test run: | bash scripts/smoke-test.sh opencode-devbox:ci-omos --variant omos validate-with-pi: runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Force IPv4 for Docker Hub run: | echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf - name: Reclaim runner disk run: | set -x df -h / || true rm -rf \ /opt/hostedtoolcache \ /opt/microsoft \ /opt/az \ /opt/ghc \ /usr/local/.ghcup \ /usr/share/dotnet \ /usr/share/swift \ /usr/local/lib/android \ /usr/local/share/powershell \ /usr/local/share/chromium \ /usr/local/share/boost \ /usr/lib/jvm 2>/dev/null || true apt-get clean || true rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* || true docker system df || true docker system prune -af --volumes || true docker builder prune -af || true df -h / || true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 with: driver-opts: network=host - name: Build with-pi image (amd64, load to local daemon) uses: docker/build-push-action@v7 with: context: . file: Dockerfile.variant platforms: linux/amd64 push: false load: true build-args: | BASE_IMAGE=joakimp/opencode-devbox:base-latest INSTALL_PI=true tags: opencode-devbox:ci-with-pi - name: Smoke test run: | bash scripts/smoke-test.sh opencode-devbox:ci-with-pi --variant with-pi validate-omos-with-pi: runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Force IPv4 for Docker Hub run: | echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf - name: Reclaim runner disk run: | set -x df -h / || true rm -rf \ /opt/hostedtoolcache \ /opt/microsoft \ /opt/az \ /opt/ghc \ /usr/local/.ghcup \ /usr/share/dotnet \ /usr/share/swift \ /usr/local/lib/android \ /usr/local/share/powershell \ /usr/local/share/chromium \ /usr/local/share/boost \ /usr/lib/jvm 2>/dev/null || true apt-get clean || true rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* || true docker system df || true docker system prune -af --volumes || true docker builder prune -af || true df -h / || true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 with: driver-opts: network=host - name: Build omos+with-pi image (amd64, load to local daemon) uses: docker/build-push-action@v7 with: context: . file: Dockerfile.variant platforms: linux/amd64 push: false load: true build-args: | BASE_IMAGE=joakimp/opencode-devbox:base-latest INSTALL_OMOS=true INSTALL_PI=true tags: opencode-devbox:ci-omos-with-pi - name: Smoke test run: | bash scripts/smoke-test.sh opencode-devbox:ci-omos-with-pi --variant omos-with-pi validate-pi-only: runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Force IPv4 for Docker Hub run: | echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf - name: Reclaim runner disk run: | set -x df -h / || true rm -rf \ /opt/hostedtoolcache \ /opt/microsoft \ /opt/az \ /opt/ghc \ /usr/local/.ghcup \ /usr/share/dotnet \ /usr/share/swift \ /usr/local/lib/android \ /usr/local/share/powershell \ /usr/local/share/chromium \ /usr/local/share/boost \ /usr/lib/jvm 2>/dev/null || true apt-get clean || true rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* || true docker system df || true docker system prune -af --volumes || true docker builder prune -af || true df -h / || true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 with: driver-opts: network=host - name: Build pi-only image (amd64, load to local daemon) uses: docker/build-push-action@v7 with: context: . file: Dockerfile.variant platforms: linux/amd64 push: false load: true build-args: | BASE_IMAGE=joakimp/opencode-devbox:base-latest INSTALL_OPENCODE=false INSTALL_PI=true tags: opencode-devbox:ci-pi-only - name: Smoke test run: | bash scripts/smoke-test.sh opencode-devbox:ci-pi-only --variant pi-only