ci: dual-publish docs to github pages and vercel - #194
Conversation
The tenant table and troubleshooting pages hardcoded https://docs.malbeclabs.com for 26 cross-links, while the other five links in the same tenant table were already relative. Relative links resolve on whichever hostname served the page and are validated by mkdocs at build time. Angle-bracket destinations are used because the target filenames contain spaces; percent-encoding is not reliably decoded before mkdocs' file lookup.
Canonical tags, sitemap.xml, robots.txt, and the .well-known agent discovery files now point at docs.doublezero.xyz. A single build carries a single site_url, so pages served from docs.malbeclabs.com will carry the new canonical and stop competing for indexing. docs/CNAME is deliberately unchanged: GitHub Pages keeps serving docs.malbeclabs.com.
Converts the same tar that GitHub Pages receives into a Build Output API v3 directory, so both origins serve byte-identical content. Extracts with tar rather than cp because the .well-known dot-directory is silently skipped by shell globs and some copy tools. .gitignore additions for .vercel and .env* come from `vercel link`.
site/ was already listed in .gitignore but remained tracked from before that rule existed, so any local `mkdocs build` overwrote 190+ tracked files and dirtied the repo. The directory is generated on every build and deployed from CI, so it does not belong in version control. Files remain on disk; only tracking is removed.
Split the single build-and-deploy job so one mkdocs build feeds two deploy targets. Both consume the same tar artifact, which is what makes byte-identical output across the two origins provable rather than aspirational, and sidesteps upload-artifact dropping hidden files. GitHub Pages deploys on push to main exactly as before. Vercel deploys production on main and a preview on same-repo pull requests. Fork PRs skip the Vercel job because they cannot receive secrets; they still get the build as validation. pull_request_target is deliberately not used. Adds --strict so broken internal links fail CI, verified adoptable against a warning-free baseline. Drops contents:write and the git credentials step: Pages is configured with build_type=workflow, so deploy-pages uploads an artifact and never pushes a branch.
Ready to merge: docs.doublezero.xyz is liveThe sequencing hazard that kept this in draft is cleared. Verified on the live new origin:
Verified the legacy origin is untouched:
Note on the auto-translate commit
Its CI runs initially landed as Remaining after mergeDigest parity between the two origins currently differs, which is expected while |
Jared-dz
left a comment
There was a problem hiding this comment.
This is certainly a PR.
Serves the docs at
docs.doublezero.xyz(Vercel, new canonical) whiledocs.malbeclabs.comkeeps running on GitHub Pages unchanged.Design and plan: PR #193 (
.specs/2026-08-10-docs-dual-publish-*.md).Why a second origin
GitHub Pages allows one custom domain and matches it against the incoming
Hostheader, so a second hostname can't just be another DNS record pointing atmalbeclabs.github.io— GitHub would 404. Adding Vercel as a real second origin is the only option needing no cutover, so the existing hostname has zero downtime.Summary
mkdocs buildfeeds both deploys via the same tar artifact, so byte-identical output across origins is provable rather than aspirational.site_urlmoves todocs.doublezero.xyz, consolidating canonical tags,sitemap.xml,robots.txt, and.well-knownon the future hostname even for pages served from the legacy one.site_namechanges toDoubleZero. Note this is a single shared build, so the header and<title>change on both origins.docs.malbeclabs.comcross-links converted to relative mkdocs links across 17 files and 8 locales. Five links in the same tenant table were already relative, so the absolute ones were the inconsistency.--strictadded, so broken internal links now fail CI.pull_request_targetdeliberately not used — it would expose the Vercel token to untrusted PR code.docs/CNAMEunchanged. Pages keepsdocs.malbeclabs.com.Incidental: untracked
site/(was gitignored but still tracked, so any local build dirtied 190+ files); droppedcontents: writeand the git-credentials step, since Pages isbuild_type=workflowanddeploy-pagesnever pushes a branch.Current state
docs.doublezero.xyzis live and serving.docs.malbeclabs.comstill serves its own build with no redirect. Merging converges them.Testing Verification
Verified against a live Vercel deployment before CI was wired, to settle assumptions rather than trust them:
.well-known/dot-directory is served at the edge (index.json,SKILL.md,mcp/server-card.jsonall 200). This was the main risk:actions/upload-artifactdrops hidden files by default, which is why both deploys route through a tar..well-knownSHA-256 matches the local build, confirming the edge serves the same bytes./setupand/setup/share the identical canonical, so trailing-slash variants consolidate instead of duplicating.../DZ%20Mainnet-beta%20Connection/from/es/tenant/→/es/…), and every localized target exists.%20wasn't assumed to survive mkdocs' file lookup, so one link was converted and its rendered href inspected before the other 25.docs.malbeclabs.comconfirmed returning 200 with an empty redirect target throughout.translate.ymlpushed 4921f21 (21 localized files, 1459 insertions). Confirmed it did not clobber the link conversion: no absolute links returned, all 14 localizedtenant/troubleshootingfiles kept their relative destinations, and--strictstill passes.After merge
Digest parity across origins currently differs by design, since
mainstill serves the old build. Post-merge,sitemap.xmland.well-known/*should match byte for byte;llms.txtembedssite_nameas its H1 so it matches too under this shared build. Needs verifying once merged.