feat(releases): capture the README per release and serve it per version - #10
Merged
Conversation
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.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
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.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, andmanifestPatchthen 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:
readmeslocale map, then thereadmepath, then the conventional root filenames — moved into one shared helper instead of living only in the git-ref path.manifestPatchomitsreadmewhen a pass resolved none, instead of writing null. This mirrors themanifestSha256/manifestRawguardpersistReleasealready 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/readmerenders and caches it, mirroring the plugin-level README handling (locale map,pickReadme, TTL).pickReadmemoved to$lib/readmeso both callers share one reader.captured: falserather than falling back to the current README — a silent fallback would misrepresent the old version.Backfill
POST /api/admin/plugins/:id/backfill-readmeswalks 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 test474 pass / 0 fail (6 new, covering tag-pinned capture, the locale map, and that a resolved-nothing pass no longer blanks the stored README),tscclean,svelte-check0 errors.