From bc62d652ca2770c519571bd0b6d1938951beb14a Mon Sep 17 00:00:00 2001 From: Chuck McAndrew <6248903+dcmcand@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:04:29 +0200 Subject: [PATCH 1/8] docs: spec for migrating the guide site from Hugo to Astro+Starlight --- ...01-template-site-astro-starlight-design.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-01-template-site-astro-starlight-design.md diff --git a/docs/superpowers/specs/2026-07-01-template-site-astro-starlight-design.md b/docs/superpowers/specs/2026-07-01-template-site-astro-starlight-design.md new file mode 100644 index 0000000..4ae946e --- /dev/null +++ b/docs/superpowers/specs/2026-07-01-template-site-astro-starlight-design.md @@ -0,0 +1,137 @@ +# Design: Migrate the "Building a Software Pack" docs site from Hugo to Astro + Starlight + +- **Date:** 2026-07-01 +- **Repos touched:** `nebari-dev/nebari-software-pack-template` (primary) and `nebari-dev/starlight` (`@nebari/starlight`, a small additive change) +- **Branch:** `feat/astro-starlight-docs` (off `main`) +- **Sub-project:** C ("per-pack docs template") of the wider Hugo -> Astro+Starlight replatform. Depends on A (`@nebari/starlight`, live on npm) and follows B (the dashboard, already live on Astro+Starlight). + +## Goal + +Replatform the guide served at `packs.nebari.dev/building-a-software-pack/` from Hugo (`nebari-hugo-theme`) to Astro + Starlight themed with `@nebari/starlight`, so that: + +1. Maintainers of this guide write only Markdown; presentation comes from the shared theme. +2. The guide's look matches the portal and every other pack (unified presentation). +3. PR-preview deploys are preserved. +4. Built-in Starlight (Pagefind) search works over the guide, ready to join the portal-wide multisite index later. + +This guide also serves as the reference implementation other pack maintainers copy, so its structure should be clean and copyable. + +## Current state (Hugo) + +- Site root: `docs/site/` (Hugo). Config `docs/site/hugo.toml`: `baseURL = https://packs.nebari.dev/building-a-software-pack/`, `nebari-hugo-theme` as a Hugo Module (pinned `v0.2.0` in `go.mod`), `params.logoLink = https://packs.nebari.dev/` (logo returns users to the portal), `params.editBase`, `params.search = true`, two manually configured sidebar groups. +- Content: `docs/site/content/*.md`, 7 files with `+++` TOML frontmatter: + - `_index.md` (home / "Building a Software Pack" intro + links) + - `what-is-a-software-pack.md` (weight 10) + - `concepts.md` (**orphan** - not in the Hugo sidebar today) + - `build-your-own.md` (weight 20) + - `nebariapp-crd-reference.md` + - `auth-flow.md` + - `release-readiness.md` + - Only two files carry `weight`; the sidebar order is driven by the **manual** `[[params.sidebar]]` config, not by weight. +- **No Hugo shortcodes.** The `{{ ... }}` occurrences in `concepts.md` and `nebariapp-crd-reference.md` are **Helm template syntax inside fenced code blocks** (literal example content), not shortcodes. +- **Internal links are all root-absolute** (13 of them), e.g. `](/auth-flow/)`, `](/nebariapp-crd-reference/#2-kubernetes-secret)`. Hugo's theme render-link hook resolves these against `baseURL`; Astro will not (see Base handling). +- CI: `.github/workflows/docs.yml` builds with Hugo and deploys `docs/site/public` to Cloudflare Pages (project `nebari-software-pack-template`, route slug `building-a-software-pack`), computing a per-environment `baseURL` (main -> portal subpath; PR -> `..pages.dev/` root) and commenting the preview URL on PRs. Skips deploy for fork PRs (no secrets). + +## Approach + +Clean re-platform in place: replace the Hugo project rooted at `docs/site/` with an Astro + Starlight project rooted at the same `docs/site/`, so the workflow path filter (`docs/site/**`), the Cloudflare project, and the repo layout are all preserved. Bun is the package manager and build runner (matches A and B). No Hugo, no Go module. + +### Scope A - `@nebari/starlight`: add an optional `logoHref` + +Today `SiteTitle.astro` links the logo to `import.meta.env.BASE_URL` (the site's own home). The Hugo site links it to the portal root, and every federated pack wants the same "logo returns to the portal" affordance. Add an optional `logoHref` string to the plugin: + +```js +nebari({ logoHref: 'https://packs.nebari.dev/' }) +``` + +- Default: `import.meta.env.BASE_URL` (unchanged behavior for current consumers - the dashboard keeps working untouched). +- Threading the value to the component: use a Vite **virtual module** (the same pattern Starlight uses for `virtual:starlight/user-config`); `SiteTitle.astro` imports the resolved `logoHref`, falling back to `BASE_URL`. The exact `addVitePlugin`/virtual-module API is verified against the Astro/Starlight plugin docs at implementation time. +- This is a backward-compatible, opt-in addition -> EffVer **MICRO** bump (`v0.1.6`). Published via the existing Trusted-Publishing release workflow. + +The template then pins `@nebari/starlight: ^0.1.6` and passes `logoHref: 'https://packs.nebari.dev/'`. + +### Scope C - the template migration + +**Repo layout.** Astro project at `docs/site/`: +- `docs/site/package.json` (Bun; deps `astro`, `@astrojs/starlight`, `@nebari/starlight ^0.1.6`) +- `docs/site/astro.config.mjs` +- `docs/site/src/content/docs/*.md` (migrated content) +- `docs/site/src/content.config.ts` (Starlight docs collection) +- Build output `docs/site/dist/` +- Remove: `hugo.toml`, `go.mod`, `go.sum`, `content/`. + +**Content migration.** +- Move each `content/*.md` to `src/content/docs/`; rename `_index.md` -> `index.md` (the Starlight home at the base root, a normal `doc` page with the sidebar, not a splash). +- Convert `+++` TOML frontmatter to `---` YAML: `title = "X"` -> `title: X`. Drop `weight` (sidebar order is explicit config). +- **Keep files as `.md`, never `.mdx`.** Astro's Markdown pipeline treats fenced code as literal, so the Helm `{{ ... }}` examples are safe; MDX would parse `{` as a JSX expression and break the build. +- Body prose, headings, and code fences are otherwise copied verbatim. + +**Sidebar** (explicit config in `astro.config.mjs`, mirroring the Hugo groups, with `concepts` added): +- **Getting Started:** Introduction (`/`) -> What is a software pack -> **Concepts** (new) -> Build your own +- **Reference:** NebariApp CRD -> Authentication Flow -> Release Readiness + +Starlight prepends `base` to sidebar `link` values automatically. + +**Base handling (the crux of "base-safe links").** +- Astro's `base` is **static**: `/building-a-software-pack/` in every environment (local dev, PR preview, production). This mirrors production paths exactly, so links behave identically everywhere and the workflow needs no baseURL computation. Cost: a PR preview's site root (`.pages.dev/`) 404s; the PR comment must deep-link to `.pages.dev/building-a-software-pack/`. +- Confirmed against official docs: **Astro does not auto-prepend `base` to root-absolute Markdown links.** The Astro config reference documents `base` affecting only asset imports and `import.meta.env.BASE_URL`; the Markdown guide says nothing about link rewriting; Starlight's authoring guide only shows a bare `/getting-started/` example (valid only at root). So the 13 existing `/foo/` body links need an explicit mechanism. +- **Mechanism: a small rehype plugin** (`markdown.rehypePlugins` in `astro.config.mjs`) that prepends `base` to internal root-absolute `` / `` values (leaving external `//`, `http(s):`, `mailto:`, and in-page `#` links alone; collapsing duplicate slashes). The plugin is a pure factory taking the `base` constant. This preserves "maintainers write natural `/foo/` Markdown" and is testable in isolation. Body-link resolution is verified end-to-end by the build-time link check (journey 3). + - Alternative considered and rejected: rewriting all body links to relative (`../auth-flow/`). Base-agnostic, but fragile under hierarchy changes, awkward with anchors, and pushes a rule onto maintainers. The rehype plugin keeps content clean. + +**Theme + branding.** `starlight({ plugins: [nebari({ logoHref: 'https://packs.nebari.dev/' })], ... })`. The theme supplies colors, fonts, logo (light/dark), Head, and footer. No local CSS needed unless a specific gap appears. + +**Search.** Starlight's default Pagefind. A `dist/building-a-software-pack/pagefind/` bundle is produced by the build. Registering this bundle in the dashboard's multisite `mergeIndex` is **out of scope** here (tracked as a follow-up in the dashboard repo). + +**Edit links.** `editLink.baseUrl = https://github.com/nebari-dev/nebari-software-pack-template/edit/main/docs/site/src/content/docs/` (updated for the new content path). Starlight appends the per-page file path. + +**Metadata.** Site `title: "Building a Software Pack"`, `description` carried over from `hugo.toml`. + +**CI workflow (`docs.yml`) rewrite.** Keep the trigger (`push` to main + `pull_request`, path filter `docs/site/**`, `workflow_dispatch`), the concurrency group, the `pull-requests: write` permission, the Cloudflare project/env vars, the fork-PR deploy skip, and the PR-comment step. Change: +- Replace `setup-go` + `actions-hugo` with `oven-sh/setup-bun` (+ `setup-node` `lts/*` if needed by tooling). +- Build: `cd docs/site && bun install --frozen-lockfile && bun run build` (static `base`, no `--baseURL`). Drop the "Compute baseURL" step entirely; keep only a `branch` value for the Cloudflare deploy (main vs head ref). +- Deploy: `pages deploy docs/site/dist` (was `docs/site/public`). +- PR comment: deep-link to `/building-a-software-pack/` (append the subpath to the deployment alias URL). +- Bump pinned actions off Node-20-deprecated majors where a newer major exists (e.g. `actions/checkout@v5`); keep `wrangler-action` and the comment action at their current latest majors, verified at implementation time. + +## Journeys + +A **reader** browses the guide at `packs.nebari.dev/building-a-software-pack/`. A **maintainer** edits the docs and opens a PR. (Accepted 2026-07-01; item 7 preview URL reflects the static-base decision.) + +| # | Item | Proof | Check method | Evidence | +|---|------|-------|--------------|----------| +| 1 | All 7 pages render under the `/building-a-software-pack/` base | Built site emits `dist/building-a-software-pack/index.html` plus a page for each of the 7 content files (`what-is-a-software-pack`, `concepts`, `build-your-own`, `auth-flow`, `nebariapp-crd-reference`, `release-readiness`, and the home); each returns HTTP 200 from local preview and contains its expected `

` | automated: build + a test that globs `dist/**/*.html`, asserts the 7 expected paths exist and each contains its title | *(empty)* | +| 2 | Sidebar shows the 2 groups with `concepts` in the right slot | Rendered sidebar HTML contains both group labels; "Getting Started" lists `What is a software pack` then `Concepts` (new) then `Build your own`; every sidebar `href` resolves to a built page | automated: test parses a built page's sidebar `