πŸš€ SDLC & CI/CD Workflow

How Solomon is built, tested, and shipped to production

πŸ”€ Dual-Workflow Architecture

Solomon uses two GitHub Actions workflows to optimize deploy times based on what changed.

πŸ—οΈ

Full Deploy

deploy-and-release.yml

  • Trigger: Push to main (excludes functions/**)
  • Duration: ~10 minutes
  • Jobs: 5 (version β†’ build β†’ deploy β†’ release-notes β†’ create-release)
  • Builds Angular app + deploys hosting, Firestore, and functions
⚑

Functions-Only Deploy

deploy-functions.yml

  • Trigger: Push to main with changes only in functions/**
  • Duration: ~2 minutes
  • Skips Angular build entirely
  • Ideal for voice ID changes, Cloud Function edits

πŸ“Š Full Pipeline (5 Jobs)

1
πŸ”’ Version Calculation
Fetches all git tags, parses latest version, scans commit log for conventional commit keywords to determine bump type.
git fetch --tags Semantic Versioning
↓
2
πŸ—οΈ Build Application
Node 20, injects version into version.ts, runs npm install then npm run build:prod. Uploads artifact with 1-day retention.
Node 20 Angular CLI npm install
↓
3
πŸš€ Deploy to Firebase
Downloads artifact, builds functions, writes secrets to functions/.env, authenticates via WIF, deploys hosting + Firestore + functions.
firebase-tools@13.15.2 WIF Auth gcloud CLI
↓
4
πŸ“ AI Release Notes
Calls Claude API (claude-sonnet-4-6) with the commit log to generate categorized release notes. Falls back to grep/sed categorization on failure.
Claude API Conventional Commits
↓
5
🏷️ Create GitHub Release
Creates an annotated git tag, pushes it, then creates a GitHub Release with the AI-generated release notes.
git tag gh release create
Legend
Pipeline Stage
Tools & Technologies

πŸ”’ Semantic Versioning

Version bumps are determined automatically from conventional commit messages in the commit log.

MAJOR (Breaking)
v2.0.0
BREAKING CHANGE or [major]
MINOR (Feature)
v1.3.0
feat: or [minor]
PATCH (Fix)
v1.2.4
fix: (default)
Tag format: v{major}.{minor}.{patch} β€” each release creates an annotated git tag and a GitHub Release with categorized notes.

πŸ” WIF Authentication

Solomon uses Workload Identity Federation (WIF) for GitHub Actions CI/CD β€” no stored service account keys.

Configuration

  • Pool: github-actions-pool
  • Provider: github-provider
  • Action: google-github-actions/auth@v2 with token_format: 'access_token'
  • Deploy token: FIREBASE_TOKEN: ${{ steps.auth.outputs.access_token }}
Critical: Do NOT set --allowed-audiences when creating the OIDC provider. The google-github-actions/auth@v2 action sends the provider resource name as the audience by default β€” setting a custom audience causes an "audience does not match" error.

πŸ“ AI Release Notes

Primary: Claude API

On each release, the pipeline sends the conventional commit log to Claude (claude-sonnet-4-6) which generates categorized markdown release notes:

  • Features β€” new capabilities
  • Bug Fixes β€” resolved issues
  • Performance β€” optimization changes
  • Documentation β€” doc updates
  • Refactoring β€” code improvements

Fallback: Shell Categorization

If the Claude API call fails (non-200 response), a shell-based fallback using grep and sed categorizes commits into the same sections.

⚠️ CI/CD Gotchas

Issue Solution
firebase-tools@13.15.3+ fails with 403 on extensions API check Pin to firebase-tools@13.15.2
npm ci fails with 403/404 on older transitive deps Use npm install (resilient to registry errors)
SSL errors in Firebase deploy Set NODE_TLS_REJECT_UNAUTHORIZED: '0' in env
WIF audience mismatch Omit --allowed-audiences on OIDC provider
Secret Manager permissions in deploy Write secrets to functions/.env in CI before deploy, not firebase.json secrets[] or gcloud run services update

πŸ› οΈ Local Deploy

# Full deploy (hosting + firestore + functions) NODE_TLS_REJECT_UNAUTHORIZED=0 firebase deploy # Functions only NODE_TLS_REJECT_UNAUTHORIZED=0 firebase deploy --only functions # Docs site only NODE_TLS_REJECT_UNAUTHORIZED=0 firebase deploy --only hosting:docs # Re-authenticate if token expired NODE_TLS_REJECT_UNAUTHORIZED=0 firebase login --reauth