One Jekyll repo, three sites:
| Domain | What | Theme |
|---|---|---|
| gmacko.com | Build-in-public hub, startups, blog, resume | Warm editorial, burnt sienna |
| grahammackie.com | Personal blog, social links | Warm editorial, slate blue |
| gmac.io | Prototype & service dashboard | Dark mode, neon purple |
bundle install
# Build one site
bundle exec jekyll build --config _config.yml,_config.gmacko.yml --destination _site_gmacko
# Build all three
for site in gmacko personal gmac; do
bundle exec jekyll build --config _config.yml,_config.${site}.yml --destination _site_${site}
donebundle exec jekyll serve --config _config.yml,_config.gmacko.yml --destination _site_gmackoHosted on Cloudflare Pages — three projects (gmacko, personal, gmac) mapped to gmacko.com, grahammackie.com, and gmac.io respectively. A workstation LaunchAgent checks GitHub main every five minutes, deploys changed commits from a dedicated clean worktree, and verifies that all three health endpoints report the exact SHA.
# One-time: authenticate
wrangler login
# Deploy one site
./scripts/deploy-pages.sh gmacko # → gmacko.com
./scripts/deploy-pages.sh personal # → grahammackie.com
./scripts/deploy-pages.sh gmac # → gmac.io
# Or all three
./scripts/deploy-pages.sh allThe deploy script refuses dirty checkouts so uncommitted drafts cannot leak into production. Use a separate clean worktree for every production or preview deployment.
Install or refresh the unattended deployment job after authenticating Wrangler:
npx wrangler login
./scripts/install-cloudflare-deploy-sync.shThe scheduler uses the local Wrangler OAuth session instead of copying a broad Cloudflare token into Forgejo. Force an immediate reconciliation with FORCE_DEPLOY=1 ./scripts/run-cloudflare-deploy-sync.sh.
Install the independent public-content canary after the sites and ForgeGraph feed are live:
./scripts/install-public-content-canary.shEvery five minutes it verifies feed freshness, generation consistency, CORS,
summary totals, the gmac.io hydration bootstrap, and convergence of all three
site health commits. Successful JSON summaries are written to
~/Library/Logs/personalWebsite/public-content-canary.log; failures are written
to the matching .error.log and exit nonzero. The canary observes production
but never publishes content or blocks the deployment agent.
_config.yml— Shared base config_config.{gmacko,personal,gmac}.yml— Per-site overridesDESIGN.md— Shared design system (typography, spacing, motion)DESIGN.{gmacko,personal,gmac}.md— Per-site palettes and layouts_layouts/— Shared + per-site layouts (landing, personal, dashboard)_sass/— CSS custom properties on:rootwith per-site theme overrides_data/gmac/— Services and prototypes for the dashboardpages/{gmac,personal}/— Per-site page templates
Posts need a site front matter field: site: gmacko, site: personal, site: gmac, or site: [gmacko, personal] for cross-posting. Templates filter by site_id from each site's config.