# Release workflow — P5-03 (docker-only release path). # # Spec : docs/superpowers/specs/2026-05-05-p5-03-docker-only-release.md # Plan : docs/superpowers/plans/2026-05-05-p5-03-docker-only-release.md # # What it does # * Triggered by either: # - tag push matching v[0-9]+.[0-9]+.[0-9]+ (real release), or # - workflow_dispatch (snapshot iteration without tagging). # * Cross-builds a multi-arch (linux/amd64,linux/arm64) image of the # server, with three agent binaries (linux amd64+arm64, windows amd64) # plus install.sh / install.ps1 / the systemd unit baked in under # /opt/restic-manager/dist (the read-only fallback path the server # handlers use when /... is empty). # * Pushes to this Gitea instance's container registry under # //restic-manager. # # Tag fan-out # * tag push: :vX.Y.Z, :X.Y, :X # * tag push and X >= 1: also :latest # * workflow_dispatch: only :snapshot-; nothing else moves. # # Why no goreleaser # The architecture already routes agent distribution through the # server's /agent/binary endpoint. The image is the only deliverable; # binary archives would just be a second source of truth. name: Release on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' workflow_dispatch: env: REGISTRY: gitea.dcglab.co.uk IMAGE_NAME: ${{ gitea.repository }} # Force bash as the default shell — see ci.yml header. defaults: run: shell: bash jobs: image: name: Build + push image runs-on: ubuntu-latest container: gitea.dcglab.co.uk/steve/ci-runner-go:2026-05-08 steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - name: Log in to Gitea registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ gitea.actor }} password: ${{ secrets.DEV_TOKEN }} - name: Compute tags + version id: meta shell: bash run: | set -euo pipefail REG="${REGISTRY}/${IMAGE_NAME}" DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" SHORT_SHA="${GITHUB_SHA::7}" if [ "${GITHUB_EVENT_NAME}" = "push" ] && [ "${GITHUB_REF_TYPE}" = "tag" ]; then TAG="${GITHUB_REF_NAME}" # vX.Y.Z VER="${TAG#v}" # X.Y.Z MAJOR="${VER%%.*}" MINOR="${VER#${MAJOR}.}"; MINOR="${MINOR%%.*}" TAGS="${REG}:${TAG}" TAGS="${TAGS},${REG}:${MAJOR}.${MINOR}" TAGS="${TAGS},${REG}:${MAJOR}" # Pre-1.0 holds back :latest by design; operators must # pin a version explicitly until v1.0.0. if [ "${MAJOR}" -ge 1 ]; then TAGS="${TAGS},${REG}:latest" fi VERSION="${TAG}" else TAGS="${REG}:snapshot-${SHORT_SHA}" VERSION="0.0.0-snapshot-${SHORT_SHA}" fi { echo "tags=${TAGS}" echo "version=${VERSION}" echo "date=${DATE}" } >> "${GITHUB_OUTPUT}" - name: Build + push uses: docker/build-push-action@v6 with: context: . file: deploy/Dockerfile.server platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} build-args: | VERSION=${{ steps.meta.outputs.version }} COMMIT=${{ gitea.sha }} DATE=${{ steps.meta.outputs.date }} labels: | org.opencontainers.image.version=${{ steps.meta.outputs.version }} org.opencontainers.image.revision=${{ gitea.sha }} org.opencontainers.image.created=${{ steps.meta.outputs.date }}