CI/CD Pipelines¶
Two GitHub Actions workflows handle everything: ci.yml (test → gated deploy) and docs.yml (docs publishing). All run on GitHub-hosted runners — no self-hosted runner needed.
CI Pipeline (ci.yml)¶
One pipeline on every push to main (plus PRs + manual dispatch). Deploys are gated on tests — a broken config can never reach the live instance.
changes → detect which areas changed (paths-filter): ha_config / grafana / lovelace
test → ALWAYS runs: yamllint + pytest (Tiers 0/2/3) + containerized check_config (Tier 1)
deploy-ha → needs: test, only if ha_config changed (Tailscale → scp → reload/restart)
deploy-grafana → needs: test, only if grafana changed (scp dashboard + provider; verify via Grafana API)
deploy-lovelace → needs: test, only if lovelace changed (race-free stop → swap → start)
- Test job is the gate: if it fails, every deploy job is skipped. It runs the same suite as
make test(see the Testing section inCLAUDE.mdand the design atdocs/superpowers/specs/2026-06-07-ha-test-suite-design.md). Tier 1 runscheck_configon the official HA image pinned to the live version (HA_VERSIONinci.yml). - On pull requests, only
changes+testrun — PRs are validated, never deployed. - deploy-ha connects via Tailscale (ephemeral node, OAuth
tag:ci), SCPs only changed files, then: onlyautomations.yaml→automation.reload(no restart); any other file →ha core restart+ health-check wait. - deploy-grafana SCPs the provisioned dashboard + provider config (auto-reloads ~30s), then verifies via the Grafana API (
GRAFANA_TOKEN) that the dashboard provisioned with resolved datasources. - deploy-lovelace stages the file, then
ha core stop → mv → ha core start(race-free; storage-mode dashboards have no hot-reload service).
Docs (docs.yml)¶
Triggers on pushes to main that change files under docs/ or mkdocs.yml.
Builds the MkDocs Material site with --strict (fails on warnings) and deploys to Cloudflare Pages via cloudflare/pages-action.
Published at: blairmont.pages.dev
Required GitHub Secrets¶
| Secret | Purpose |
|---|---|
HAOS_HOST | HAOS Tailscale IP (100.x.x.x) |
HAOS_SSH_KEY | Ed25519 private key for SCP to HAOS |
HA_TOKEN | HA long-lived access token for REST API calls (reload/restart, health checks, entity snapshot) |
GRAFANA_TOKEN | Grafana service-account token (Viewer) for post-deploy dashboard verification |
TAILSCALE_OAUTH_CLIENT_ID | OAuth client ID (tag: tag:ci) |
TAILSCALE_OAUTH_CLIENT_SECRET | OAuth client secret |
CLOUDFLARE_ACCOUNT_ID | Cloudflare account ID |
CLOUDFLARE_API_TOKEN | Cloudflare API token with Pages Edit permission |
Infrastructure Decisions¶
Private repo — home automation config reveals occupancy patterns, camera placements, and alarm zones. Kept private; the docs site is the public-facing portfolio artifact.
Tailscale over self-hosted runner — GitHub warns against self-hosted runners on public repos (fork PRs can execute arbitrary code). Tailscale lets GitHub-hosted runners reach the local network without that risk, and provides secure remote HAOS access as a side benefit.
Cloudflare Pages over GitHub Pages — GitHub Pages on private repos requires a paid plan. Cloudflare Pages is free and the published docs site is the resume link, not the raw repo.