Files
Gitea/cloudbuild/image.yaml
JMR-devandClaude Opus 5.5 3d38240b2f image build: enable BuildKit
The first image build failed in build-gitea:

  the --chmod option requires BuildKit

Both Dockerfiles declare `# syntax=docker/dockerfile:1` and use
`COPY --chmod`, but gcr.io/cloud-builders/docker runs the legacy builder
unless DOCKER_BUILDKIT=1 is set. The image ships the buildx plugin, so
setting it on the two build steps is all that is needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 04:04:18 -05:00

133 lines
4.8 KiB
YAML

# Build the Gitea and Caddy images, push them, and roll them out.
#
# The rollout works by kicking podman-auto-update on the VM. That is why the
# quadlets track a floating :prod tag rather than a digest: AutoUpdate=registry
# compares the local digest against the registry's digest FOR A TAG, so a
# digest-pinned image would give it nothing to poll.
#
# The :$SHORT_SHA and version tags are the audit trail and the rollback targets.
substitutions:
_REGION: us-east1
_ZONE: us-east1-b
_DOMAIN: gitea.jasonmross.dev
_REPO: gitea
_VM: gitea-vm
options:
# A user-specified service account cannot write to the legacy default log
# bucket. Without this the very first build fails on storage.objects.create.
logging: CLOUD_LOGGING_ONLY
machineType: E2_HIGHCPU_8
timeout: 2400s
steps:
- id: read-versions
name: bash
script: |
#!/usr/bin/env bash
set -euo pipefail
mkdir -p /workspace/vars
tr -d '[:space:]' < image/gitea.version > /workspace/vars/gitea_version
tr -d '[:space:]' < image/caddy.version > /workspace/vars/caddy_version
tr -d '[:space:]' < image/coraza.version > /workspace/vars/coraza_version
echo "gitea=$(cat /workspace/vars/gitea_version) caddy=$(cat /workspace/vars/caddy_version) coraza=$(cat /workspace/vars/coraza_version)"
- id: build-gitea
name: gcr.io/cloud-builders/docker
# Both Dockerfiles are written for BuildKit (`# syntax=`, COPY --chmod). This
# builder image defaults to the legacy builder, which rejects --chmod.
env: [DOCKER_BUILDKIT=1]
entrypoint: bash
args:
- -c
- |
set -euo pipefail
V=$(cat /workspace/vars/gitea_version)
IMG="${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/gitea"
docker build \
--build-arg "GITEA_VERSION=$${V}" \
--tag "$${IMG}:prod" \
--tag "$${IMG}:$SHORT_SHA" \
--tag "$${IMG}:$${V}" \
image/gitea
- id: build-caddy
name: gcr.io/cloud-builders/docker
env: [DOCKER_BUILDKIT=1]
entrypoint: bash
# xcaddy runs inside the Dockerfile's golang builder stage, so the plain
# docker builder is all this step needs -- no Go toolchain out here.
args:
- -c
- |
set -euo pipefail
V=$(cat /workspace/vars/caddy_version)
C=$(cat /workspace/vars/coraza_version)
IMG="${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/caddy"
# The Dockerfile asserts both the DNS and WAF modules are present, so a
# dropped --with fails the build rather than shipping an unprotected
# server. Caddy and coraza-caddy versions are coupled: coraza-caddy
# pins a minimum Caddy, and a mismatch fails here at `go get`.
docker build \
--build-arg "CADDY_VERSION=$${V}" \
--build-arg "CORAZA_VERSION=$${C}" \
--tag "$${IMG}:prod" \
--tag "$${IMG}:$SHORT_SHA" \
--tag "$${IMG}:$${V}" \
image/caddy
- id: push
name: gcr.io/cloud-builders/docker
entrypoint: bash
args:
- -c
- |
set -euo pipefail
BASE="${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}"
docker push --all-tags "$${BASE}/gitea"
docker push --all-tags "$${BASE}/caddy"
# Record the digests actually published. Pulumi does not pin these, so
# the build log is where you look to find a rollback target.
docker image inspect "$${BASE}/gitea:prod" --format '{{index .RepoDigests 0}}'
docker image inspect "$${BASE}/caddy:prod" --format '{{index .RepoDigests 0}}'
- id: rollout
name: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
entrypoint: bash
env:
# gcloud needs a writable HOME to generate the ephemeral SSH key it pushes
# through OS Login. The default HOME in this image is not writable.
- HOME=/workspace
args:
- -c
- |
set -euo pipefail
gcloud compute ssh "${_VM}" \
--zone="${_ZONE}" \
--tunnel-through-iap \
--quiet \
--command 'sudo systemctl start podman-auto-update.service'
- id: verify
name: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
entrypoint: bash
# A build that pushes a broken image and reports success is worse than a
# failed build. Gate on the app actually answering.
args:
- -c
- |
set -euo pipefail
for i in $(seq 1 30); do
if curl -fsS --max-time 10 "https://${_DOMAIN}/api/healthz" >/dev/null; then
echo "healthz passed after $${i} attempt(s)"
exit 0
fi
echo "waiting for https://${_DOMAIN}/api/healthz ($${i}/30)"
sleep 10
done
echo "healthz never passed -- check 'journalctl -u podman-auto-update' on the VM;" >&2
echo "podman should have rolled back automatically if the new image failed to start." >&2
exit 1