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. on: push: branches: - main paths-ignore: - 'CHANGELOG.md' - 'README.md' - 'DOCKER_HUB.md' - 'deploy/**' - '.gitleaks.toml' pull_request: branches: - main # Gitea Actions' default step shell is `sh` (dash); force bash workflow-wide so # no run: step silently falls through to dash. Enforced by lint.yml's # scripts/check-workflow-shell.sh guard, which scans ALL .gitea/workflows/*.yml # (so this file must resolve to bash too, not just docker-publish-split.yml). defaults: run: shell: bash 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) AND exports `base_changed` so # validate-base/validate-omos can skip. validate.yml uses Hub's base-latest # as the parent for variant builds, so a changed base is NOT exercised here # — worse, if the same commit tightens smoke-test.sh in lockstep with the # base change (as the nano/micro commit did), those jobs HARD-FAIL against # the stale base-latest: a false red that self-heals only once the release # rebuilds base-latest. So we skip them here and defer full base validation # to the release path (docker-publish-split.yml on tag push). runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest outputs: base_changed: ${{ steps.detect.outputs.base_changed }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 2 - name: Detect base-input changes id: detect run: | set -e # Base inputs baked into Dockerfile.base: Dockerfile.base itself, any # file under rootfs/ (COPYed into the base), and entrypoint*.sh. NB: # rootfs/ is a PREFIX match — the earlier '^(...|rootfs/|...)$' anchored # the whole group, so the rootfs/ branch only matched a file literally # named 'rootfs/' and never real paths like rootfs/usr/.../x.py. base_re='^(Dockerfile\.base$|rootfs/|entrypoint.*\.sh$)' changed="$(git diff --name-only HEAD~1 HEAD 2>/dev/null | grep -E "$base_re" || true)" if [ -n "$changed" ]; then echo "base_changed=true" >> "$GITHUB_OUTPUT" 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 — validate-base/validate-omos are SKIPPED to avoid a false failure against the stale base-latest. 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:" printf '%s\n' "$changed" else echo "base_changed=false" >> "$GITHUB_OUTPUT" echo "No base-image inputs changed in this commit — validate.yml fully exercises the published base-latest." fi validate-base: needs: [base-change-warning] if: ${{ needs.base-change-warning.outputs.base_changed != 'true' }} 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: needs: [base-change-warning] if: ${{ needs.base-change-warning.outputs.base_changed != 'true' }} 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