feat(release): add Gitea release-publishing pipeline
Release / release (push) Successful in 27s

- Add scripts/build-release.sh: cross-compile linux+windows amd64 with
  ldflags injecting main.version and main.commit, write
  checksums-sha256.txt and release-manifest.json (full commit SHA).
- Add scripts/release-check.sh: local mirror of CI (test, vet, build,
  --version substring check); falls back to Windows artifact when run
  on a Windows host where the Linux binary can't exec.
- Wire main.version / main.commit -> cmd.SetVersionInfo. Default to
  "dev" / "" so local builds without ldflags still produce a
  sensible string. Output format: single line
  'ctask <version> (<short-sha>)' or 'ctask <version>' / 'ctask dev'.
- Add .gitea/workflows/release.yml: triggered on v* tags, runs-on
  ctask-release (golang:1.26-bookworm). Tag parsed from gitea.ref
  (not gitea.ref_name). Pure shell + Gitea API; no actions/checkout,
  no setup-go, no third-party release action. Installs jq at job
  start. RC tags are deletable+recreatable; final tags are immutable.
  Verify step downloads published assets, sha256sum -c's, and runs
  --version.
- notes.md: log Phase 0/2/3 + version-injection completion.
This commit is contained in:
2026-05-20 15:19:59 -04:00
parent 0e8e4a5d7b
commit bbc41646ee
6 changed files with 440 additions and 5 deletions
+109
View File
@@ -727,3 +727,112 @@ For the v0.4 surface:
- **v0.5.4:** Do not move the Attach-hint string construction into `session.SessionStatus`. The cmd layer owns invocation-name rendering; the session package owns lease parsing. The boundary is what keeps SessionStatus testable without a `withInvocationName` seam.
- **v0.5.4:** Do not re-introduce a hardcoded `"ctask"` in command-form hints. Use `invocationName()`. The regression tests pin a non-canonical name specifically to catch this.
---
# Release-Publishing Pipeline (plan: `ctask-release-pipeline-plan-final.md`)
## Phase 0 — Runner & Release API Preflight — **BLOCKED** (started 2026-05-19)
Tooling versions:
- Workstation: go 1.26.3, git 2.53.0, curl 8.18.0, sha256sum (coreutils 8.32), jq 1.8.1, bash 5.2.37
- VPS host (`netcup`): git 2.47.3, curl 8.14.1, sha256sum (coreutils 9.7), jq 1.7; **no Go on host**
Gitea instance:
- Gitea 1.25.5 in Docker container `gitea` (`gitea/gitea:1.25.5`), fronted by Traefik.
- API `https://git.typebased.dev/api/v1` reachable (200) from workstation and VPS.
- No explicit `[actions]` section in app.ini -> Actions enabled by default (1.21+).
- Org `typebasedio` -> 404 unauthenticated; repo `typebasedio/ctask` -> 404 (not created).
BLOCKERS (Phase 0 gate NOT satisfied):
1. **No Gitea Actions runner exists** anywhere on the VPS — no `act_runner`
container/binary/process/service. Phases 3-4 cannot run without a registered,
labelled runner. Plan assumed a runner already existed.
2. **Token value not located.** `workstation-dev-environment` token value needed
for release API preflight + `RELEASE_TOKEN` secret. Awaiting user retrieval
instructions (must not create a second token).
Open issues: runner install/registration decision; token retrieval source.
### Update 2026-05-20 — runner provisioned
Shared Typebased runner `vps-act-runner-01` came online (provisioned by the
windows-dev agent; canonical docs in `typebasedio/windows-dev-setup` @ ae9eef5,
`typebasedio/project-registry` @ 2ddc8c6). Contract for ctask:
- `runs-on: ctask-release`
- Backed by `golang:1.26-bookworm` (jq NOT in base image — workflow installs it)
- Pure Go workflow only; no Docker-in-job, no Docker image build steps
- `RELEASE_TOKEN` repo/user secret used for release create/upload
- Prior runner-registration token leak rotated; current credential is fresh
Phase 0 runner blocker resolved. Token still pending (Bitwarden locked at last
check; user will unlock and we'll fetch by item name).
## Phase 2 — Local release build target — **DONE** (2026-05-20)
Added:
- `scripts/build-release.sh` — single recipe shared with CI; wipes `dist/`,
cross-compiles `ctask-linux-amd64` and `ctask-windows-amd64.exe` with
`CGO_ENABLED=0` and ldflags injecting `main.version` and `main.commit`,
writes `checksums-sha256.txt` and `release-manifest.json`.
- `scripts/release-check.sh` — runs `go test ./...`, `go vet ./...`,
invokes `build-release.sh`, then asserts the binary's `--version`
contains the requested tag. Falls back to the Windows binary on
Windows hosts where the Linux binary can't exec.
## Phase 5 prerequisite — version injection — **DONE** (2026-05-20)
- `main.go` declares `var version = "dev"` and `var commit = ""` and forwards
them to `cmd.SetVersionInfo` before `cmd.Execute()`.
- `cmd/root.go` exposes `SetVersionInfo(v, c)` and rebuilds the Cobra version
template via `applyVersionTemplate()`.
- Output format (single line, decided pre-Phase 5):
- `ctask v0.6.1-rc.1 (0e8e4a5)` — tagged build, commit known
- `ctask v0.6.1-rc.1` — tagged build, no commit injected
- `ctask dev` — local build (no ldflags)
- Phase 5 Dockerfile exact-equality assertion (optional in the plan) will need
to be a substring/prefix check because the commit is appended. Sample:
`./ctask --version | grep -qF "${CTASK_VERSION}"`.
- `just install` / local Windows workflow unchanged — local builds simply now
print `ctask dev` instead of `ctask v0.6.0`. No behavioral regression.
### Local validation result (commit 0e8e4a5)
`bash scripts/release-check.sh v0.6.1-rc.1`:
- tests: all packages pass (`cmd`, `internal/agent`, `internal/config`,
`internal/lockfile`, `internal/seed`, `internal/session`, `internal/shell`,
`internal/workspace`)
- vet: clean
- artifacts:
- `dist/ctask-linux-amd64` sha256 `808c71f982a3ed50f63bd5c4e1d25c4cf0643c887b8c2e011c5181a9020d1004` (5,288,098 bytes)
- `dist/ctask-windows-amd64.exe` sha256 `c8dee43d5ade90899020fb8b31a41230672057b74478aa78f91d6f509dd689e8` (5,511,168 bytes)
- `dist/checksums-sha256.txt` (`sha256sum -c` passes)
- `dist/release-manifest.json` (valid JSON; commit `0e8e4a5d7bc4320cd933008d5b6e505f2b3c5ec4`)
- `--version` output on Windows host: `ctask v0.6.1-rc.1 (0e8e4a5)`
- Linux binary cross-exec'd on VPS (`netcup`): `ctask v0.6.1-rc.1 (0e8e4a5)` ✓
## Phase 3 — Gitea Actions workflow — **DRAFTED** (2026-05-20)
Added `.gitea/workflows/release.yml`. Not yet committed/pushed.
- Trigger: `push` of tags matching `v*`
- `runs-on: ctask-release`
- Tag parsed from `gitea.ref` (strips `refs/tags/`), NOT `gitea.ref_name`
- Installs jq at job start (`golang:1.26-bookworm` base lacks it)
- Steps: derive tag → `git clone --depth 1 --branch` (no `actions/checkout`)
→ `go test ./...` + `go vet ./...` → `bash scripts/build-release.sh`
→ create release via Gitea API (delete+recreate if RC and exists; refuse if
non-RC and exists) → upload 4 assets → download-verify with `sha256sum -c`
and `--version` substring match.
- All JSON bodies built with `jq -nc` rather than string-interpolated to
prevent quoting bugs.
## Still pending (token-dependent)
- Phase 0 token check: create+delete a draft release against the live repo.
- Phase 1: create `typebasedio/ctask` (public), wire `origin` remote, push `main`.
- Phase 3 secret: set `RELEASE_TOKEN` from the existing `workstation-dev-environment` token (no new PAT).
- Phase 4: commit changes, push, tag `v0.6.1-rc.1`, validate the CI run.