5fb07e0a39
A push to main used to trigger validate.yml, which builds an amd64 variant and
runs the smoke test. Pushing work-in-progress to main therefore cost a build.
Restructure so the three workflows divide by cost, matching pi-devbox:
lint.yml cheap checks, every push/PR — the ONLY workflow a
push to main triggers
validate.yml amd64 build + smoke test — pull_request and
workflow_dispatch only, no push trigger at all
docker-publish-split.yml the release path, tag-only (unchanged)
docs-check (DOCKER_HUB.md vs HUB_TEMPLATE) moves from validate.yml to lint.yml.
It needs no image, and anything that needs no image belongs in the workflow that
actually runs on push — otherwise the doc-drift guard would have been silently
lost when validate.yml stopped running on pushes. That guard earns its keep: it
has caught real drift.
validate-base/validate-omos additionally keep a `github.event_name != 'push'`
clause. It is redundant now that the trigger is gone, and deliberately so:
re-adding a push trigger later cannot silently re-enable builds on every push.
Renamed lint.yml `Lint workflows` -> `Lint`, since it now covers Dockerfiles and
docs as well as workflows. No references to the old name existed.
Safe because the release path already fails closed: docker-publish-split.yml
pushes variant tags only after smoke-base/smoke-omos pass and promotes
base-latest last, so an aborted release leaves at worst an unreferenced
base-<hash> blob on Hub — never a half-published version tag. Pre-tag
validation remains available three ways: open a PR, dispatch Validate, or
dispatch docker-publish-split.yml against a throwaway tag with
promote_latest=false — the only route that also exercises a CHANGED base, which
validate.yml structurally cannot (it builds variants on Hub's base-latest).
Coverage lost is narrower than it looks: validate-base/validate-omos were
already skipped whenever a commit touched Dockerfile.base, rootfs/, or
entrypoint*.sh, so they only ever ran for variant-only changes — most usefully a
bare OPENCODE_VERSION bump, for which an explicit dispatch is now the
equivalent.
Verified with the CI-pinned actionlint 1.7.7 and scripts/check-workflow-shell.sh.
AGENTS.md updated in the same commit (file roles for both workflows + the
trigger-model convention). The CHANGELOG entry lands with the v2.9.0 changeset
in the following commit, which is one unreleased block covering all of v2.9.0.
228 lines
9.5 KiB
YAML
228 lines
9.5 KiB
YAML
name: Validate
|
|
|
|
# Image build + smoke test. This workflow has NO push trigger at all — it fires
|
|
# only on pull_request and on explicit workflow_dispatch.
|
|
#
|
|
# Division of labour across this repo's three workflows:
|
|
# lint.yml — cheap checks, every push/PR: workflow lint,
|
|
# Dockerfile lint, DOCKER_HUB.md sync. No builds.
|
|
# validate.yml (this file) — amd64 build + smoke test. PR / manual only.
|
|
# docker-publish-split.yml — the release path, tag-only: multi-arch build,
|
|
# smoke gates, then push + promote.
|
|
#
|
|
# Net effect: no image build can start from a push to main. A tag — or an
|
|
# explicit dispatch — is required. That is safe because the release path fails
|
|
# closed: variant tags are pushed only after smoke-base/smoke-omos pass, and
|
|
# base-latest is promoted last, so an aborted release leaves at worst an
|
|
# unreferenced base-<hash> blob on Hub, never a half-published version tag.
|
|
#
|
|
# To validate before tagging: open a PR, dispatch this workflow, or dispatch
|
|
# docker-publish-split.yml against a throwaway tag with promote_latest=false
|
|
# (the only route that also exercises a CHANGED BASE — see the trade-off below).
|
|
#
|
|
# 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:
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
workflow_dispatch:
|
|
|
|
# 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:
|
|
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-base/validate-omos will build against the published base-latest."
|
|
fi
|
|
|
|
validate-base:
|
|
needs: [base-change-warning]
|
|
# Two gates, both must hold for this job to run:
|
|
# 1. base_changed != true — the documented blind spot: this workflow builds
|
|
# variants on top of Hub's base-latest, so a commit changing base inputs
|
|
# would either not exercise the change or hard-fail against a stale
|
|
# base-latest (see base-change-warning).
|
|
# 2. event_name != push — belt-and-braces. Redundant today (this workflow
|
|
# has no push trigger), kept deliberately so that re-adding a push
|
|
# trigger later cannot silently re-enable image builds on every push to
|
|
# main. If you intend that, remove this clause explicitly.
|
|
if: ${{ needs.base-change-warning.outputs.base_changed != 'true' && github.event_name != 'push' }}
|
|
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]
|
|
# Same two gates as validate-base — see the comment there.
|
|
if: ${{ needs.base-change-warning.outputs.base_changed != 'true' && github.event_name != 'push' }}
|
|
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
|