Skip to content

Split release-admin.tsx into focused files - #970

Merged
selfcontained merged 1 commit into
mainfrom
agt_f55f59b3775c/job-componentizer-dc00ae60
Aug 17, 2026
Merged

Split release-admin.tsx into focused files#970
selfcontained merged 1 commit into
mainfrom
agt_f55f59b3775c/job-componentizer-dc00ae60

Conversation

@selfcontained

Copy link
Copy Markdown
Owner

What

ReleasesAdmin was 647 lines — the largest component left in apps/web — and the componentizer job's queued target. One component owned release-info fetching, the GitHub releases list, the promote mutation, and four independent render regions, with 12 useState + 4 useEffect + a ref between them.

The shell drops to 197 lines and now holds only the create-release flow (which needs the stream's setJob/connectStream) plus composition.

New structure

FileLinesContents
release-admin.tsx647 → 197stream + composition, create-release handler, progress-view takeover
release-admin-banners.tsx91ReleaseJobBanners — in-flight / failed / released banners
release-admin-unreleased.tsx121UnreleasedChanges — commit count, commit list, error branches
release-admin-create.tsx136CreateReleaseSection + VERSION_CONFIG + confirm dialog
release-admin-list.tsx131RecentReleases + inline promote-confirm flow
use-release-admin-data.ts150/release/info + /releases fetching, tick, promote mutation
release-utils.ts150 → 196CREATE_PHASES, GitHubRelease, bumpVersion, formatAgo

Per the recorded pattern about existing *-utils siblings, the pure helpers went into the existing release-utils.ts (next to UPDATE_PHASES) rather than a new module, and picked up tests in the existing release-utils.test.ts — 12 new assertions covering the bump prediction and the "checked Xs ago" label, neither of which had coverage before.

Why it's behaviour-identical

Purely structural — every JSX block and handler moved unchanged. Two hazards this codebase has hit before were checked explicitly:

  • Effect registration order.useReleaseAdminData is called immediately after useReleaseStream, so its mount-fetch and 30s-tick effects still register ahead of the watched-release and done-refresh effects that stayed in the shell — same order as before.
  • State lifetime.confirmType, promotingTag, confirmPromoteTag, and promoteError all stay in the always-mounted shell (in the component or the hook) rather than moving into the conditionally-rendered sections, so they survive the progress-view toggle exactly as they did.

Validation

  • pnpm run finalize:web — type check + production build pass
  • pnpm run test:e2e — 180 passed
  • release-utils.test.ts — 43 tests pass (31 existing + 12 new)
  • Playwright on a dev stack, Settings → Releases: all three sections render with real data; opened the create-release confirm dialog (correct predicted tag v0.34.4) and the inline promote-confirm row, cancelling both; refresh button cycles loading → "Checked just now". No real release or promotion was triggered.

Next run

markdown.tsx (489 lines) — a shared UI primitive loaded by many consumers, whose per-element renderer map looks like a clean constants extraction.

ReleasesAdmin was 647 lines: one component owning release-info
fetching, the GitHub releases list, the promote mutation, and four
independent render regions.
The shell drops to 197 lines and now just holds the create-release
flow plus composition:
- release-admin-banners.tsx (91) — ReleaseJobBanners
- release-admin-unreleased.tsx (121) — UnreleasedChanges
- release-admin-create.tsx (136) — CreateReleaseSection + VERSION_CONFIG
- release-admin-list.tsx (131) — RecentReleases
- use-release-admin-data.ts (150) — info/releases fetching + promote
- release-utils.ts — CREATE_PHASES, GitHubRelease, bumpVersion, formatAgo
(now covered by release-utils.test.ts)
Pure refactor: every JSX block and handler moved unchanged. The data
hook is called immediately after useReleaseStream so its two effects
still register ahead of the watched-release and done-refresh effects,
and all promote/confirm state stays in the shell so it survives the
progress-view toggle.
@selfcontained
selfcontained merged commit cc5dd8e into mainAug 17, 2026
1 check passed
@selfcontained
selfcontained deleted the agt_f55f59b3775c/job-componentizer-dc00ae60 branch August 17, 2026 08:13
selfcontained added a commit that referenced this pull request Aug 22, 2026
`release-admin.tsx` and the five modules #970 split out of it
(`release-admin-banners`, `-create`, `-list`, `-unreleased`, and
`use-release-admin-data`) had no tests at all. This adds one file that
mounts the real `ReleasesAdmin` — a fake `EventSource` for the release
stream and a URL-routed `fetch` — so the props wiring between the shell,
the panes, and the data hook is exercised together rather than mocked
apart.
What it pins:
- the `UnreleasedChanges` ternary ladder, in priority order: an
unreleased-fetch error outranks a missing ref, which outranks the
commit count; plus the commit list, the `+N more` overflow line, and
the singular/plural wording
- `useReleaseAdminData`: both mount fetches, the client-id header that
lets the server push info-progress back over the stream, `refresh()`
hitting both endpoints, and the promote mutation — request body,
the row flipping to stable, and a failed promote surfacing a cleaned
error while the confirmation collapses
- the promote confirm flow scoped to the row the operator clicked
- the create section's render gate, the bumped-tag previews (newest
GitHub release wins over the deployed tag), the confirm dialog, the
POST body, and the switch into the progress takeover
- the banner set: in-flight, failed, and the `watchedRelease` guard that
keeps a stale done snapshot from claiming a release just finished,
plus the refresh-once-per-run behaviour when one really does
Mutation battery: 45 mutants against the six modules, 45 killed.
Claude-Session: https://claude.ai/code/session_01Dqcng3rgYPEToKrksY3FZk
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto 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

@selfcontained