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

GET /api/v1/repos/:owner/:name/tags carries an ETag and answers a repeat with 304 until a tag is added, moved or deleted #9163

Merged opened this

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

Merged

`GET /api/v1/repos/:owner/:name/tags` now carries a weak ETag and answers a repeat that holds it with a 304. The new `test/tags-etag.test.ts` (5 tests) and the existing `test/api-tags-timing.test.ts` pass, and `bun x tsc --noEmit` is clean. I ran `bun scripts/gate.ts` but did not check its output, so its result is unconfirmed. I did not run the full suite; the forge runs the check on push.

shipped production is serving it deploy 0af2c9c

opened this

GET /api/v1/repos/:owner/:name/tags now carries a weak ETag and answers a repeat that holds it with a 304. The new test/tags-etag.test.ts (5 tests) and the existing test/api-tags-timing.test.ts pass, and bun x tsc --noEmit is clean. I ran bun scripts/gate.ts but did not check its output, so its result is unconfirmed. I did not run the full suite; the forge runs the check on push.

What changed

  • The tags route in src/http/api.ts hashes the JSON it is about to send with weakEtag. It replies with that etag, cache-control: private, no-cache and vary: authorization, cookie.
  • If If-None-Match holds the tag (holds), the reply is a 304 with the same headers and no body. The shared response floor still adds x-content-type-options to the 304, and the test checks it.
  • The 422 (bad limit, bad or stale cursor) and 404 replies return before that code, so they carry no ETag.
  • A tag pushed, re-pointed or deleted changes the body, so the tag changes. limit and cursor change which items and next_cursor the body holds, so they change it too.

What the test covers

  • The first read has a weak ETag with the cache headers.
  • A repeat is a 304 with an empty body and the security header.
  • A different limit gets a different ETag.
  • A 422 and a 404 carry no ETag.
  • Pushing, re-pointing and deleting a tag each make the old ETag miss. The test makes these changes with git update-ref in the bare repo, not through a real git push.
  • I did not run the "fails without the change" check by reverting the route.

What I chose not to do

  • No cache of refs. The ETag is only a hash of the rendered body, as the issue says.

To try it

  • curl -i -H "authorization: Bearer …" $FORGE_URL/api/v1/repos/OWNER/NAME/tags gives the etag.
  • Repeat the request with -H 'if-none-match: <that etag>' and it returns 304.
  • vary also lists Accept-Encoding, which the server adds, so the test checks that it contains authorization, cookie rather than equals it.

Closes #9161

commented

GET /api/v1/repos/:owner/:name/tags now carries a weak ETag and answers a repeat that holds it with a 304. The new test/tags-etag.test.ts (5 tests) and the existing test/api-tags-timing.test.ts pass, and bun x tsc --noEmit is clean. I ran bun scripts/gate.ts but did not check its output, so its result is unconfirmed. I did not run the full suite; the forge runs the check on push.

What changed

  • The tags route in src/http/api.ts hashes the JSON it is about to send with weakEtag. It replies with that etag, cache-control: private, no-cache and vary: authorization, cookie.
  • If If-None-Match holds the tag (holds), the reply is a 304 with the same headers and no body. The shared response floor still adds x-content-type-options to the 304, and the test checks it.
  • The 422 (bad limit, bad or stale cursor) and 404 replies return before that code, so they carry no ETag.
  • A tag pushed, re-pointed or deleted changes the body, so the tag changes. limit and cursor change which items and next_cursor the body holds, so they change it too.

What the test covers

  • The first read has a weak ETag with the cache headers.
  • A repeat is a 304 with an empty body and the security header.
  • A different limit gets a different ETag.
  • A 422 and a 404 carry no ETag.
  • Pushing, re-pointing and deleting a tag each make the old ETag miss. The test makes these changes with git update-ref in the bare repo, not through a real git push.
  • I did not run the "fails without the change" check by reverting the route.

What I chose not to do

  • No cache of refs. The ETag is only a hash of the rendered body, as the issue says.

To try it

  • curl -i -H "authorization: Bearer …" $FORGE_URL/api/v1/repos/OWNER/NAME/tags gives the etag.
  • Repeat the request with -H 'if-none-match: <that etag>' and it returns 304.
  • vary also lists Accept-Encoding, which the server adds, so the test checks that it contains authorization, cookie rather than equals it.
commented

GET /api/v1/repos/:owner/:name/tags now carries a weak ETag and answers a repeat that holds it with a 304 and no body. The gate failed because none of my commits had a "Try:" line, so I added an empty commit with one. bun scripts/gate.ts printed nothing when I ran it. The code is unchanged since the last push, and I did not re-run the tests or tsc this turn.

What changed

  • The tags route in src/http/api.ts hashes the JSON it is about to send with weakEtag. It replies with that etag, cache-control: private, no-cache and vary: authorization, cookie.
  • A request whose If-None-Match holds the tag (holds) gets a 304 with the same headers. The security headers stay on it.
  • Pushing, re-pointing or deleting a tag changes the body, so the ETag changes. A different limit or cursor changes the page, so it changes too.
  • A 422 (bad limit, bad or stale cursor) and a 404 return before that code, so they carry no ETag.
  • test/tags-etag.test.ts (5 tests) covers the first read's headers, the 304 on a repeat, a changed ETag after a tag is added, moved or deleted, a different limit getting a different ETag, and no ETag on the 422 and 404. The tag changes in that test are made with git update-ref in the bare repo, not a real git push. I did not revert the route to confirm each test fails without it.

What I chose not to do

  • No cache of refs. The ETag is only a hash of the rendered body.

To try it

  • Call GET /api/v1/repos/<owner>/<name>/tags on the preview and note the etag.
  • Repeat the call with that value in If-None-Match and expect a 304.
  • vary also lists Accept-Encoding, which the server adds.
commented

GET /api/v1/repos/:owner/:name/tags carries a weak ETag and answers a repeat that holds it with a 304 and no body. The failing check was in my test, not the route, and I've pushed a fix. I haven't seen the check's verdict on it yet.

Cause of the failure. The test "pushing, re-pointing and deleting a tag each make the old etag miss" makes a commit with git commit-tree. That needs a git author identity, and the check machine probably has none. I couldn't read the log to confirm that, but the test failed after 13 ms, which fits a git error. The test's git helper now passes -c user.name=t -c [email protected].

Checks run here. test/tags-etag.test.ts passes (5 tests), including with HOME unset and no system git config. bun x tsc --noEmit is clean. I didn't re-run the full suite; the forge's check does that.

What changed in the route. The tags route in src/http/api.ts hashes the JSON it is about to send with weakEtag.

  • It replies with that etag, cache-control: private, no-cache and vary: authorization, cookie.
  • A request whose If-None-Match holds the tag gets a 304 with the same headers. The security headers stay on it.
  • A pushed, moved or deleted tag changes the body, and so does a different limit or cursor, so the ETag changes with them.
  • A 422 (bad limit, bad or stale cursor) and a 404 return before that code, so they carry no ETag.

What I chose not to do. I added no cache of refs. The ETag is only a hash of the rendered body.

To try it. Call GET /api/v1/repos/<owner>/<name>/tags on the preview and note the etag. Repeat the call with that value in If-None-Match and expect a 304. vary also lists Accept-Encoding, which the server adds.

Approved

Clean and minimal: the tags route hashes the exact JSON it sends with weakEtag, answers a holding If-None-Match with a 304 carrying the same headers, and returns 422/404 before that code so they carry no ETag. securityFloor is applied by the shared responder and the test checks it on the 304. Tests cover headers, 304, limit changing the tag, errors without etag, and add/re-point/delete changing it. No README change, no new dependency. API-only change, so no TUI screen is needed.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs