docs(spec): Plan 4c release pipeline design
This commit is contained in:
156
docs/superpowers/specs/2026-04-14-release-pipeline-design.md
Normal file
156
docs/superpowers/specs/2026-04-14-release-pipeline-design.md
Normal file
@@ -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_<version-without-v>_linux_<arch>.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).
|
||||||
Reference in New Issue
Block a user