Skip to content

ci: build and verify all six release targets on every PR#2

Merged
tompscanlan merged 2 commits into
mainfrom
ci/release-build-verify-on-pr
Jul 14, 2026
Merged

ci: build and verify all six release targets on every PR#2
tompscanlan merged 2 commits into
mainfrom
ci/release-build-verify-on-pr

Conversation

@tompscanlan

Copy link
Copy Markdown
Owner

What

CI currently runs only build/test/vet on Ubuntu — the six cross-compiled release targets are never built or checked on a PR, so a break in the release path only surfaces at tag time. This wires the existing scripts/release.sh (which already cross-compiles all six targets and both Windows GUI-subsystem variants from one Linux box, CGO_ENABLED=0) into CI on every PR, and adds archive verification.

  • scripts/verify-archives.sh (new): asserts each archive contains the expected binaries, that the Windows GUI binary really is the GUI subsystem (file(1) on the PE header), that unix binaries carry the exec bit, and that the UI is embedded.
  • ci.yml: new release-build job (run release.shverify-archives.sh → linux/amd64 startup smoke → upload artifacts), plus smoke-windows and smoke-macos native-startup jobs.
  • release.yml: runs verify-archives.sh before publish, so a tagged release can't ship a bad archive either.

No Actions build matrix — the six targets build from a single ubuntu-latest runner, so a matrix would be wasteful. .goreleaser.yaml is intentionally left alone (it lacks the Windows GUI variant and can't produce what we ship). The new jobs inherit the toolchain from go.mod via go-version-file.

Verified locally

  • scripts/release.sh builds all six targets.
  • verify-archives.sh passes on good artifacts and fails on a deliberately corrupted archive and on a GUI→console regression (checked both).
  • Both workflows parse.

What this PR run confirms (only Actions can)

The workflow triggering, the artifact upload/download round-trip, and the two native smoke jobs (windows/amd64, darwin/arm64) — no hosted Windows/macOS runner exists locally. One known unknown: the cross-compiled unsigned darwin binary may carry com.apple.quarantine when artifact-extracted; the macOS smoke job is where we'll find out.

Scope is "build all six, start three" — only three targets are natively startable on GitHub runners, and the jobs say so.

…5gvj)

Compiling all six targets proves almost nothing about what a user unpacks.
scripts/verify-archives.sh asserts, against dist/, the things that break
silently per-platform and never fail to compile:

- all six archives present, listed in checksums.txt, and matching them
  (sha256sum -c catches any byte corruption);
- unix binaries carry the exec bit as stored IN the tar header;
- each Windows zip ships CoinRollHunter.exe, cli/coinrollhunter.exe, and
  READ ME FIRST.txt;
- file(1) confirms CoinRollHunter.exe is the GUI subsystem and the CLI copy
  is console — i.e. -H=windowsgui actually took (a regression to console
  ships a flashing black window to every double-click user);
- the embedded UI is really in the binary (grep the index.html mount point,
  since web/dist is a gitignored artifact resolved against a .gitkeep and a
  bare `go build` without `make ui` serves an EMPTY UI and still exits 0).

Runnable locally against dist/; exits nonzero on any failure.
… (om-5gvj)

We ship six release targets and verified none of them on any PR — "runs on
six platforms" was a promise kept by hand. scripts/release.sh already
cross-compiles all six (linux/windows/darwin x amd64/arm64) plus both Windows
GUI-subsystem variants from one ubuntu box (CGO_ENABLED=0, pure-Go SQLite),
so this is a matter of running the script we already have, not adding an
Actions build matrix (which would be six runners doing one runner's job).

ci.yml gains three jobs:
- release-build (ubuntu): run scripts/release.sh, then verify-archives.sh,
  then unpack linux/amd64 and start `serve --spot-provider=none`, polling
  /api/health; uploads the archives for the native smoke jobs.
- smoke-windows (windows-latest): start cli/coinrollhunter.exe (the GUI build
  cannot print) and poll /api/health.
- smoke-macos (macos-latest, Apple silicon): start darwin/arm64 and poll
  /api/health.

Honest scope: build all SIX, start THREE. linux/arm64, darwin/amd64 and
windows/arm64 stay build-verified only — no hosted runner starts them
natively. Runners are free here (public repo).

release.yml runs the same verify-archives.sh before publishing, so a tagged
release can never ship a corrupt or wrong-subsystem archive either. Both new
build legs inherit the go-version-file pin (no floating go-version).
@tompscanlan
tompscanlan merged commit 2ad81e8 into main Jul 14, 2026
5 checks passed
@tompscanlan
tompscanlan deleted the ci/release-build-verify-on-pr branch July 14, 2026 18:40
tompscanlan added a commit that referenced this pull request Jul 15, 2026
…ten decode ceiling (om-6hlp)

Verify-stage fixes for adversarial-review findings on the photos branch:
- #1 (major, path escape): the upload route ran MkdirAll/CreateTemp on
  photos/<owner_uid>/... BEFORE validating owner_kind, so an unknown kind + a
  traversal owner_uid escaped photosDir (empty dir + a transient temp file) before
  InsertPhoto's Validate rejected it with a 400 — falsifying the feature's own
  'traversal structurally impossible' guarantee on the WRITE path. Now owner_kind,
  owner_uid (uidV4RE), and owner existence are all checked before any filesystem path
  is built, so the write path matches the serve route's whitelist-before-filesystem.
- #4 (test gap): TestUploadRejectsOwnerKindEscapeBeforeTouchingDisk — proven to FAIL
  pre-fix (created an ESCAPED dir outside photosDir) and PASS post-fix.
- #3 (decode DoS): maxPixels 100M->50M, maxDimension 30000->20000 — bounds the decode
  allocation (~200MB RGBA, recurs per lazy-regen cache miss), not just absurd headers.
- #2 (invariant honesty): soften the overstated 'committed row always has its
  original on disk' claim to record the commit->rename crash window; tracked in om-9occ.

Verified independently: go test ./... + -race clean; qa 106/106; live escape probe
(happy 201, both attacks 400 with no filesystem escape).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant