Skip to content

ci: install litestream so the replication suite runs in the gate #77

ci: install litestream so the replication suite runs in the gate

ci: install litestream so the replication suite runs in the gate #77

Workflow file for this run

name: image
# The image a cluster is made of.
#
# Opening a cluster from the console provisions a machine that pulls this and
# runs it. Before this workflow existed there was nothing to pull: `just ship`
# rsyncs the source to a host and builds there, which works for the one node
# somebody has a shell on and cannot work for a machine created by a request.
#
# Published on every push to main rather than on a tag, because a cluster opened
# today should be the blazie of today. `latest` is what cloud-init asks for; the
# sha tag is so a machine can be pinned to what it was opened with when something
# has to be explained afterwards.
on:
push:
branches: [main]
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Built and LOADED, not pushed. The probe below decides whether it is fit
# to be `latest`, and a push before that has already happened is not a
# gate — the first two runs of this workflow published a `latest` that
# could not boot, and only the run that finally passed replaced it. Same
# ordering rule the control plane follows when it makes a tunnel before a
# machine: do the irreversible thing last.
- name: Build
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
# A cluster is a Linux server and nothing else builds from this, so one
# architecture. arm64 doubles the build for a platform nothing runs.
platforms: linux/amd64
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Refuse an image that will not answer
run: |
set -euo pipefail
# A throwaway secret, because the release refuses to boot without one
# and a probe that skipped it would be probing a different program
# than the one a cluster runs.
docker run -d --name probe -p 4000:4000 \
-e SECRET_KEY_BASE="$(head -c 48 /dev/urandom | base64)" \
ghcr.io/${{ github.repository }}:latest
# The same question the healthcheck asks, for the same reason: a
# container that starts and does not serve is the failure this catches,
# and it is the one that shipped unnoticed when the healthcheck was
# left pointing at a retired endpoint.
for i in $(seq 1 60); do
code=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
http://127.0.0.1:4000/run -H 'content-type: application/json' \
-d '{}' --max-time 5 || true)
[ "$code" = "401" ] && break
sleep 2
done
docker logs probe | tail -30
if [ "$code" != "401" ]; then
echo "the image answered ${code} where 401 was expected"
exit 1
fi
echo "the image serves"
# Only now, and only because it answered.
- name: Publish
run: |
docker push ghcr.io/${{ github.repository }}:latest
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}