p5-03: docker-only release path (drop goreleaser)

Single public deliverable per tag: a multi-arch server image, with
cross-compiled agent binaries + install scripts + the systemd unit
baked under /opt/restic-manager/dist/. The /agent/binary and
/install/* handlers fall back from <DataDir>/... to that read-only
path so a fresh container Just Works without first-run staging;
operators can still drop a custom build into <DataDir>/ to override
per-host.

Architecture rationale: agent distribution already routes through
the running server, so the release surface mirrors that — there's
no second source of truth to keep in sync.

Workflow .gitea/workflows/release.yml triggers on v*.*.* tag-push
(fan-out :vX.Y.Z / :X.Y / :X, plus :latest once MAJOR>=1) and
workflow_dispatch (snapshot tag only). Pushes to the Gitea
container registry on this instance.

Both binaries grow main.commit + main.date ldflag targets. Makefile
and Dockerfile fill them; release workflow forwards from gitea.sha
plus a UTC timestamp.

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
This commit is contained in:
2026-05-05 15:18:48 +01:00
parent 5ee58979fa
commit 7cc17813a9
11 changed files with 752 additions and 29 deletions
+107
View File
@@ -0,0 +1,107 @@
# 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 <DataDir>/... is empty).
# * Pushes to this Gitea instance's container registry under
# <gitea-host>/<owner>/restic-manager.
#
# Tag fan-out
# * tag push: :vX.Y.Z, :X.Y, :X
# * tag push and X >= 1: also :latest
# * workflow_dispatch: only :snapshot-<shortsha>; 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 }}
jobs:
image:
name: Build + push image
runs-on: ubuntu-latest
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.GITHUB_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 }}