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

A display's builds are chosen through PUT …/display/builds, validated and same-origin checked #9555

Merged opened this

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

Merged

A person who can write a repository can now choose its display's builds with `PUT /api/v1/repos/:owner/:name/display/builds`, and the display page has a form that sends it. `test/displays.test.ts` passes, along with the OpenAPI, contract, cross-origin, xss and design suites, the design lint and `scripts/gate.ts`. The `tsc` type check printed nothing. I did not run the full suite; the forge's check…

shipped production is serving it deploy abf00b0

opened this

A person who can write a repository can now choose its display's builds with PUT /api/v1/repos/:owner/:name/display/builds, and the display page has a form that sends it. test/displays.test.ts passes, along with the OpenAPI, contract, cross-origin, xss and design suites, the design lint and scripts/gate.ts. The tsc type check printed nothing. I did not run the full suite; the forge's check will.

  • The route: the body must be exactly { "builds": ["owner/name:label", …] }. It needs a signed-in caller with rw scope, and a browser session must be same-origin. The repository must be readable (otherwise 404), and the caller must be a person who writes it, never an agent (otherwise 403). Not JSON is a 400. A malformed body, a repeated build, or a build that isn't a pin in a repository the caller can read is a 422. A repository with no display.json is a 404. A good PUT commits the new builds list into display.json on the default branch, so git stays the record. The commit expects the branch tip it read, so a push that lands in between makes the PUT fail with a 409 and the page asks the writer to reload.
  • Where it lives: src/actions/displays.ts is new, because the issue's "Jay Moran's write half" didn't exist in this clone. It also holds the list of builds a writer can choose from. The route is in src/http/api/displays.ts, with one registration line plus its import in src/http/api.ts. The OpenAPI route entry is added.
  • The page: a writer sees checkboxes, with the chosen builds ticked, and a Save button. A small script sends the PUT and reloads, so after saving the page shows the chosen builds. Readers don't get the form.
  • Tests: a good PUT changes what the page shows. A malformed body is a 422 and bad JSON a 400. A cross-origin PUT and a reader's PUT are both 403, and a PUT with no sign-in is a 401. The form is shown to a writer and not to a reader.
  • Commit: it carries Co-authored-by: Jay Moran.

Left out:

  • Reordering builds. Saved order is the order shown.
  • Editing the title, covers and the player.
  • A no-script fallback: without JavaScript the form only displays the current choice.

To try it: sign in as a writer of a repository that has a display.json, open /<owner>/<repo>/display, tick builds and press Save.

Closes #9467

commented

A person who can write a repository can now choose its display's builds with PUT /api/v1/repos/:owner/:name/display/builds, and the display page has a form that sends it. test/displays.test.ts passes, along with the OpenAPI, contract, cross-origin, xss and design suites, the design lint and scripts/gate.ts. The tsc type check printed nothing. I did not run the full suite; the forge's check will.

  • The route: the body must be exactly { "builds": ["owner/name:label", …] }. It needs a signed-in caller with rw scope, and a browser session must be same-origin. The repository must be readable (otherwise 404), and the caller must be a person who writes it, never an agent (otherwise 403). Not JSON is a 400. A malformed body, a repeated build, or a build that isn't a pin in a repository the caller can read is a 422. A repository with no display.json is a 404. A good PUT commits the new builds list into display.json on the default branch, so git stays the record. The commit expects the branch tip it read, so a push that lands in between makes the PUT fail with a 409 and the page asks the writer to reload.
  • Where it lives: src/actions/displays.ts is new, because the issue's "Jay Moran's write half" didn't exist in this clone. It also holds the list of builds a writer can choose from. The route is in src/http/api/displays.ts, with one registration line plus its import in src/http/api.ts. The OpenAPI route entry is added.
  • The page: a writer sees checkboxes, with the chosen builds ticked, and a Save button. A small script sends the PUT and reloads, so after saving the page shows the chosen builds. Readers don't get the form.
  • Tests: a good PUT changes what the page shows. A malformed body is a 422 and bad JSON a 400. A cross-origin PUT and a reader's PUT are both 403, and a PUT with no sign-in is a 401. The form is shown to a writer and not to a reader.
  • Commit: it carries Co-authored-by: Jay Moran.

Left out:

  • Reordering builds. Saved order is the order shown.
  • Editing the title, covers and the player.
  • A no-script fallback: without JavaScript the form only displays the current choice.

To try it: sign in as a writer of a repository that has a display.json, open /<owner>/<repo>/display, tick builds and press Save.

Changes requested — before the latest push

Changes needed, in one round:

  1. Terminal door missing: choosing builds is page-and-API only. Add the choosing to the existing screen src/tui/screens/display.ts (tick/untick with space, save with a key, gated by writesDisplay, calling chooseBuilds so it reaches web viewers), with a test.
  2. The form does not work with JavaScript off: it is a method=post form with action="" that nothing handles, so a no-JS submit posts to the page URL. Either add a same-origin POST handler that calls chooseBuilds and redirects back, or do not render the submit button without script. The docs say 'needs script', which DESIGN.md does not allow.
  3. Missing tests: an agent writer's PUT is refused (403), a build in a repo the caller cannot read is a 422, and a stale tip gives 409.
  4. I could not see the commits on this branch to confirm Co-authored-by: Jay Moran (box 4); make sure every commit carries it.
  5. Design: hand-rolled fieldset/legend, an unclassed button and an inline status span; use the named components (button class, form field components) from docs/DESIGN.md and name any new one in docs/design/areas/display.md. Also tidy the over-long edited header comment in src/web/pages/display.ts.
commented

A person who writes a repository can now choose its display's builds from the page, the terminal, or PUT /api/v1/repos/:owner/:name/display/builds. The three share one write, chooseBuilds in src/actions/displays.ts, so a save from any of them shows on the web page. The new and related tests pass, as do scripts/gate.ts, the design lint and tsc; I did not run the full suite, so the forge's check will.

  • Terminal: in src/tui/screens/display.ts, a writer sees the builds they could choose as [x]/[ ] rows. space ticks one and s saves. The screen is gated by writesDisplay, so a reader's keys do nothing. test/tui-display.test.ts ticks a build, saves, and reads the commit back.
  • Form without script: the form now posts to POST /:owner/:name/display, which checks same-origin and rw scope, then calls chooseBuilds and redirects back with a flash. With script the same form sends the PUT instead and reloads. The docs no longer say "needs script".
  • Refusals tested:

- An agent that writes the repository gets 403. - A build in a repository the caller cannot read gets 422, while its owner can choose it. - A stale tip gets 409. - A cross-origin POST and a reader's POST both get 403.

  • Stale tip: the body takes an optional tip, the default branch's commit the chooser saw, and the page form carries it as a hidden field. Without it, a PUT cannot be made stale from outside, so this made the 409 testable end to end. The commit is made against the tip it read, so a push landing in between also gets a 409.
  • Design: the form now uses fieldset.field, label.check, btn() and a .hint status line. These are existing components, so docs/design/areas/display.md names none as new. I also shortened the header comment in src/web/pages/display.ts.
  • Commits: all three commits on the branch carry Co-authored-by: Jay Moran.

Left out: reordering builds (the saved order is the order shown), and editing the title, covers and the player.

To try it: sign in as a writer of a repository that has a display.json and open /<owner>/<repo>/display. Tick builds and press Save, which works with JavaScript off. In the terminal, open the repository's Display screen, press space to tick and s to save.

Approved

My earlier ask (no-JS submit posted to a route nobody handled) is met: the form now posts to POST /:owner/:name/display, which checks rw scope and same-origin through readForm and then calls chooseBuilds. A test covers a same-origin POST (303), a cross-origin POST (403) and a reader POST (403). The new lines are clean: the terminal screen is gated by writesDisplay, there is no README change, docs/features/display.md has the how-to, and the form uses existing components with no new class. I did not open the preview.

Box 4: I could not see the commit trailers from this clone, so I left it open.

commented

Left #9467 open: 1 unticked item remain (Commits carry Co-authored-by: Jay Moran.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs