-
Notifications
You must be signed in to change notification settings - Fork 17
feat(dist): pure-Go releases, a cache preset, --idle-exit, and gateway conformance
#141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0ad717e
b652546
e1ef6b9
0177df8
93677f6
953f648
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| name: Release | ||
|
|
||
| # Tag-driven, so a release is something a maintainer does on purpose. The `workflow_dispatch` | ||
| # entry builds the same matrix WITHOUT publishing (snapshot mode), which is how the release | ||
| # path gets exercised before there is a tag to regret. | ||
| on: | ||
| push: | ||
| tags: ["v*"] | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| release: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| # Only the tag path publishes, and only this job needs the write. | ||
| contents: write | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| # GoReleaser's changelog needs the history the default shallow clone does not have. | ||
| fetch-depth: 0 | ||
| - uses: actions/setup-go@v5 | ||
| with: | ||
| # Same source as CI (go.mod), deliberately: an artifact people download must be built | ||
| # with the toolchain CI validated, and a literal here is how that drifts apart. | ||
| go-version-file: go.mod | ||
|
|
||
| # The claim the release rests on, asserted in CI rather than trusted: the shipped | ||
| # binary needs no C toolchain. CGO_ENABLED=0 with no compiler on PATH would fail loudly | ||
| # here if a cgo dependency ever escaped the cg_skeleton build tag — which is exactly the | ||
| # regression that would otherwise be discovered by an evaluator, at install time. | ||
| - name: Assert the binary is pure Go | ||
| env: | ||
| CGO_ENABLED: "0" | ||
| CC: /nonexistent-c-compiler | ||
| run: | | ||
| go build -o /tmp/cg-purego ./cmd/context-guru-proxy | ||
| file /tmp/cg-purego | tee /dev/stderr | grep -q "statically linked" | ||
| # And it has to actually start, not just link. | ||
| /tmp/cg-purego --listen 127.0.0.1:4471 --preset cache & | ||
| for _ in $(seq 1 40); do | ||
| sleep 0.25 | ||
| curl -fsS http://127.0.0.1:4471/healthz && break | ||
| done | ||
| curl -fsS http://127.0.0.1:4471/healthz | grep -q ok | ||
| # An installer asks the binary what it is; make sure it can answer. | ||
| /tmp/cg-purego --version | tee /dev/stderr | grep -q context-guru-proxy | ||
|
|
||
| # Nothing tested the configuration we actually SHIP. | ||
| # | ||
| # ci.yaml runs the suite only with CGO_ENABLED=1, and a tag push previously published | ||
| # without running any tests at all. So the one guard that matters most to a released | ||
| # artifact — TestEveryPresetBuilds, which catches a preset naming a component that is not | ||
| # registered in a CGO-free binary — was never executed in the CGO-free configuration. That | ||
| # is exactly the `preset: coding` / `unknown component "skeleton"` failure, in a build no | ||
| # developer runs locally. | ||
| # | ||
| # The race detector needs cgo, so this cannot be the whole suite; it is the packages whose | ||
| # behaviour depends on which components are compiled in. | ||
| - name: Test the shipped configuration (CGO off, no race detector) | ||
| env: | ||
| CGO_ENABLED: "0" | ||
| # -p 1 for the reason ci.yaml's purego job documents: on a 2-core runner, parallel package | ||
| # binaries provoked a real flake (#163), and this workflow runs the full suite a few steps | ||
| # below as well, so the contention here is at least as bad. A tag push must not fail to | ||
| # publish for a cause already diagnosed and mitigated one file over. | ||
| run: go test -p 1 ./config/... ./components/... ./apply/... ./proxy/... ./store/... | ||
|
|
||
| # A tag must not publish something the full suite has not seen. | ||
| - name: Full test suite | ||
| env: | ||
| CGO_ENABLED: "1" | ||
| run: go test ./... | ||
|
|
||
| - name: Release | ||
| uses: goreleaser/goreleaser-action@v6 | ||
| with: | ||
| version: "~> v2" | ||
| # A tag publishes; a manual run builds the full matrix and publishes nothing. | ||
| args: ${{ startsWith(github.ref, 'refs/tags/v') && 'release --clean' || 'release --clean --snapshot' }} | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Upload snapshot artifacts | ||
| if: ${{ !startsWith(github.ref, 'refs/tags/v') }} | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: snapshot-dist | ||
| path: | | ||
| dist/*.tar.gz | ||
| dist/checksums.txt | ||
| retention-days: 7 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # GoReleaser: the release artifacts an evaluator downloads instead of installing a toolchain. | ||
| # | ||
| # The whole file is a plain GOOS/GOARCH matrix with no C cross-toolchains, no zig, and no | ||
| # libc coupling, because `CGO_ENABLED=0` builds the shipped binary. That is verified rather | ||
| # than assumed — the `Assert the binary is pure Go` step in .github/workflows/release.yaml fails | ||
| # the release if a cgo dependency ever escapes the cg_skeleton build tag. Measured directly on | ||
| # go 1.26.4: | ||
| # all four targets build, the artifact is 27–34 MB stripped, `file` reports "statically | ||
| # linked" and `ldd` "not a dynamic executable", and the resulting binary serves /healthz. | ||
| # | ||
| # `cg_skeleton` is the ONE thing that needs cgo (tree-sitter), and it is deliberately not | ||
| # built here: it is in no default preset, not in the cache story, and shipping it would mean | ||
| # per-platform C cross-compilation for a component this funnel never runs. Source build is | ||
| # documented in docs/components/skeleton.md. | ||
| # | ||
| # There is no `brews:` block yet — the tap repo and release signing are an open ownership | ||
| # question (spec §"Open questions", 3). Until it is answered the funnel installs from the | ||
| # release tarball, so nothing here depends on a repo that does not exist. Adding the tap | ||
| # later is additive and changes none of the below. | ||
| version: 2 | ||
|
|
||
| project_name: context-guru | ||
|
|
||
| before: | ||
| hooks: | ||
| - go mod download | ||
|
|
||
| builds: | ||
| - id: context-guru-proxy | ||
| main: ./cmd/context-guru-proxy | ||
| binary: context-guru-proxy | ||
| env: | ||
| # The point of the whole file. Not inherited from the Makefile, which sets | ||
| # CGO_ENABLED=1 because `go test -race` needs it — a test-time requirement that was | ||
| # being read as a shipping requirement. | ||
| - CGO_ENABLED=0 | ||
| flags: | ||
| # Reproducible paths in panics, and no VCS stamping (the checkout is shallow in CI). | ||
| - -trimpath | ||
| - -buildvcs=false | ||
| ldflags: | ||
| # Same two symbols the Makefile stamps, so `/stats` build_version is populated in a | ||
| # released binary exactly as it is in a locally built one. | ||
| - -s -w | ||
| - -X github.com/rossoctl/context-guru/internal/buildinfo.Version={{ .Version }} | ||
| - -X github.com/rossoctl/context-guru/internal/buildinfo.Commit={{ .ShortCommit }} | ||
| goos: [linux, darwin] | ||
| goarch: [amd64, arm64] | ||
|
|
||
| archives: | ||
| - id: default | ||
| ids: [context-guru-proxy] | ||
| # An evaluator untars this into ~/.local/bin, so the archive name is what they see and | ||
| # the binary inside must be the plain name with no version in it. | ||
| name_template: >- | ||
| {{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }} | ||
| formats: [tar.gz] | ||
| # Everything unpacks into ONE directory named after the archive. | ||
| # | ||
| # Without this GoReleaser writes a flat archive, so LICENSE, README.md and THIRD-PARTY-NOTICES | ||
| # land at the root — and the extraction command in the footer below (and in the quickstart) has | ||
| # no -C, so running it in a project directory silently overwrites that project's own README.md | ||
| # and LICENSE. A person evaluating a proxy for their coding agent is standing in exactly such a | ||
| # directory. | ||
| wrap_in_directory: true | ||
| files: | ||
| - LICENSE | ||
| - README.md | ||
| - THIRD-PARTY-NOTICES | ||
|
|
||
| checksums: | ||
| # The integrity check for every downloaded artifact, and the ONLY one: these binaries are | ||
| # unsigned (the tap and signing ownership are still open), so nothing else stands between a | ||
| # tampered tarball and a proxy that handles the user's LLM traffic. | ||
| # | ||
| # The plugin's installer verifies against this file and strips macOS quarantine from the | ||
| # download, which is what makes it load-bearing rather than decorative. That installer is NOT in | ||
| # this change — it ships with the plugin — so today this file is what a human curling a release | ||
| # should check by hand. | ||
| name_template: checksums.txt | ||
| algorithm: sha256 | ||
|
|
||
| snapshot: | ||
| version_template: "{{ incpatch .Version }}-next" | ||
|
|
||
| changelog: | ||
| use: github | ||
| sort: asc | ||
| filters: | ||
| exclude: | ||
| - "^docs:" | ||
| - "^test:" | ||
| - "^chore:" | ||
| - "^ci:" | ||
|
|
||
| release: | ||
| prerelease: auto | ||
| footer: | | ||
| ## Install | ||
|
|
||
| No Go toolchain and no C compiler are needed — the binary is statically linked. | ||
|
|
||
| Download the tarball for your platform, untar it, and put `context-guru-proxy` on your | ||
| `PATH`. The archive unpacks into its own directory, so this is safe to run anywhere: | ||
|
|
||
| ``` | ||
| tar xzf context-guru_*_darwin_arm64.tar.gz | ||
| install -m 755 context-guru_*/context-guru-proxy ~/.local/bin/ | ||
| ``` | ||
|
|
||
| Then see `docs/get-started/quickstart-proxy.md`. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,6 +108,9 @@ docker build -t context-guru:local . | |
|
|
||
| ## Quickstart (60 seconds) | ||
|
|
||
| Download a release binary — statically linked, **no Go and no C compiler needed** — or build | ||
| from source: | ||
|
|
||
| ```sh | ||
| # 1 — run the proxy (ships with the SWE-bench-winning cache-aware config by default) | ||
| ./bin/context-guru-proxy # --preset house (the default); listens on :4000 | ||
|
|
@@ -144,8 +147,10 @@ See [docs/components.md](docs/components.md) and [docs/reference/presets.md](doc | |
| | Flag / env | Default | Purpose | | ||
| |---|---|---| | ||
| | `--preset` / `PRESET` | `house` | pipeline preset when no `--config` | | ||
| | `--idle-exit` / `IDLE_EXIT` | `0` (never) | exit after this long unused; floor `max(2 × store.ttl_seconds, 1h)`, refused with `--upstreams` | | ||
| | `--version` | — | print version and commit, then exit | | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two lines below this, the table still documents only |
||
| | `--config` / `CONFIG` | — | YAML config (overrides preset) | | ||
| | `LISTEN_ADDR` | `:4000` | listen address | | ||
| | `--listen` / `LISTEN_ADDR` | `:4000` | listen address. The flag exists so the port is visible in `ps` and to a supervisor | | ||
| | `--anthropic-upstream` / `ANTHROPIC_UPSTREAM` | `https://api.anthropic.com` | Anthropic upstream base | | ||
| | `--openai-upstream` / `OPENAI_UPSTREAM` | `https://api.openai.com` | OpenAI upstream base | | ||
| | `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` | — | real key injected on forward (gateway mode); empty = pass client auth through | | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No
wrap_in_directory, so the documented untar command clobbers the user's files.archives[0].filesputsLICENSE,README.mdandTHIRD-PARTY-NOTICESat the archive root (GoReleaser defaultswrap_in_directoryto false), and both the release footer (:95) anddocs/get-started/quickstart-proxy.mdtell the evaluator to runtar xzf context-guru_*_darwin_arm64.tar.gz && install -m 755 context-guru-proxy ~/.local/bin/— with no-C.Run in a project directory, which is exactly where someone evaluating a proxy for their agent is standing, that silently overwrites their own
README.mdandLICENSE. Either setwrap_in_directory: true, or make the documented command extract into a temp dir.