Push to main → CI workflow (lint, test, build) → Deploy workflow → Vercel --prod → Smoke test
↓
Fail → Auto-rollback + Issue
Two GitHub Actions workflows manage the pipeline:
| Workflow | Trigger | Purpose |
|---|---|---|
CI (ci.yml) |
Push/PR to main | Lint, format, test, build, quality gates |
Deploy (deploy.yml) |
After CI passes on main | Build, deploy to Vercel, smoke test, rollback |
Pipeline (pipeline.yml) |
Manual / Monthly schedule | Full resume generation pipeline + PR creation |
The Deploy workflow uses workflow_run to trigger only after CI succeeds on main. This prevents deploying broken builds.
Configure these in GitHub repo Settings → Secrets and variables → Actions:
| Secret | Source | Used by |
|---|---|---|
VERCEL_TOKEN |
vercel.com/account/tokens | Deploy |
VERCEL_ORG_ID |
.vercel/project.json → orgId |
Deploy |
VERCEL_PROJECT_ID |
.vercel/project.json → projectId |
Deploy |
ANTHROPIC_API_KEY |
console.anthropic.com | Pipeline |
After every deployment, the smoke test (npm run smoke) verifies:
- Homepage returns 200 with expected content ("Paul Prae", "Professional Summary")
- Resume MD download hash matches the local committed file
- PDF download returns 200, correct content-type, size > 10 KB
- DOCX download returns 200, correct content-type, size > 5 KB
- HTTP → HTTPS redirect works
- Security headers present (HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy)
Run locally:
npm run smoke # Test against https://paulprae.com
SMOKE_TEST_URL=https://preview.vercel.app npm run smoke # Test against previewIf the smoke test fails after deployment, the workflow automatically:
- Retrieves the previous production deployment from Vercel
- Promotes it back to production
- Creates a GitHub issue labeled
deploy-failure
# List recent production deployments
vercel ls --prod
# Promote a specific deployment to production
vercel promote <deployment-url> --yes- Go to vercel.com → Project → Deployments
- Find the last known-good deployment
- Click "..." → "Promote to Production"
Each pipeline run archives the resume to data/generated/versions/. To restore a previous version:
# List available versions
ls data/generated/versions/
# Restore a specific version
cp data/generated/versions/Paul-Prae-Resume-YYYY-MM-DD-<sha>.md data/generated/Paul-Prae-Resume.md
npm run export && npm run check:fix
# Commit and deploy
git add data/generated/ public/Paul-Prae-Resume.*
git commit -m "revert: restore resume from YYYY-MM-DD"
git pushCI runs npm run check:quick -- --ci which validates:
- Data files —
career-data.jsonand resume markdown exist and are non-empty - Resume quality — expected sections present, sufficient positions/bullets, quantification density >= 30%, key companies included, reasonable length
- Public downloads — PDF, DOCX, MD in
public/matchdata/generated/(hash comparison)
In CI mode (--ci), results are output as:
- GitHub Actions annotations (
::error::for failures) - Step summary table in
$GITHUB_STEP_SUMMARY
The Pipeline workflow (pipeline.yml) runs the full resume generation pipeline in CI:
# Trigger manually from GitHub Actions UI or CLI
gh workflow run pipeline.yml
gh workflow run pipeline.yml -f force=true -f auto_approve=trueIt creates a PR with generated changes rather than pushing directly to main, keeping human review in the loop.
The workflow also runs on a monthly schedule (1st of each month at 9 AM UTC) to keep the resume fresh.
- Framework:
null(static site served fromout/) - Build command:
npm run build - Output directory:
out - Auto-deploy: Vercel's Git integration may still fire, but the GitHub Actions deploy is the source of truth. To prevent double-builds, set Vercel Project Settings → Git → Ignored Build Step to
exit 0.
- GitHub Environments: The deploy job uses
environment: production, so deployment history is visible at Settings → Environments → production. - Badges: CI and Deploy status badges are in README.md.
- Failure notifications: GitHub notifies repo owner on workflow failures by default. Additionally, failed deploys create issues labeled
deploy-failure.