Public: anyone can read this; the crew can change it.

GET /api/v1/repos/:owner/:name/builds carries an ETag and answers a repeat with 304 when no pin has changed #9132

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`GET /api/v1/repos/:owner/:name/builds` now carries a weak `etag` and answers a repeat `If-None-Match` with an empty 304. The new test, the MCP size and builds tests, the OpenAPI and contract tests, `bun x tsc --noEmit` and `scripts/gate.ts` all pass. I did not run the full suite; the forge's check does that on this push.

shipped production is serving it deploy 652cd9c

opened this

GET /api/v1/repos/:owner/:name/builds now carries a weak etag and answers a repeat If-None-Match with an empty 304. The new test, the MCP size and builds tests, the OpenAPI and contract tests, bun x tsc --noEmit and scripts/gate.ts all pass. I did not run the full suite; the forge's check does that on this push.

What changed

  • Route (src/http/api/builds.ts): the tag is computed from the exact JSON sent, so a new, noted or deleted pin changes it. Nothing is cached by time. The 200 and the 304 both carry etag, cache-control: private, no-cache and vary: authorization, cookie. It uses weakEtag and holds from src/http/etag.ts, the same pattern as the issues list.
  • OpenAPI (getApiV1ReposOwnerNameBuilds.ts): the summary names the etag header and the 304, and the route lists 304 as an extra status.
  • Test (test/builds-etag.test.ts):

- Two requests give 200, then 304 with the same tag and no body. - After POST …/builds the old tag gets a 200 with a new tag. - After a note (PATCH) the tag changes again. - A stranger asking for a private repository's builds gets a 404 with no etag, even holding If-None-Match: *.

What I chose not to do

  • No new MCP tool, and no change to the MCP size ceilings.
  • Writes (POST, PATCH, DELETE) are unchanged.

How to try it

  • Run bun test test/builds-etag.test.ts.
  • Or curl -i the builds list, then repeat it with -H 'if-none-match: <etag>'.

🤖 Generated with Claude Code

Closes #9127

commented

GET /api/v1/repos/:owner/:name/builds now carries a weak etag and answers a repeat If-None-Match with an empty 304. The new test, the MCP size and builds tests, the OpenAPI and contract tests, bun x tsc --noEmit and scripts/gate.ts all pass. I did not run the full suite; the forge's check does that on this push.

What changed

  • Route (src/http/api/builds.ts): the tag is computed from the exact JSON sent, so a new, noted or deleted pin changes it. Nothing is cached by time. The 200 and the 304 both carry etag, cache-control: private, no-cache and vary: authorization, cookie. It uses weakEtag and holds from src/http/etag.ts, the same pattern as the issues list.
  • OpenAPI (getApiV1ReposOwnerNameBuilds.ts): the summary names the etag header and the 304, and the route lists 304 as an extra status.
  • Test (test/builds-etag.test.ts):

- Two requests give 200, then 304 with the same tag and no body. - After POST …/builds the old tag gets a 200 with a new tag. - After a note (PATCH) the tag changes again. - A stranger asking for a private repository's builds gets a 404 with no etag, even holding If-None-Match: *.

What I chose not to do

  • No new MCP tool, and no change to the MCP size ceilings.
  • Writes (POST, PATCH, DELETE) are unchanged.

How to try it

  • Run bun test test/builds-etag.test.ts.
  • Or curl -i the builds list, then repeat it with -H 'if-none-match: <etag>'.

🤖 Generated with Claude Code

commented

GET /api/v1/repos/:owner/:name/builds now carries a weak etag, and a repeat request holding that tag gets an empty 304. The previous push failed the gate because no commit message had a Try: line. I added an empty commit with one and pushed it. bun scripts/gate.ts printed no errors locally, and the push went through without rewriting history. The forge's check on this push hasn't reported yet, so the gate isn't confirmed green on the forge.

What changed

  • The 200 carries etag, cache-control: private, no-cache and vary: authorization, cookie. The tag is computed from the exact JSON sent, so a new, noted or deleted pin changes it.
  • A matching If-None-Match gets a 304 with no body and the same tag.
  • The route's OpenAPI summary names the etag header and the 304.
  • test/builds-etag.test.ts covers three cases:

- Two requests give a 200, then a 304. - A pin and a note each give a 200 with a new tag. - A stranger asking for a private repository's builds gets a 404 with no tag, even with If-None-Match: *.

What I chose not to do

  • No new MCP tool, and no change to the MCP size ceilings.
  • Writes (POST, PATCH, DELETE) are unchanged.

How to try it

  • Run bun test test/builds-etag.test.ts.
  • Or curl -i the preview's /api/v1/repos/<owner>/<name>/builds, then repeat with -H 'if-none-match: <etag>' and expect 304.

🤖 Generated with Claude Code

Approved

Clean: mirrors the issues-list ETag pattern, tag is of the exact bytes sent, 404 for unreadable repos comes before any tag, vary/cache-control are right, test covers 200/304/pin/note/private. No README or new dependency.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs