Skip to content

feat(releases): capture the README per release and serve it per version - #10

Merged
NewtTheWolf merged 3 commits into
mainfrom
feat/per-release-readme
Aug 18, 2026
Merged

feat(releases): capture the README per release and serve it per version#10
NewtTheWolf merged 3 commits into
mainfrom
feat/per-release-readme

Conversation

@NewtTheWolf

Copy link
Copy Markdown
Contributor

Reported on Discord: "if only I could understand how to get the README to populate consistently."

Why it was inconsistent

The README was only ever read on the git-ref manifest path. The asset-first path — the default, and the one every documented release takes — returned readme: null, and manifestPatch then wrote that null onto the plugin row.

So the README appeared after a manual Refresh (git-ref path) and was wiped again by the next release webhook (asset path). Nothing the author did was wrong.

Two fixes behind that one symptom:

  • The asset path now reads the README at the release's tag. A tag is immutable, so that is the README the release actually shipped with. The lookup order — readmes locale map, then the readme path, then the conventional root filenames — moved into one shared helper instead of living only in the git-ref path.
  • manifestPatch omits readme when a pass resolved none, instead of writing null. This mirrors the manifestSha256/manifestRaw guard persistRelease already had, for exactly the same reason.

Per-version READMEs

The README is now stored on the release row too, so opening an older version shows the docs that shipped with it rather than whatever the plugin's README says today.

  • GET /api/plugins/:slug/releases/:version/readme renders and caches it, mirroring the plugin-level README handling (locale map, pickReadme, TTL). pickReadme moved to $lib/readme so both callers share one reader.
  • The plugin page loads it lazily when a release row is expanded, so the detail payload does not grow a full README per version.
  • Releases predating the column report captured: false rather than falling back to the current README — a silent fallback would misrepresent the old version.

Backfill

POST /api/admin/plugins/:id/backfill-readmes walks the releases that have none, reads each one's README at its tag, and fills it in. The tags are all still on the forge, so the history is recoverable; releases whose tag or README is gone are skipped rather than failing the run.

(The release source zip would also contain it, but it is not an uploaded asset — it is a separate generated zipball_url — so using it would mean pulling a whole repo archive plus a zip decoder for one file. Reading the file at the tag is one small request and just as immutable.)

Schema

releases.readme, nullable, added for all three dialects. The app applies migrations at boot, so no manual step on deploy.

Verification

bun test 474 pass / 0 fail (6 new, covering tag-pinned capture, the locale map, and that a resolved-nothing pass no longer blanks the stored README), tsc clean, svelte-check 0 errors.

The README was only ever read on the git-ref manifest path. The
asset-first path — the default, and the one every documented release
takes — returned readme: null, and manifestPatch wrote that null over
the plugin row. So the README appeared after a manual refresh and was
wiped again by the next release, which is what authors saw as 'it will
not populate consistently'.

Two changes behind that symptom:

- The asset path now reads the README at the release's tag. A tag is
  immutable, so that is the README the release actually shipped with.
  The lookup order (readmes locale map, readme path, then the
  conventional root filenames) is shared with the git-ref path instead
  of duplicated.
- manifestPatch omits  when a pass resolved none, rather than
  writing null. Mirrors the manifestSha256/manifestRaw guard that
  persistRelease already had for the same reason.

The README is also stored on the release row, so browsing an older
version shows the docs that shipped with it instead of whatever the
plugin's README says today. Served by
GET /api/plugins/:slug/releases/:version/readme and loaded lazily when
a release row is expanded, so the detail payload does not grow a full
README per version. Releases predating the column report captured:false
instead of falling back to the current README, which would quietly
misrepresent the old version.

For that history, POST /api/admin/plugins/:id/backfill-readmes reads
each release's README at its tag and fills it in — tags are still on the
forge, so nothing is lost.

Migration added for all three dialects; the app applies them at boot.
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
tabularium-docs Skipped Skipped Aug 18, 2026 7:00pm
tabularium-website Skipped Skipped Aug 18, 2026 7:00pm

Request Review

@vercel
vercel Bot temporarily deployed to Preview – tabularium-website August 18, 2026 18:42 Inactive
@vercel
vercel Bot temporarily deployed to Preview – tabularium-docs August 18, 2026 18:42 Inactive
The releases table has had a min_runtime_version column since the
initial migration, the manifest schema validates the field, and the
submit preview echoes it back — but no ingest path ever wrote it. Only
the seed script did, which is why it looked wired up. Every release
ingested through a webhook, submit or publish stored NULL.

The API then served that NULL faithfully: the postgresql plugin
declares min_runtime_version 0.20.0 in its .tabularium for
v1.0.0-beta.7, and /api/plugins/postgresql/releases/1.0.0-beta.7
answers with min_runtime_version: null. Clients had nothing to gate on,
so a plugin could install on a runtime too old for it.

Carried through the same opts guard as manifestSha256/manifestRaw, so an
asset-only re-ingest (rehash, asset backfill) leaves a stored value
intact instead of nulling it.
@vercel
vercel Bot temporarily deployed to Preview – tabularium-website August 18, 2026 18:45 Inactive
@vercel
vercel Bot temporarily deployed to Preview – tabularium-docs August 18, 2026 18:45 Inactive
The README backfill threw when a plugin's owner had no stored or no
longer valid OAuth token, which is exactly the case for the older
plugins whose history the backfill exists to repair.

A README in a public repo needs no credentials, so a missing or expired
token now downgrades to an unauthenticated read from the forge's
raw-content host instead of failing the run. Private repos answer 404
there and are skipped, same as any other release whose README is gone.
@vercel
vercel Bot temporarily deployed to Preview – tabularium-website August 18, 2026 19:00 Inactive
@vercel
vercel Bot temporarily deployed to Preview – tabularium-docs August 18, 2026 19:00 Inactive
@NewtTheWolf
NewtTheWolf merged commit 96b34dd into main Aug 18, 2026
9 checks passed
@NewtTheWolf
NewtTheWolf deleted the feat/per-release-readme branch August 18, 2026 19:01
Sign up for free to 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