# 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).