OSS catalog — 47 projects from one source preview

The decision behind the case

The bet

The catalog has to be a projection of machine data rather than a hand-kept table: otherwise cards and GitHub drift apart and nobody notices the divergence.

What counted as failure

The build is accepted only if the validator passes on every page, a repeated render leaves the file unchanged, and a field absent from the source never reaches the frontmatter.

After launch

The separation of machine and editorial data held: `exclude:` and explicit languages survive a re-sync. `website` and `readme_image` stayed unused — fetched but never rendered; that is recorded as debt, not as a feature.

Situation
The project catalog was maintained by hand: cards drifted from GitHub, and metadata and links had to be updated in two places
Task
I needed a catalog where the data comes from one machine-owned source, pages are built from it, and a repeated sync does not overwrite authored edits
Action
Astro 7 with content collections, a Python sync from the GitHub API into TOML, a frontmatter generator with an ownership marker, a notes validator and 26 tests, and CI ordered from the cheap gate to the build
Result
47 project pages and 15 metadata fields are built from one TOML file; a repeated render is idempotent, and 26 tests cover all three record-matching branches
Stack
AstroTypeScriptBunPythonGitHub APIPagefinduv
Source View on GitHub → Updated: Nov 1, 2026
On this page
  1. Situation
  2. Task
  3. Actions
  4. Result
  5. Limitations
  6. Documentation

OSS catalog — 47 projects from one source

Situation

An open-source project catalog assembled by hand ages faster than it can be updated. Stars, releases and languages change on GitHub while the site keeps numbers from a month ago. Worse, the same card exists in two places — a data table and the markup — and disagrees with itself.

Task

I needed a catalog where the data comes from one machine-owned source, pages are built from it, and a repeated sync does not overwrite authored edits.

Actions

Data source. A Python script calls the GitHub API and writes one TOML file: 47 records with 15 fields each — name, description, stars, forks, releases, licence, language, contributors, last update and others. The API response cache lives outside the repository.

Ownership split. The generator rewrites only the frontmatter block under an «updated automatically» marker; the rest of the page stays authored. An exclude: field prunes noisy machine values, and explicitly set languages survive a repeated sync.

Build. Astro 7 with content collections: a glob loader reads project and note pages, a Zod schema checks types, and the project page renders metadata and links. The site is static with no client framework.

Validation. A separate validator checks notes and project pages: required fields, key parity with the source, no empty blocks. In strict mode a warning about a record without metadata becomes an error.

Tests. 26 tests cover the three record-matching branches — owner/name, short name, directory name — render idempotency, preservation of editorial fields, and the real page tree.

CI. Steps are ordered by cost: the validator and Python tests run before Astro dependencies are installed, then astro check, build, Pagefind and deploy. The cheap gate fails before the expensive one runs.

Result

  • 47 project pages and 47 repository records from one TOML file.
  • 15 metadata fields per record, including stars, forks, releases, licence and last update.
  • 26 tests, including byte-level idempotency of a repeated render.
  • 8 topic pages, 76 tag pages and 5 language pages generated from the same source.
  • The build never goes to the network: the site reads only committed frontmatter, so the deploy is reproducible.
  • The validator passes on every page, and astro check catches schema drift before the build.

Limitations

Validation is mechanical: description drift, placeholders and alt text remain a human or separate editorial pass.

The page-count test asserts a floor, not the exact number, so drift away from 47 stays green.

website, readme_image, latest_release and first_commit are fetched or declared in the schema but never rendered: the field exists, the consumer does not.

Pagefind is installed and runs in the build, but there is no search UI — search remains an unused dependency.

The catalog is a showcase of an engineering pipeline, not a product with metrics: there is no user analytics and no A/B testing here.

Documentation

  • README.md — the data source, quick start and the boundaries of the claim.
  • scripts/update-github-repos.py — the GitHub API sync into TOML.
  • scripts/update-project-frontmatter.py — the metadata block generator with the ownership marker.
  • Checks: bun run validate → uv run tests/test_scripts.py → bun run check → bun run build.

Need this reasoning applied to your data?

Tell me the task — I'll come back with a plan: what data it needs, what we test, and how long it takes. I reply within a day.

Discuss a task →