Skip to content

Deploying the docs to GitHub Pages ​

The TeslaSync user docs are a VitePress site under docs/. The published site at https://teslasync.dev/ is a build artefact, not something you edit on the live server. This page describes how that pipeline works end to end so you can preview locally, ship safely, and debug when it breaks.

What you're publishing ​

Only the user-facing docs ship. Internal-only material — audits, runbooks, observability notes, architecture ADRs, signal audits, dev guidelines like A11Y_GUIDELINES.md — stays in the repo for contributors but is excluded from the built site.

The exclusion list lives in docs/.vitepress/config.ts under srcExclude. Anything matched there is skipped during build. If you add a new internal-only doc, add it to the list. If you want a previously-internal doc to be published, remove it from the list and verify it renders cleanly (some internal docs contain Vue-interpolation-confusing characters that need escaping for VitePress to parse them).

Preview locally ​

bash
cd docs
npm install
npm run docs:dev

VitePress watches the markdown files and reloads instantly on save. The dev server is at http://localhost:5173 (port may shift if 5173 is busy).

Build a production bundle locally before pushing if you've made structural changes:

bash
npm run docs:build

This runs the same build as CI. Run the custom-domain asset gate too:

bash
node scripts/verify-pages-assets.mjs

The gate checks the root-relative CSS/JS/hero references, screenshot files, and the published CNAME. A build can succeed while those asset URLs are wrong.

Preview the production bundle:

bash
npm run docs:preview

This serves docs/.vitepress/dist at http://localhost:4173. Use this to verify the production base path and asset URLs work — that's where the most common Pages-specific bugs show up.

How the production build differs from dev ​

VitePress builds for a specific base path. The official site uses the custom domain https://teslasync.dev/, served at the domain root. Its CSS, JS, links, and screenshots must resolve under /, not /teslasync/. GitHub's project Pages URL redirects to the custom domain.

docs/.vitepress/config.ts reads the base path from the environment:

ts
const base = process.env.DOCS_BASE || '/'

CI (.github/workflows/docs.yml) builds with DOCS_BASE=/ to match the custom domain. Local dev and docs:preview also default to /.

If you fork without a custom domain, set DOCS_BASE in the workflow to /<repo-name>/ and remove the docs/public/CNAME file. A wrong base path produces a site that loads index.html but 404s on JS, CSS, and image assets.

Markdown links and images get the base applied automatically, but raw HTML in markdown does not. Link cards and other raw-HTML anchors must use Vue bindings:

html
<a :href="withBase('/guide/getting-started')">Install</a>

with <script setup>import { withBase } from 'vitepress'</script> at the top of the page. (Theme .vue files should use withBase() the same way.)

What ships in dist ​

After a build:

docs/.vitepress/dist/
├── assets/             # hashed JS, CSS, fonts
├── images/             # static images from docs/public/images
├── logo.svg            # static asset from docs/public
├── index.html          # generated home
├── guide/              # generated per-page HTML
├── features/
├── deployment/
├── contributing/
└── 404.html

Anything in docs/public/ is copied verbatim to the root of dist. Anything in docs/.vitepress/theme/ participates in the VitePress build pipeline. Don't put runtime-fetched data in docs/ — the docs are static.

The CI pipeline ​

The workflow lives in .github/workflows/docs.yml. The shape is:

  1. Trigger on push to main with docs changes or by manual workflow dispatch.
  2. Set up Node.
  3. cd docs && npm ci.
  4. npm run docs:build.
  5. Run docs/scripts/verify-pages-assets.mjs to reject a subpath build on the custom domain.
  6. Upload docs/.vitepress/dist as a Pages artefact.
  7. Deploy the artefact to the github-pages environment.

The repo's Pages settings (Settings → Pages in GitHub) must be configured to deploy from GitHub Actions, not from a branch. Branch-based Pages is the legacy mode and will fight the workflow.

Triggering a deploy ​

A deploy happens when:

  • Anyone merges to main with changes under docs/
  • A maintainer manually re-runs the workflow from the Actions tab (e.g., to re-publish after a Pages settings change)

Pull requests do not trigger this workflow or deploy the site. Build and run the asset gate locally before merging a docs change.

Custom domain ​

If you're forking and want to publish on a custom domain:

  1. Replace docs/public/CNAME with your domain (e.g., docs.example.com).
  2. Set DOCS_BASE: / in .github/workflows/docs.yml (custom domains serve from the root).
  3. Configure your DNS provider with the CNAME / A records GitHub Pages requires.
  4. Add the custom domain in Settings → Pages and wait for the TLS certificate to provision (a few minutes).

The bundled CNAME records the official custom domain in the published artifact.

Theme overrides ​

VitePress uses Vue Single-File Components for theme customisation. docs/.vitepress/theme/ is the override directory. We currently use the default theme with a small amount of CSS tuning; if you add component overrides, keep them additive — extend the default theme rather than replacing it, so future VitePress upgrades don't break your customisations.

Common things that go wrong ​

SymptomLikely causeFix
Build fails with Element is missing end tagA markdown file has Vue-style double-brace interpolation outside a fenced code blockWrap the offending example in a fenced code block, or add the file to srcExclude
Site loads but every page is 404base is wrong for where you're publishingSet DOCS_BASE: / in docs.yml for teslasync.dev (/<repo-name>/ without a custom domain)
Assets 404 in production but work in devSame as above — base path mismatchSame fix
Search returns nothingThe local-search index didn't build (corrupt cache)Delete docs/.vitepress/cache/ and rebuild
Build "succeeds" but the workflow failsPages settings aren't set to deploy from ActionsSettings → Pages → Source: GitHub Actions
Deploy succeeds but the URL still shows the old siteCloudflare / CDN cache between you and PagesPurge the cache, or wait it out (usually ~5 minutes)
npm ci fails in CI but works locallyLockfile driftCommit the regenerated package-lock.json from CI's diff

Excluding files mid-stream ​

If a doc breaks the build and you need to ship without it temporarily, add the path to srcExclude in docs/.vitepress/config.ts. The file stays in the repo for editors but is omitted from the production site until you fix it.

This is the mechanism the project uses for the development-guideline docs (A11Y_GUIDELINES.md, FORM_GUIDELINES.md, etc.) that contain Vue-interpolation-conflicting examples like double-brace expressions outside fenced code blocks.

Verifying before you push docs changes ​

The pre-push baseline for docs is:

bash
cd docs
npm run docs:build
node scripts/verify-pages-assets.mjs

If either command errors, fix it locally — a VitePress build alone does not guarantee that assets resolve on the production domain.

Where to learn more ​

Self-hosted Tesla intelligence · MIT licensed · Privacy policy