Skip to content

feat(release): add GoReleaser config and release CI workflow - #19

Merged
shreyanshjain7174 merged 2 commits into
mainfrom
ci/goreleaser-release-workflow
Aug 19, 2026
Merged

feat(release): add GoReleaser config and release CI workflow#19
shreyanshjain7174 merged 2 commits into
mainfrom
ci/goreleaser-release-workflow

Conversation

@shreyanshjain7174

@shreyanshjain7174 shreyanshjain7174 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Rewritten now that #20 (pure-Go sqlite driver) merged — this closes out the release-engineering queue per the stated merge order.

What changed:

  • .goreleaser.yaml: single builds entry per binary (agentgw, agentgate-verify), CGO_ENABLED=0, full goos/goarch matrix: linux, darwin, windows × amd64, arm64. No more Linux-only + separate macOS job. Windows is in, since CGO/mingw was the only reason it wasn't before.
  • .github/workflows/release.yml: collapsed to one ubuntu-latest runner. Deleted the gcc-aarch64-linux-gnu/libc6-dev-arm64-cross/libsqlite3-dev install step, the entire darwin job, and the gh release upload dance that attached macOS binaries after the fact.
  • Dockerfile: rewritten FROM scratch. A static pure-Go binary needs no base OS image — no libc, no shell, no package manager, smaller attack surface. Only the CA cert bundle is copied in, for the outbound TLS calls this gateway makes to upstream SaaS APIs during OAuth token refresh.

Verified for real, not just linted:

  • goreleaser check — passes.
  • goreleaser release --snapshot --clean --skip=docker,publish — all 12 binaries build (6 platforms × 2 binaries), all 6 archives produced correctly (windows zipped with .exe binaries, others tar.gz).
  • docker build + docker run locally against the new Dockerfile: image is 9.4MB, server starts, dashboard serves correctly at /dashboard/, confirmed no shell available via docker exec (attack surface claim isn't just asserted).
  • Caught and fixed a real bug before pushing: ENTRYPOINT ["agentgate"] would fail on scratch — no default $PATH exists in that base, so name-based exec lookup fails. Fixed to the absolute path.
  • CI green on this PR itself: https://github.com/Clawdlinux/agentgate/actions (CI/ci check, 25s).

mergeable_state: clean, base already includes #20/#21/#22.

shreyanshjain7174 added a commit that referenced this pull request Aug 19, 2026
#20)

* feat(deps): replace mattn/go-sqlite3 with modernc.org/sqlite (pure Go)

Spike per T1: swap the CGO SQLite driver for the pure-Go one, verified
before merging, not assumed.

Why: the CGO dependency was forcing split-runner builds, cross-gcc for
linux/arm64, and dropping Windows entirely from PR #19. It also means
agentgate-verify dynamically links the host libc — an auditor on musl,
distroless, or an older glibc gets a linker error instead of a
verification, undermining the actual product claim (independent
offline verification, runnable anywhere).

Driver registers as "sqlite", not "sqlite3" - every sql.Open call site
updated. DSN pragma shorthand keys (_journal_mode, _busy_timeout,
_foreign_keys) are identical between both drivers, so the connection
string in internal/db/sqlite.go is unchanged content, just the driver
name.

Acceptance criteria, all actually run:

1. CGO_ENABLED=0 go build ./... - succeeds.
2. CGO_ENABLED=0 go test ./... - all packages pass, zero skipped
   (confirmed via `go test -v | grep -c SKIP` = 0).
3. CGO_ENABLED=0 cross-builds succeed for all 6 targets from one
   machine, no cross-compilers: linux/amd64, linux/arm64, darwin/amd64,
   darwin/arm64, windows/amd64, windows/arm64 (agentgw +
   agentgate-verify, 12 binaries total).
4. Synchronous ledger commit latency (Ledger.Append, the exact path
   used before /v1/act responds), pooled over 8 runs x 10,000 commits
   = 80,000 samples per driver, same machine, back-to-back via a git
   worktree at the pre-swap commit for the old-driver baseline:

       driver                          mean     p50     p90     p99
       mattn/go-sqlite3 (CGO)          83.5us   59.5us  89.2us  337.0us
       modernc.org/sqlite (pure Go)   156.8us  104.5us 175.6us  654.4us
       ratio (new/old)                 1.88x    1.76x   1.97x    1.94x

   p99 regresses 1.94x - under the 2x decision-rule threshold. Both
   numbers are a fraction of a millisecond; for an append-heavy ledger
   with range scans this is the honest, measured tradeoff, not an
   assumption.
5. Cross-driver chain verification: wrote two 5-receipt test databases,
   one with each driver, then ran every combination of
   {verify built with new driver, verify built with old driver} x
   {db written by new driver, db written by old driver} - all 4 PASS.
   Chain verification does not care which driver wrote the rows, as
   expected (SQLite's file format doesn't depend on which Go driver
   wrote it).

Per the stated decision rule (stop only if p99 > 2x), this passes.
PR #19 (GoReleaser + release workflow) should be revised on top of
this to delete the darwin job, cross-gcc config, and Pro-style
split complexity entirely, collapsing to one ubuntu-latest runner
with CGO_ENABLED=0 and a full goos/goarch matrix including Windows.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>

* test: temporary linux/amd64 percentile benchmark for #20

One-off, deleted before merge. Confirms whether the darwin/arm64
ledger-commit benchmark generalizes to linux/amd64, since
modernc.org/sqlite is transpiled C and its profile can differ by
platform.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>

* test: remove temporary linux/amd64 percentile benchmark

Numbers captured and reported. This harness and one-off workflow were
throwaway measurement tools for the #20 driver-swap decision, not
meant to be part of the permanent test suite or CI config.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>

---------

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>
shreyanshjain7174 added a commit that referenced this pull request Aug 19, 2026
…gofmt, mod-tidy drift check

Adds .github/workflows/ci.yml. PR #19's release.yml only triggers on
tag push, so pull requests currently get zero validation - six good
first issue tickets invite outside contributors and their PRs would
run nothing.

Installs gcc + libsqlite3-dev since main still uses the CGO
mattn/go-sqlite3 driver as of this commit; comments in the file note
to drop that step and set CGO_ENABLED=0 once PR #20 (pure-Go sqlite
driver swap) merges.

Adds the CI status badge to README.md.

Every step verified locally against current main before opening this
PR: go build, go vet, go test -race, gofmt -l (clean), and go mod
tidy produces no diff. The actual acceptance criterion is that this
fires green on the PR itself, not on local runs - see the PR
description for the live check run link.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>
shreyanshjain7174 added a commit that referenced this pull request Aug 19, 2026
* ci: add pull_request/push:main workflow with build, vet, test -race, gofmt, mod-tidy drift check

Adds .github/workflows/ci.yml. PR #19's release.yml only triggers on
tag push, so pull requests currently get zero validation - six good
first issue tickets invite outside contributors and their PRs would
run nothing.

Installs gcc + libsqlite3-dev since main still uses the CGO
mattn/go-sqlite3 driver as of this commit; comments in the file note
to drop that step and set CGO_ENABLED=0 once PR #20 (pure-Go sqlite
driver swap) merges.

Adds the CI status badge to README.md.

Every step verified locally against current main before opening this
PR: go build, go vet, go test -race, gofmt -l (clean), and go mod
tidy produces no diff. The actual acceptance criterion is that this
fires green on the PR itself, not on local runs - see the PR
description for the live check run link.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>

* fix(ci): pin DEBIAN_FRONTEND=noninteractive for apt-get install

First live run hung 10+ minutes on 'Install sqlite build dependencies'
- almost certainly needrestart/debconf prompting interactively on a
sudo apt-get install with no noninteractive frontend set. Cancelled
that run (32233234510) and fixed rather than reporting the earlier
local-only pass as done.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>

* fix(ci): wrap apt-get in timeout+retry after a real 10min network stall

Run 32233234510's 'Install sqlite build dependencies' step went silent
for over 10 minutes mid apt-get update (checked the actual log
timestamps - real network stall fetching package indices, not a
debconf prompt, not a lock). Wraps both apt-get calls in a 120s
timeout with up to 3 attempts so a future transient mirror stall
fails fast and retries instead of hanging the whole job.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>

* ci: drop CGO build deps now that #20 merged

github.com//pull/20 replaced mattn/go-sqlite3 with
the pure-Go modernc.org/sqlite. No more gcc/libsqlite3-dev needed, no
more apt-get retry wrapper for the network stall that only existed to
install those. Set CGO_ENABLED=0 globally instead.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>

* ci: -race needs CGO_ENABLED=1, doesn't need sqlite build deps

go test -race genuinely requires cgo on linux (confirmed via a real
failing CI run: 'go: -race requires cgo; enable cgo by setting
CGO_ENABLED=1'), independent of whatever sqlite driver is in use.
ubuntu-latest ships gcc via build-essential, so this needs no apt-get
step -- unlike the old libsqlite3-dev requirement this file used to
work around.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>

---------

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>
Closes the first punch-list gap: GoReleaser binaries + CI. SECURITY.md
and re-cutting v0.1.1 are next, in the stated order.

mattn/go-sqlite3 is CGO-based, which rules out a plain single-runner
GOOS/GOARCH matrix. GoReleaser Pro has a split/merge feature for this
(building each platform on its own native runner and combining them),
but that's a paid feature per goreleaser.com/resources/limitations/cgo.
This uses the free equivalent instead:

- .goreleaser.yaml builds only the Linux side (native amd64 + Linux
  arm64 cross-compiled via aarch64-linux-gnu-gcc, both from one
  ubuntu-latest runner), handles archives/checksums/changelog/GitHub
  release creation, and builds+pushes the ghcr.io Docker images
  (linux/amd64 + linux/arm64 via buildx/QEMU).
- The darwin job in release.yml runs after the linux job creates the
  release, builds macOS amd64/arm64 binaries with plain `go build` on a
  native macos-latest runner, tars/checksums them, and attaches them to
  the same release with `gh release upload`.
- Windows isn't built: cgo + sqlite3 + mingw cross-compilation is a
  real source of flakiness, not worth it until there's actual demand
  for a native Windows binary. The Docker image already covers that
  platform's users.

Verified before committing, not just linted:
- `goreleaser check` passes on the config.
- Linux amd64 native build and arm64 cross-compile (exact CC/env used
  in the config) both tested inside a real linux/amd64 Docker
  container, producing a working `agentgw --version` binary.
- Darwin amd64/arm64 builds tested natively on this machine via
  `goreleaser build --single-target`, producing working binaries with
  correctly injected -X main.version ldflags.
- The darwin job's tar/checksum shell logic tested standalone locally
  end-to-end (minus the actual `gh release upload` call).
- go build ./..., go vet ./..., and YAML parsing of both new files all
  pass.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>
github.com//pull/20 replaced mattn/go-sqlite3
(CGO) with modernc.org/sqlite (pure Go). This removes every reason
the release process was split across two runners:

- .goreleaser.yaml: one builds entry per binary, CGO_ENABLED=0, full
  goos/goarch matrix (linux, darwin, windows x amd64, arm64) instead
  of linux-only + a separate macOS job. Verified locally with
  'goreleaser check' and a full 'goreleaser release --snapshot --clean
  --skip=docker,publish' -- all 12 binaries build, all 6 archives
  produced correctly (windows zipped, others tar.gz).

- release.yml: collapsed to a single ubuntu-latest runner. Deleted the
  cross-gcc/libsqlite3-dev install step, the separate darwin job, and
  the 'gh release upload' dance that attached macOS binaries to the
  release after the fact.

- Dockerfile: FROM scratch. A static pure-Go binary needs no base OS
  image -- no libc, no shell, no package manager. Only the CA cert
  bundle is copied over, for the outbound TLS calls this gateway makes
  to upstream SaaS APIs during OAuth token refresh. Built and ran this
  locally: image is 9.4MB (was alpine-based before), server starts,
  dashboard serves correctly, no shell available via 'docker exec'.
  Also fixed ENTRYPOINT to use an absolute path -- scratch has no
  default $PATH, so 'agentgate' by name would fail to exec.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>
@shreyanshjain7174
shreyanshjain7174 force-pushed the ci/goreleaser-release-workflow branch from 827108f to cae8459 Compare August 19, 2026 19:13
@shreyanshjain7174
shreyanshjain7174 merged commit 1d9c395 into main Aug 19, 2026
1 check passed
@shreyanshjain7174
shreyanshjain7174 deleted the ci/goreleaser-release-workflow branch August 19, 2026 19:16
shreyanshjain7174 added a commit that referenced this pull request Aug 19, 2026
…file (#23)

The v0.1.1 tag push failed release.yml for real:

  ERROR: failed to build: failed to solve: failed to compute cache
  key: ... "/configs": not found

GoReleaser's docker build context only contains the Dockerfile, the
binaries it already cross-compiled in the builds: step, and whatever
extra_files: lists -- not the full repo. The rewritten Dockerfile from
#19 still had a multi-stage 'COPY go.mod go.sum ./' + 'COPY . .'
builder stage, which can never work in that context. This was never
caught because #19's local testing used 'docker build .' from the
repo root (full source available) and --skip=docker snapshot builds,
and the PR itself said as much: "Not yet tested: a real end-to-end
tag-triggered CI run." This is that first real run.

Fix: split into two Dockerfiles instead of trying to make one work in
both contexts.

- Dockerfile (unchanged behavior): multi-stage, builds from source.
  Used by docker-compose / local dev, where the full repo is the
  build context.
- Dockerfile.release (new): single small alpine helper stage for CA
  certs + an empty /data dir, then FROM scratch copying the binaries
  GoReleaser already built. Doesn't rebuild from source -- there's
  nothing to rebuild from in that context, and GoReleaser already did
  the cross-compile.
- .goreleaser.yaml: dockers[].dockerfile -> Dockerfile.release,
  added extra_files: [configs] since that's not in the context either.

Verified for real: 'goreleaser release --snapshot --clean --skip=publish'
now builds both docker images successfully (previously failed on
this exact step). Ran the amd64 image via 'docker run' (under QEMU,
darwin/arm64 host): server starts, version string correctly injected
(0.1.1-SNAPSHOT-1d9c395), 'curl .../dashboard/' returns 200. Image is
10.1MB.

Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>
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