From 79f2bcd2303cf14142f0ffc41488132bec1add39 Mon Sep 17 00:00:00 2001 From: MDietermann Date: Tue, 14 Apr 2026 20:54:37 +0200 Subject: [PATCH] docs(spec): Plan 4c release pipeline design --- .../2026-04-14-release-pipeline-design.md | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-14-release-pipeline-design.md diff --git a/docs/superpowers/specs/2026-04-14-release-pipeline-design.md b/docs/superpowers/specs/2026-04-14-release-pipeline-design.md new file mode 100644 index 0000000..626a7f3 --- /dev/null +++ b/docs/superpowers/specs/2026-04-14-release-pipeline-design.md @@ -0,0 +1,156 @@ +# Release Pipeline Design (Plan 4c) + +**Date:** 2026-04-14 +**Scope:** Plan 4c of the Archduino Go rewrite — ship versioned releases and enable one-line install. + +## Goal + +Let a user install Archduino with a single `curl | bash`, and let the maintainer ship versioned releases with a single `make release`, hosted entirely on the self-hosted Gitea instance at `gitlab.hive-brain.de:2222`. + +## Deliverables + +1. `bootstrap.sh` — thin curl-piped shim that installs prereqs, downloads the latest release, and execs `archduino install`. +2. `.goreleaser.yaml` — cross-compile + archive + checksum + changelog + publish to Gitea Releases. +3. `Makefile` with `release` and `snapshot` targets — driven by `svu` for SemVer bumps. +4. `.gitea/workflows/release.yml` — tag-triggered workflow that runs goreleaser. + +## Components + +### 1. `bootstrap.sh` (repo root) + +Replaces the legacy `install.sh` (which is Bash-era and will be removed/archived). + +Behavior: +- `set -euo pipefail` throughout. +- Detect Arch via `/etc/arch-release`; exit 1 with clear message otherwise. +- `sudo pacman -Sy --needed --noconfirm sudo git curl ca-certificates`. +- Detect arch: `uname -m` → `x86_64` → `amd64`, `aarch64` → `arm64`. Unknown arch → exit 1. +- Query Gitea API for latest release: + `GET https://gitlab.hive-brain.de/api/v1/repos/MDietermann/Archduino/releases/latest` + Extract `tag_name` (e.g. `v0.1.0`) and use it to locate assets. +- Download `archduino__linux_.tar.gz` and `checksums.txt` into a `mktemp -d`. +- Verify SHA256 via `sha256sum --check --ignore-missing checksums.txt`; mismatch → exit 1. +- Extract tarball. +- Install with sudo: + - Binary → `/usr/local/bin/archduino` (0755), overwriting. + - `completions/archduino.bash` → `/usr/share/bash-completion/completions/archduino`. + - `completions/_archduino` → `/usr/share/zsh/site-functions/_archduino`. + - `completions/archduino.fish` → `/usr/share/fish/vendor_completions.d/archduino.fish`. +- `exec /usr/local/bin/archduino install "$@"` so flags pass through (e.g. `--halt-on-error`). + +Usage: +``` +curl -fsSL https://gitlab.hive-brain.de/MDietermann/Archduino/raw/branch/master/bootstrap.sh | bash +``` + +### 2. `.goreleaser.yaml` (repo root) + +- `project_name: archduino` +- `before.hooks:` regenerate shell completions via the cobra `completion` subcommand: + - `go run ./cmd/archduino completion bash > completions/archduino.bash` + - `go run ./cmd/archduino completion zsh > completions/_archduino` + - `go run ./cmd/archduino completion fish > completions/archduino.fish` + - Prerequisite: the `completion` subcommand must be registered on the root cobra command. If not already present, add it as part of the implementation. +- `builds:` + - One entry, `main: ./cmd/archduino`, `binary: archduino`. + - `env: [CGO_ENABLED=0]`. + - `goos: [linux]`, `goarch: [amd64, arm64]`. + - `ldflags: -s -w -X gitlab.hive-brain.de/MDietermann/Archduino/internal/buildinfo.Version={{.Version}} -X ...Commit={{.Commit}} -X ...Date={{.Date}}` (add a small `internal/buildinfo` package to hold these; surface via `archduino version`). +- `archives:` + - Format tar.gz. + - Name template `archduino_{{.Version}}_linux_{{.Arch}}`. + - Files: binary + `completions/*` + `README.md` + `LICENSE` (add if missing). +- `checksum: name_template: 'checksums.txt'` (SHA256). +- `changelog:` + - `use: git` + - Filters: exclude `^chore:`, `^docs:`, `^test:`, `^ci:`, `^style:`, `Merge pull request`, `Merge branch`. + - Groups: `Features` (`^feat`), `Bug fixes` (`^fix|^perf`), `Others` (everything else). +- `release:` + - `gitea_urls.api: https://gitlab.hive-brain.de/api/v1` + - `gitea_urls.download: https://gitlab.hive-brain.de` + +### 3. `Makefile` (repo root) + +Targets: +- `snapshot` — `goreleaser release --snapshot --clean`. No tagging, no publish. For local build-matrix validation. +- `release` — SemVer bump flow: + 1. Assert working tree clean and current branch is `master`; abort otherwise. + 2. `CURRENT=$$(svu current)` and `NEXT=$$(svu next)`. Display both plus the commit log since `CURRENT`. + 3. Prompt `Tag $NEXT and push? [y/N]`. Abort on anything but `y`. + 4. `git tag $$NEXT && git push origin $$NEXT`. + The actual release is then produced by CI. +- `completions` — regenerate completions into `completions/` locally (useful for dev). +- `build` — local `go build ./cmd/archduino` for quick smoke. + +`svu` is a dev dependency; README gains a one-line note: `go install github.com/caarlos0/svu@latest`. + +### 4. `.gitea/workflows/release.yml` + +- Trigger: `on: push: tags: ['v*']`. +- `runs-on: ubuntu-latest` (Gitea Actions convention; matches runner labels). +- Steps: + 1. `actions/checkout@v4` with `fetch-depth: 0` (goreleaser needs full history for changelog). + 2. `actions/setup-go@v5` with the Go version pinned in `go.mod`. + 3. `goreleaser/goreleaser-action@v6` with `args: release --clean`. +- Env: `GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}`. Token is created manually in Gitea UI (repo-scoped, `write:package`/`write:repository` as needed) and stored as a repo secret before first release. +- Prerequisite (documented, not executed by the plan): a Gitea Actions runner must be registered on the instance. One-time setup outside the scope of this spec. + +## Data flow + +``` +dev: git commit -m "feat: ..." (conventional commits) +dev: make release → svu computes vX.Y.Z → git tag + push +Gitea Actions: tag push → goreleaser --clean → Gitea Release published + (tarballs + checksums.txt + changelog) +user: curl … | bash → bootstrap.sh → Gitea API (latest release) → tarball → + sha256 verify → /usr/local/bin + completions → exec archduino install +``` + +## Versioning + +SemVer driven by Conventional Commits via `svu`: +- `feat:` → minor +- `fix:` / `perf:` → patch +- `feat!:` or `BREAKING CHANGE:` footer → major +- `chore:` / `docs:` / `refactor:` / `test:` / `ci:` / `style:` → no release + +First release: `v0.1.0` (seed tag so `svu` has a baseline; manual one-time `git tag v0.1.0`). + +## Error handling + +- `bootstrap.sh`: checksum mismatch, non-Arch host, arch detection failure, non-200 from API → exit 1 with a specific message; `set -euo pipefail` covers the rest. +- goreleaser is transactional — the Gitea Release is created after artifacts upload successfully, so a mid-run failure leaves no partial release. +- `make release` aborts before tagging if the tree is dirty or not on `master`. + +## Testing + +- `make snapshot` locally verifies the build matrix and archive contents without publishing. +- `bootstrap.sh` tested against a snapshot tarball in a throwaway Arch container: + `docker run --rm -it archlinux bash` → serve the tarball locally → run bootstrap with an overridden base URL (env var hook, e.g. `ARCHDUINO_RELEASE_BASE`) for test runs. +- No Go test changes; this is all out-of-band release machinery. + +## Out of scope (deferred) + +- GPG-signed checksums (revisit if/when distribution widens). +- Debian/Fedora/macOS builds (waiting on multi-distro support elsewhere in the roadmap). +- Homebrew tap and AUR PKGBUILD (AUR is a natural follow-up but tracked separately). +- Automated "release PR" bots (release-please / semantic-release) — `svu` + manual `make release` is the chosen trigger. + +## File touch list + +New: +- `bootstrap.sh` +- `.goreleaser.yaml` +- `Makefile` +- `.gitea/workflows/release.yml` +- `internal/buildinfo/buildinfo.go` (tiny — holds `Version`, `Commit`, `Date` vars) +- `completions/` (gitignored; generated) +- `LICENSE` (if missing) + +Modified: +- `cmd/archduino/main.go` (register cobra `completion` subcommand if absent; wire a `version` subcommand that prints `buildinfo`). +- `README.md` (install-via-bootstrap instructions, `svu` note). +- `.gitignore` (add `dist/`, `completions/`). + +Removed/archived: +- `install.sh` (legacy Bash installer — superseded by `bootstrap.sh` + Go binary).