From 6da4a67160d42c36b8240c1de12a389ae03d7e69 Mon Sep 17 00:00:00 2001 From: Jon B Date: Wed, 15 Apr 2026 01:02:37 -0500 Subject: [PATCH 1/2] docs: canonical URL is https://jonbogaty.com/pkgs/ The operator's apex jonbogaty.com already CNAMEs to jbcom.github.io repo-wide, so GitHub Pages transparently serves /pkgs/ on the apex with no reverse proxy needed. Updates astro.config.mjs `site` field to feed correct absolute URLs into sitemap + OG tags, and aligns README / CLAUDE.md / AGENTS.md / docs/ to the real canonical URL. jbcom.github.io/pkgs/ 301-redirects to the canonical URL. --- CLAUDE.md | 4 ++-- README.md | 4 ++-- astro.config.mjs | 5 +++-- docs/ARCHITECTURE.md | 20 ++++++++++++-------- docs/DEPLOYMENT.md | 19 +++++++++++-------- docs/STATE.md | 2 -- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f73fd54..b3b2fc1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,7 @@ domain: technical Unified package repository for every `jbcom/*` project. Ships Homebrew, Scoop, and Chocolatey packages from one git tree plus an auto-generated -Astro static site at . +Astro static site at . ## Critical rules @@ -71,7 +71,7 @@ Upstream projects write manifests here on every tagged release: commit manifests here. The Astro site rebuilds automatically on every push to `main`, so -new packages appear on within minutes +new packages appear on within minutes of the upstream release landing here. ## What jbcom/pkgs is NOT diff --git a/README.md b/README.md index 8567a00..60b17e3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Unified package repository for every `jbcom/*` project — Homebrew, Scoop, and Chocolatey from one git tree. Public package index at -. +. ## Install @@ -142,7 +142,7 @@ PY `deploy.yml` runs on every push to `main` using `withastro/action@v6`. The built site is deployed to GitHub Pages at -. +. ## License diff --git a/astro.config.mjs b/astro.config.mjs index acb2513..9038378 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -8,10 +8,11 @@ import { ViteToml } from "vite-plugin-toml"; import tailwindcss from "@tailwindcss/vite"; // jbcom/pkgs — package index site -// Served at https://jbcom.github.io/pkgs per Astro's GitHub Pages guide. +// Served at https://jonbogaty.com/pkgs/ via GitHub Pages custom-domain +// routing. jbcom.github.io/pkgs/ 301-redirects here automatically. export default defineConfig({ - site: "https://jbcom.github.io", + site: "https://jonbogaty.com", base: "/pkgs", integrations: [ vue(), diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index fabbcd2..be747d6 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -82,17 +82,21 @@ tap git tree directly. - **Deployed** via `withastro/action@v6` (auto-detects pnpm) to GitHub Pages with base path `/pkgs` -## Why not subpath under custom domain (jonbogaty.com/pkgs)? +## Site serves at jonbogaty.com/pkgs/ -Astro's official GitHub Pages guide only supports either: +The operator's apex domain `jonbogaty.com` already CNAMEs to +`jbcom.github.io`, so GitHub Pages transparently serves `/pkgs/` at +`https://jonbogaty.com/pkgs/`. `jbcom.github.io/pkgs/` 301-redirects +to the canonical URL. -- Project page (`jbcom.github.io/pkgs`) with `base: '/pkgs'` -- Custom domain with `site: 'https://custom.tld'` and **no base** +Astro's `astro.config.mjs` uses `site: 'https://jonbogaty.com'` + +`base: '/pkgs'` — this feeds correct absolute URLs into sitemap and OG +metadata while internal routing stays subpath-relative. -Serving `jonbogaty.com/pkgs` requires an external reverse proxy -(Cloudflare Worker, Netlify rewrite, Caddy) that maps that path to -`jbcom.github.io/pkgs/`. That's a future-operator decision; the repo -itself is deployed at `jbcom.github.io/pkgs` today. +No CNAME file is needed in `public/` because the apex-domain CNAME is +configured repo-wide (pages settings), not per-repo via file. Adding a +`public/CNAME` would *break* the `/pkgs` base path by treating the +repo as apex-mode. ## Dependencies diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index c0ab317..7f3a584 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -11,7 +11,7 @@ domain: ops | Env | URL | Trigger | |-----|-----|---------| -| Production | | Push to `main` | +| Production | | Push to `main` | | Preview | Local (`pnpm dev`) | `localhost:4321/pkgs/` | There is no staging environment. Previews happen locally. @@ -53,20 +53,23 @@ Or: - GitHub Pages build status: - Workflow runs: -- Site health: manual check at +- Site health: manual check at No uptime SLA. If the site is down for more than an hour during a release window, escalate via the upstream project's issue tracker. ## DNS / custom domain -Currently served on the GitHub-issued subdomain -(`jbcom.github.io/pkgs`). To serve `jonbogaty.com/pkgs` in the future: +The operator's apex `jonbogaty.com` already CNAMEs to +`jbcom.github.io`. GitHub Pages transparently serves every `jbcom/*` +repo's pages subpath at the matching URL on the apex, so this repo +is live at `https://jonbogaty.com/pkgs/` with no additional config. -1. Configure Cloudflare (or equivalent CDN) to proxy - `jonbogaty.com/pkgs/*` → `jbcom.github.io/pkgs/*` -2. Do NOT add a `public/CNAME` file — per Astro docs, that converts - the site to apex-domain mode and breaks the `/pkgs` base path +`jbcom.github.io/pkgs/` 301-redirects to the apex, so operators +following the GitHub URL end up in the right place. + +Do NOT add a `public/CNAME` file. That converts this repo to +apex-domain mode and would break the `/pkgs` base path. ## Broken-build triage diff --git a/docs/STATE.md b/docs/STATE.md index 6d4e86b..1de951a 100644 --- a/docs/STATE.md +++ b/docs/STATE.md @@ -29,8 +29,6 @@ publish from `jbcom/radioactive-ralph`. the seeded manifests - A non-Go publishing workflow template for `jbcom/paranoid-passwd` and other CMake/C projects (no GoReleaser equivalent) -- Reverse-proxy setup for `jonbogaty.com/pkgs` → `jbcom.github.io/pkgs` - (optional; current deploy works at the GitHub-issued subdomain) ## Active owners From 82947c93e2882c64fa47b63a0847b2c58f0cfe31 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 16 Apr 2026 18:37:50 -0500 Subject: [PATCH 2/2] fix(ci): accept multi-arch Scoop manifests in validator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The validator required top-level `url` + `hash` keys, which is the single-arch/legacy shape. GoReleaser v2 (and modern Scoop itself) emit the multi-arch shape with `url` and `hash` nested under `architecture.{64bit,32bit,arm64}`. This blocked PR #10 (radioactive-ralph v0.8.1) — the manifest was valid Scoop but the validator rejected it. New logic: top-level url+hash pass, OR an `architecture` block with at least one known arch sub-block containing both url and hash. Flat-shape manifests still pass unchanged. Includes coverage for obviously-broken manifests (no top-level, no architecture block; architecture block present but missing url/hash). --- .github/workflows/validate-packages.yml | 54 +++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/.github/workflows/validate-packages.yml b/.github/workflows/validate-packages.yml index ea4ad0d..fd4dd10 100644 --- a/.github/workflows/validate-packages.yml +++ b/.github/workflows/validate-packages.yml @@ -37,18 +37,64 @@ jobs: import glob import json - required = {"version", "description", "homepage", "license", "url", "hash"} + # Top-level keys every manifest must carry. + top_required = {"version", "description", "homepage", "license"} + # url + hash may live either at the root OR nested under + # architecture.{32bit,64bit,arm64}.* for multi-arch manifests + # (GoReleaser v2, modern Scoop). Either shape is valid. + arch_required = {"url", "hash"} + arch_keys = {"64bit", "32bit", "arm64"} + files = glob.glob("bucket/*.json") if not files: print("No Scoop manifests found") raise SystemExit(0) + errors = [] for path in files: with open(path, "r", encoding="utf-8") as f: data = json.load(f) - missing = required - set(data) - if missing: - raise SystemExit(f"{path}: missing required keys: {', '.join(sorted(missing))}") + + missing_top = top_required - set(data) + if missing_top: + errors.append(f"{path}: missing top-level keys: {', '.join(sorted(missing_top))}") + continue + + root_has_url_hash = arch_required.issubset(data.keys()) + arch = data.get("architecture") + + if root_has_url_hash: + # Single-arch manifest — fine. + continue + + if not isinstance(arch, dict) or not arch: + errors.append( + f"{path}: must have top-level url+hash OR an architecture.{{64bit|32bit|arm64}} block" + ) + continue + + present_arches = [a for a in arch_keys if a in arch] + if not present_arches: + errors.append( + f"{path}: architecture block has no known arch key ({', '.join(sorted(arch_keys))})" + ) + continue + + for a in present_arches: + block = arch.get(a) + if not isinstance(block, dict): + errors.append(f"{path}: architecture.{a} is not an object") + continue + missing = arch_required - set(block) + if missing: + errors.append( + f"{path}: architecture.{a} missing: {', '.join(sorted(missing))}" + ) + + if errors: + for e in errors: + print(e) + raise SystemExit(1) print(f"Validated {len(files)} Scoop manifest(s)") PY