Skip to content

fix(pages): publish to scanner.registry.coder.com#13

Merged
DevelopmentCats merged 4 commits into
mainfrom
cat/custom-domain
Jun 25, 2026
Merged

fix(pages): publish to scanner.registry.coder.com#13
DevelopmentCats merged 4 commits into
mainfrom
cat/custom-domain

Conversation

@DevelopmentCats

Copy link
Copy Markdown
Collaborator

What this does

Makes scanner.registry.coder.com actually work as the Pages host. Two real things were keeping the site dark on the custom domain:

  1. No CNAME was being deployed. The scan workflow ships Pages via actions/upload-pages-artifact + actions/deploy-pages. With that flow, the custom-domain binding is restored from the CNAME file at the root of the uploaded artifact on every deploy. There was no CNAME anywhere in the repo, so every 6-hour scheduled scan silently wiped the Pages custom-domain setting.
  2. The SPA's asset base was hardcoded to /coder-skill-scanner/. vite.config.ts's resolveProductionBase() derived the prefix from $GITHUB_REPOSITORY, which is correct for coder.github.io/coder-skill-scanner/ but wrong for a custom domain where the site lives at the apex. Every asset URL, the React Router basename, and the 404.html SPA-fallback all 404'd on the new host.

Fix: make site/public/CNAME the single source of truth for whether a custom domain is configured. Everything that needs to know reads from it.

Changes

  • site/public/CNAME (new) — scanner.registry.coder.com. Vite copies site/public/ verbatim into site/dist/, so the CNAME lands at the root of the Pages artifact built by the scan workflow with zero workflow changes. actions/deploy-pages reads it from the artifact root and configures the custom-domain binding on every deploy.
  • site/vite.config.tsresolveProductionBase() now checks public/CNAME first and returns / when present. The change flows through automatically to:
    • asset URLs emitted by Vite into dist/index.html,
    • index.html's %BASE_URL% substitutions (favicon, noscript link),
    • the rewrite-public-base-url Vite plugin's 404.html SPA-fallback rewrite,
    • React Router's basename, which already uses import.meta.env.BASE_URL.
  • scanner/_carry_history.py — the _default_base() fallback also reads CNAME so the Restore prior history step mirrors snapshots from the canonical custom-domain origin instead of bouncing through a github.io redirect. The vars.PAGES_URL repo-var override still wins.
  • README.md — every reader-facing URL now points at scanner.registry.coder.com. The Forking section documents the CNAME-as-source-of-truth contract so a fork only needs to edit (or delete) site/public/CNAME to control where its Pages site is served, no workflow edits required.

Why no workflow change in this PR

The site works on scanner.registry.coder.com end-to-end with just the changes above. There's one nice-to-have in .github/workflows/scan.yaml that I couldn't push because the GitHub App backing my tooling doesn't have the workflows permission — GitHub returns 404 on workflow-file writes for those tokens, by design.

The leftover hardcoded github.io URLs are in two places:

  • publish-release → Create timestamped release step (release notes string),
  • publish-pages → Build pages tree step (--public-base-url passed to scanner build-api-v1, which embeds it in links.status_badge_svg and friends inside the v1 per-skill detail JSON).

Both keep working because GitHub Pages issues a 301 from the github.io project-page URL to the custom domain once CNAME is live. But the URLs embedded in the v1 API responses should be canonical so shields.io endpoint mode and downstream cached badge consumers don't follow a redirect.

Please apply the following patch yourself (it's the same pattern in both jobs, factored into a Resolve canonical Pages URL step in publish-release and inlined in publish-pages):

Workflow patch (apply locally and commit to this branch)

Replace the publish-release → Create timestamped release step with:

      - name: Resolve canonical Pages URL
        id: pages_url
        run: |
          set -euo pipefail
          if [[ -f site/public/CNAME ]]; then
            cname="$(head -n1 site/public/CNAME | tr -d '[:space:]')"
            url="https://${cname}"
          else
            repo_short="${GITHUB_REPOSITORY#*/}"
            url="https://${GITHUB_REPOSITORY_OWNER}.github.io/${repo_short}"
          fi
          echo "url=${url}" >> "$GITHUB_OUTPUT"
      - name: Create timestamped release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG: ${{ steps.stage.outputs.tag }}
          PUBLIC_BASE_URL: ${{ steps.pages_url.outputs.url }}
        run: |
          set -euo pipefail
          gh release create "${TAG}" \
            --title "Scan ${TAG}" \
            --notes "Automated scan run. See ${PUBLIC_BASE_URL}/latest.json for the public report." \
            release-assets/*

And in publish-pages → Build pages tree, replace:

          repo_short="${GITHUB_REPOSITORY#*/}"
          public_base_url="https://${GITHUB_REPOSITORY_OWNER}.github.io/${repo_short}"

With:

          if [[ -f site/public/CNAME ]]; then
            cname="$(head -n1 site/public/CNAME | tr -d '[:space:]')"
            public_base_url="https://${cname}"
          else
            repo_short="${GITHUB_REPOSITORY#*/}"
            public_base_url="https://${GITHUB_REPOSITORY_OWNER}.github.io/${repo_short}"
          fi

After merge

Dispatch the scan workflow once (or wait for the next 6h cron). The Build SPA step will emit a /-based bundle, the artifact will contain CNAME at its root, and actions/deploy-pages will rebind scanner.registry.coder.com. Confirm:

  • https://scanner.registry.coder.com/ renders the SPA.
  • Settings → Pages shows scanner.registry.coder.com and "DNS check successful".
  • The legacy https://coder.github.io/coder-skill-scanner/ 301s to the new host.

If the Pages settings were previously cleared by a wiped deploy, re-entering the domain there is not needed — the CNAME-in-artifact restores it. DNS is the user's responsibility (you said it's already pointed at coder.github.io, which is correct).

This PR was prepared with help from Coder Agents.

With GitHub Pages deployed via actions/deploy-pages, the custom-domain
setting is restored from the CNAME file at the root of the uploaded
artifact on every deploy. Without it, each scheduled scan silently
wiped the Pages custom-domain binding.

Vite copies the entire site/public/ tree verbatim into site/dist/, so
putting CNAME here lands it at the root of the Pages artifact built by
the scan workflow.
The production build hardcoded `/coder-skill-scanner/` as the asset
base prefix (derived from $GITHUB_REPOSITORY). On a custom domain the
site is served at the apex, so every asset URL 404s.

resolveProductionBase() now checks public/CNAME and returns `/` when
present. Forks without a CNAME keep the project-page behaviour.

The change flows through automatically to:
  + asset URLs emitted by Vite
  + index.html %BASE_URL% substitutions (favicon, noscript link)
  + the rewrite-public-base-url plugin's 404.html SPA-fallback target
  + React Router's `basename`, which already uses import.meta.env.BASE_URL
Mirror the new vite.config.ts behaviour: when site/public/CNAME exists,
the canonical Pages URL is https://<CNAME>, not the github.io project
page. Carrying history from the canonical origin avoids a redirect hop
and ensures the manifest URLs we mirror match the host the SPA fetches
them from at runtime.

PAGES_URL repo var override still takes precedence; only the fallback
is newly CNAME-aware.
Also document the CNAME-as-source-of-truth convention so a fork only
needs to edit (or delete) site/public/CNAME to control where its Pages
site is served, with no workflow edits required.
Copilot AI review requested due to automatic review settings June 25, 2026 17:12
@DevelopmentCats DevelopmentCats merged commit 82dc4fb into main Jun 25, 2026
6 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the GitHub Pages deployment so the scanner SPA and related URLs work correctly when served from the custom domain scanner.registry.coder.com (apex) instead of the default *.github.io/<repo>/ project path.

Changes:

  • Add a Pages CNAME file under site/public/ so it is included in the built Pages artifact.
  • Update the Vite production base resolution to use / when a public/CNAME is present.
  • Update history-carrying logic and public documentation to prefer the custom-domain host.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
site/vite.config.ts Adjusts production base-path resolution to return / when a custom domain (CNAME) is configured.
site/public/CNAME Adds the custom domain so Pages can re-bind the domain from the deployed artifact.
scanner/_carry_history.py Changes the default history-fetch origin to prefer the custom domain when configured.
README.md Updates reader-facing URLs and documents the intended custom-domain configuration approach.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scanner/_carry_history.py
Comment on lines +25 to +38
def _default_base() -> str:
"""Return the canonical Pages URL for this repo.

Prefer ``site/public/CNAME`` when present so a custom-domain deploy
fetches prior history from the same origin it will publish to. Fall
back to the github.io project-page URL otherwise.
"""
cname = Path("site/public/CNAME")
if cname.is_file():
for line in cname.read_text(encoding="utf-8").splitlines():
host = line.strip()
if host:
return f"https://{host}"
return DEFAULT_BASE_FALLBACK
Comment thread README.md
Comment on lines +84 to +91
For a fork, swap the host: `https://<your-host>/api/v1/...`. The scanner
picks the public base URL at publish time in this order:

1. `site/public/CNAME` (the custom Pages domain, if set),
2. otherwise `$GITHUB_REPOSITORY` -> `https://<owner>.github.io/<repo>`.

So a fork that just sets a CNAME gets the right URLs everywhere without
touching workflow code.
Comment thread site/vite.config.ts
Comment on lines +40 to +42
if (fs.existsSync(path.resolve("public", "CNAME"))) {
return "/";
}
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.

2 participants