Skip to content

CI/CD Pipeline Guide

Last Updated: 2026-01-14
Status: ✅ Active

Overview

Lantern uses GitHub Actions for continuous integration and continuous deployment (CI/CD). This ensures code quality, automated testing, and streamlined deployments to development and production environments.

Table of Contents

  1. Implementation Summary
  2. Workflows
  3. Branch Strategy
  4. Environment Variables
  5. Deployment Process
  6. Status Checks
  7. Troubleshooting
  8. Maintenance

Implementation Summary

What Was Implemented

A complete CI/CD pipeline using GitHub Actions that automates:

  1. Code Quality Checks - Linting, formatting, building, and testing
  2. Security Scanning - npm audit and CodeQL vulnerability detection
  3. Firestore Validation - Ensures indexes are valid before deployment
  4. Automated Deployments - Deploys to dev and prod environments
  5. Discord Notifications - Team alerts for deployment status

Files Added

GitHub Actions Workflows (4 files):

  • .github/workflows/ci.yml - Main CI pipeline (runs on every commit and PR)
  • .github/workflows/deploy-dev.yml - Development deployment (triggers on dev branch)
  • .github/workflows/deploy-prod.yml - Production deployment (triggers on main branch)
  • .github/workflows/codeql.yml - Security scanning (runs on push, PR, and weekly)

Documentation:

  • docs/engineering/deployment/CICD_GUIDE.md - Complete reference guide (this file)
  • docs/engineering/deployment/CICD_QUICK_SETUP.md - Step-by-step setup checklist
  • docs/engineering/deployment/BRANCH_PROTECTION_SETUP.md - Branch protection guide
  • docs/worklog/CICD_IMPLEMENTATION_COMPLETE.md - Implementation worklog entry

Cost Impact

  • GitHub Actions: ~300-400 minutes/month (free tier includes 2,000/month)
  • Cloudflare Pages: Unlimited deployments on free tier
  • Firebase: Rules/indexes deployment is free
  • Total Cost: $0

Workflows

1. CI Workflow (.github/workflows/ci.yml)

Triggers:

  • Push to main or dev branches
  • Pull requests to main or dev branches

Jobs:

Lint

  • Runs ESLint on all JavaScript/JSX files
  • Checks code formatting with Prettier
  • Required to pass for merge

Build

  • Builds the web application
  • Builds Storybook component library
  • Builds VitePress documentation
  • Uploads build artifacts for review
  • Required to pass for merge

Test

  • Runs Vitest unit tests with coverage
  • Uploads coverage reports to Codecov
  • Required to pass for merge

Security

  • Runs npm audit on main dependencies
  • Runs npm audit on Discord bot dependencies
  • Checks for known vulnerabilities
  • Non-blocking but generates warnings

Validate Firestore Indexes

  • Validates firestore.indexes.json exists and is valid JSON
  • Warns about manual index deployment requirements
  • Required to pass for merge

All Checks Complete

  • Aggregates all job results
  • Provides clear pass/fail status
  • Required to pass for merge

Concurrency:
Cancels in-progress runs for the same workflow on the same PR/branch to save resources.


2. Deploy to Development (.github/workflows/deploy-dev.yml)

Triggers:

  • Automatically after CI workflow completes successfully on dev branch
  • Uses workflow_run trigger to ensure CI checks pass before deployment

Pre-deployment Checks:

  • CI Success Check: Verifies CI workflow completed successfully
  • Fail if CI Failed: Blocks deployment if CI checks did not pass
  • Purpose: Prevents deploying broken, untested, or insecure code

Jobs:

Check CI Success

  • Runs only if CI workflow passed all checks
  • Gates all deployment jobs
  • Required to pass before any deployment occurs

Deploy App

  • Builds application with dev Firebase config
  • Deploys to Cloudflare Pages (lantern-app-dev project)
  • Live URL: dev.ourlantern.app
  • Depends on: CI success check

Deploy Storybook

  • Builds Storybook component library
  • Deploys to Cloudflare Pages (lantern-storybook-dev project)
  • Live URL: storybook.dev.ourlantern.app
  • Depends on: CI success check

Deploy Docs

  • Builds VitePress documentation
  • Deploys to Cloudflare Pages (lantern-docs-dev project)
  • Live URL: docs.dev.ourlantern.app
  • Depends on: CI success check

Deploy Firebase Rules

  • Deploys Firestore security rules to lantern-app-dev
  • Deploys Firestore indexes to lantern-app-dev
  • Deploys Cloud Storage rules to lantern-app-dev
  • Depends on: CI success check

Notify Discord

  • Sends deployment status to Discord webhook
  • Includes deployment results for all services
  • Provides direct links to deployed environments
  • Critical: If Discord notification fails, the entire deployment fails
    • Prevents losing commit notification history
    • Ensures audit trail is maintained
    • Common failure causes: invalid webhook, character encoding issues
    • Deployment can be retried to send the notification

3. Deploy to Production (.github/workflows/deploy-prod.yml)

Triggers:

  • Automatically after CI workflow completes successfully on main branch
  • Manual workflow dispatch (for emergency deployments)
  • Uses workflow_run trigger to ensure CI checks pass before deployment

Pre-deployment Checks:

  • CI Success Check: Verifies CI workflow completed successfully (automatic deploys only)
  • Fail if CI Failed: Blocks deployment if CI checks did not pass (automatic deploys only)
  • Verify Deployment: Ensures deployment is from main branch only
  • Purpose: Prevents deploying broken, untested, or insecure code to production

Environment: Uses GitHub production environment with protection rules

Jobs:

Check CI Success

  • Runs only if CI workflow passed all checks (automatic deploys)
  • Gates all deployment jobs
  • Skipped for manual emergency deployments
  • Required to pass before any automatic deployment occurs

Verify Deployment

  • Ensures deployment is from main branch only
  • Logs manual deployment triggers
  • Depends on: CI success check (for automatic deploys)

Deploy App

  • Builds application with production Firebase config
  • Deploys to Cloudflare Pages (lantern-app project)
  • Live URL: ourlantern.app

Deploy Storybook

  • Builds Storybook component library
  • Deploys to Cloudflare Pages (lantern-storybook project)
  • Live URL: storybook.ourlantern.app

Deploy Docs

  • Builds VitePress documentation
  • Deploys to Cloudflare Pages (lantern-docs project)
  • Live URL: docs.ourlantern.app

Deploy Firebase Rules

  • Deploys Firestore security rules to lantern-app-prod
  • Deploys Firestore indexes to lantern-app-prod
  • Deploys Cloud Storage rules to lantern-app-prod

Notify Discord

  • Sends deployment status to Discord webhook
  • Critical: If Discord notification fails, the entire deployment fails
    • Prevents losing commit notification history
    • Ensures audit trail is maintained
    • Common failure causes: invalid webhook, character encoding issues
    • Deployment can be retried to send the notification
  • Alerts team on deployment failures with exit code 1

4. CodeQL Security Scan (.github/workflows/codeql.yml)

Triggers:

  • Push to main or dev branches
  • Pull requests to main or dev branches
  • Scheduled: Every Monday at 6:30 AM UTC

Jobs:

Analyze Code

  • Scans JavaScript/JSX code for security vulnerabilities
  • Uses security-extended and security-and-quality query suites
  • Uploads results to GitHub Security tab
  • Non-blocking but generates alerts for review

Permissions:
Requires security-events: write to upload scan results


5. Discord Notification (.github/workflows/discord-notify.yml)

Triggers:

  • Push to main or dev branches

Jobs:

Notify

  • Sends commit notifications to Discord
  • Includes commit messages, authors, and links
  • Color-coded by branch (main = green, dev = blue)
  • Smart recovery: Tracks posted commits and automatically retries missed ones on next push
    • If notification fails, commit is logged for retry
    • Next push includes any previously failed commits
    • Ensures complete audit trail even if Discord is temporarily down

How missed commit recovery works:

  1. Each successful Discord notification is recorded
  2. If notification fails, commit is marked for retry
  3. Next successful push automatically includes missed commits with a note
  4. You'll never lose commit history even if Discord API is temporarily down

CI/CD Workflow Dependencies

How Deployments Depend on CI

To prevent deploying broken code, our deployment workflows use GitHub's workflow_run trigger:

Flow:

Push to dev/main

CI Workflow runs (lint, build, test, security, indexes)

    ├─ ✅ CI Passes → Deployment workflow triggers
    │                 └─ Check CI Success job gates deployment
    │                     └─ All deployment jobs proceed

    └─ ❌ CI Fails → Deployment workflow triggers
                      └─ Fail if CI Failed job runs
                          └─ Deployment blocked with exit 1

Key Benefits:

  • Zero risk of deploying untested code: Deployments only occur after all CI checks pass
  • Automatic blocking: No manual intervention needed; failed CI = no deployment
  • Clear feedback: Deployment logs show exactly why deployment was blocked
  • Emergency override: Manual deployments can bypass CI (use with extreme caution)

Technical Implementation:

  • Deployment workflows use on: workflow_run instead of on: push
  • workflows: ["CI"] specifies dependency on CI workflow
  • types: [completed] triggers after CI finishes (success or failure)
  • if: github.event.workflow_run.conclusion == 'success' gates deployment jobs
  • Failed CI causes fail-if-ci-failed job to exit with code 1

Important Notes:

  • Automatic deploys: CI must pass before deployment runs
  • Manual deploys (prod only): Can bypass CI check for emergencies
  • Both dev and prod: Protected against deploying broken code
  • Cloudflare Pages: While Cloudflare may auto-deploy on push, our GitHub Actions workflow enforces the CI gate before triggering deployment

Cloudflare Pages Configuration

Automatic Deployment Settings

⚠️ CRITICAL: Cloudflare Pages must be configured to disable automatic deployments and instead rely on GitHub Actions workflow triggers.

Why? Cloudflare's automatic deployment feature watches for push events directly and deploys immediately, bypassing our CI/CD gates. This means broken code could be deployed to production without passing tests.

Setup Instructions

For both lantern-app-dev and lantern-app-prod projects:

  1. Go to Cloudflare Pages project settings

    • Navigate to Cloudflare Dashboard
    • Select your project (e.g., lantern-app-dev)
    • Click SettingsBuilds & deployments
  2. Disable Automatic Deployments

    • Under "Automatic deployments," toggle OFF
    • This prevents Cloudflare from auto-deploying on every push
  3. Verify GitHub Integration is Active

    • Ensure the GitHub integration shows your repository is connected
    • GitHub Actions workflows will now trigger deployments after CI passes

Result

After disabling automatic deployments:

  • ✅ Push to dev → CI workflow runs
  • ✅ CI completes → Deployment workflow triggers (via workflow_run)
  • ✅ Deployment gates check CI status
  • Only if CI passed: Cloudflare deployment proceeds via GitHub Actions
  • If CI failed: Deployment is blocked automatically

This ensures zero risk of deploying untested code.

Troubleshooting Cloudflare Deployments

If deployments aren't triggering after disabling automatic deployments:

  1. Verify GitHub integration is still active - Go to project settings and confirm the connection
  2. Check workflow run logs - Look at .github/workflows/deploy-dev.yml execution
  3. Confirm CI workflow completed - The deployment workflow only triggers after CI finishes
  4. Check branch name - Deployments only trigger on dev and main branches

Branch Strategy

main (production)

  └── dev (development/staging)

        └── feature/my-feature
        └── fix/bug-fix
        └── copilot/ai-feature

Rules

  • Feature branches: All new work starts in a feature branch from dev
  • Development: Merge features → dev for testing and QA
  • Production: Merge devmain for production releases
  • Never commit directly to main (enforced by branch protection)

Workflow

  1. Create feature branch from dev:

    bash
    git checkout dev
    git pull origin dev
    git checkout -b feature/my-feature
  2. Make changes, commit, and push:

    bash
    git add .
    git commit -m "Add my feature"
    git push origin feature/my-feature
  3. Open pull request to dev branch

  4. CI runs automatically on PR:

    • ✅ Lint
    • ✅ Build
    • ✅ Test
    • ✅ Security
    • ✅ Validate Firestore indexes
  5. After PR approval and merge to dev:

    • ✅ Auto-deploys to dev environment
  6. When ready for production, merge devmain:

    • ✅ Auto-deploys to production

Environment Variables

Required GitHub Secrets

Set these in Settings → Secrets and variables → Actions → Repository secrets:

Firebase Configuration (Development)

VITE_FIREBASE_API_KEY_DEV
VITE_FIREBASE_AUTH_DOMAIN_DEV
VITE_FIREBASE_PROJECT_ID_DEV
VITE_FIREBASE_STORAGE_BUCKET_DEV
VITE_FIREBASE_MESSAGING_SENDER_ID_DEV
VITE_FIREBASE_APP_ID_DEV

Firebase Configuration (Production)

VITE_FIREBASE_API_KEY_PROD
VITE_FIREBASE_AUTH_DOMAIN_PROD
VITE_FIREBASE_PROJECT_ID_PROD
VITE_FIREBASE_STORAGE_BUCKET_PROD
VITE_FIREBASE_MESSAGING_SENDER_ID_PROD
VITE_FIREBASE_APP_ID_PROD

Cloudflare

CLOUDFLARE_API_TOKEN       # API token with Pages:Edit permissions
CLOUDFLARE_ACCOUNT_ID      # Account ID from Cloudflare dashboard

Firebase CLI

FIREBASE_TOKEN             # Firebase CI token (run: firebase login:ci)

Discord

DISCORD_WEBHOOK_COMMITS     # Discord webhook for commit notifications (#commits channel)
DISCORD_WEBHOOK_DEV_DEPLOY  # Discord webhook for dev deployments (#deployments channel)
DISCORD_WEBHOOK_PROD_DEPLOY # Discord webhook for prod deployments (#deployments channel)

Note: See DISCORD_WEBHOOK_SETUP.md for detailed webhook configuration instructions.

Optional

CODECOV_TOKEN              # Codecov token for coverage reports

Environment Detection

The application automatically detects the environment:

  • Local: VITE_APP_ENV=local (or not set)
  • Development: VITE_APP_ENV=development
  • Production: VITE_APP_ENV=production

This controls which Firebase project is used and which features are enabled.


Deployment Process

Automatic Deployments

Development

  1. Merge PR to dev branch
  2. CI runs and validates
  3. On success, deployment workflow triggers
  4. All services deploy to dev environment
  5. Discord notification sent

Production

  1. Merge dev to main branch (via PR)
  2. CI runs and validates
  3. On success, deployment workflow triggers
  4. All services deploy to production
  5. Discord notification sent
  6. Manual approval required (if environment protection is enabled)

Manual Deployments

Emergency Production Deploy

Use GitHub Actions UI:

  1. Go to Actions tab
  2. Select Deploy to Production workflow
  3. Click Run workflow
  4. Select main branch
  5. Optionally skip tests (emergency only)
  6. Click Run workflow

⚠️ Use manual deployments only for emergencies!

Rollback Procedure

If a bad deployment goes live:

  1. Immediate rollback:

    bash
    git checkout main
    git revert <bad-commit-sha>
    git push origin main

    This triggers a new production deployment with the revert.

  2. Or revert to previous commit:

    bash
    git checkout main
    git reset --hard <previous-good-commit>
    git push --force origin main

    ⚠️ Force push requires temporarily disabling branch protection

  3. Cloudflare Pages rollback:

    • Go to Cloudflare Pages dashboard
    • Select project (e.g., lantern-app)
    • Go to Deployments
    • Find previous working deployment
    • Click Rollback to this deployment

Status Checks

Required Status Checks (Branch Protection)

To enable branch protection on main and dev:

  1. Go to Settings → Branches

  2. Add branch protection rule for main:

    • ✅ Require a pull request before merging
    • ✅ Require approvals (at least 1)
    • ✅ Dismiss stale pull request approvals when new commits are pushed
    • ✅ Require status checks to pass before merging
      • lint
      • build
      • test
      • validate-firestore-indexes
      • all-checks-complete
    • ✅ Require conversation resolution before merging
    • ✅ Do not allow bypassing the above settings
  3. Repeat for dev branch

Result: PRs cannot be merged if CI fails


Troubleshooting

Build Failures

Lint Errors

Error: ESLint found issues

Fix:

bash
npm run lint:fix
npm run format

Build Errors

Error: Vite build failed

Fix:

  • Check for TypeScript/JavaScript errors in code
  • Verify all imports are correct
  • Check environment variables are set in GitHub Secrets

Test Failures

Error: Tests failed

Fix:

  • Run tests locally: npm test
  • Fix failing tests
  • Ensure Firebase environment variables are set

Deployment Failures

Cloudflare API Error

Error: Unauthorized (403)

Fix:

  • Verify CLOUDFLARE_API_TOKEN is set and valid
  • Ensure token has Pages:Edit permissions
  • Check CLOUDFLARE_ACCOUNT_ID is correct

Firebase Deployment Error

Error: HTTP Error: 401, Unauthorized

Fix:

  • Regenerate Firebase token: firebase login:ci
  • Update FIREBASE_TOKEN secret in GitHub

Discord Notification Failure

Error: Webhook URL invalid
Error: Character encoding issues

Fix:

  • Verify DISCORD_WEBHOOK_COMMITS, DISCORD_WEBHOOK_DEV_DEPLOY, DISCORD_WEBHOOK_PROD_DEPLOY are set and valid
  • Check Discord server settings
  • Recreate webhook if necessary
  • Important: Discord notification failures will retry automatically on next push
    • The commit is logged for automatic retry
    • Next successful push will include the missed commit(s)
    • Ensures complete audit trail even if Discord is temporarily down

Why notifications are critical:

  • Discord notifications provide an audit trail of all commits and deployments
  • Missed notifications are automatically tracked and retried
  • You'll never lose commit history due to temporary Discord downtime
  • If notifications consistently fail, investigate the webhook configuration

Manual recovery if needed:

bash
# View the artifact containing missed commits
# In GitHub Actions, check "discord-posted-commits-log" artifact
# It contains all successfully posted commit SHAs

# These commits will automatically be included in the next push notification

Firestore Index Issues

Index Not Found Error

Error: The query requires an index

Fix:

  1. Firestore Console will suggest the exact index needed
  2. Add to firestore.indexes.json:
    json
    {
      "collectionGroup": "collectionName",
      "queryScope": "COLLECTION",
      "fields": [
        {"fieldPath": "field1", "order": "ASCENDING"},
        {"fieldPath": "field2", "order": "DESCENDING"}
      ]
    }
  3. Deploy manually:
    bash
    firebase deploy --only firestore:indexes --project lantern-app-dev
    firebase deploy --only firestore:indexes --project lantern-app-prod

⚠️ IMPORTANT: Indexes must exist in BOTH dev and prod projects!

Discord Bot Integration Issues

If Discord notifications fail after deployments:

  1. Verify webhook URLs are set and valid:
    • DISCORD_WEBHOOK_COMMITS (for commit notifications)
    • DISCORD_WEBHOOK_DEV_DEPLOY (for dev deployments)
    • DISCORD_WEBHOOK_PROD_DEPLOY (for prod deployments)
  2. Verify webhooks still exist in Discord server
  3. Test webhook manually:
    bash
    curl -X POST $DISCORD_WEBHOOK_COMMITS \
      -H "Content-Type: application/json" \
      -d '{"content": "Test message"}'
  4. If webhook is deleted, recreate and update secret (see DISCORD_WEBHOOK_SETUP.md)

Note: Discord bot deployment is separate (Railway). See DISCORD_BOT_DEPLOYMENT.md.


Maintenance

Regular Tasks

Weekly

  • ✅ Review CodeQL security scan results
  • ✅ Check for npm audit warnings

Monthly

  • ✅ Update dependencies: npm update
  • ✅ Review and update Firestore indexes
  • ✅ Verify all deployments are healthy

Quarterly

  • ✅ Rotate Firebase tokens
  • ✅ Rotate Cloudflare API tokens
  • ✅ Review and optimize CI/CD workflows
  • ✅ Update GitHub Actions versions

Updating Workflows

To update a workflow:

  1. Edit workflow file in .github/workflows/
  2. Commit to feature branch
  3. Test with a PR to dev
  4. Verify workflow runs correctly
  5. Merge to dev, then main

⚠️ Test workflow changes in dev before merging to main!

Adding New Secrets

  1. Go to Settings → Secrets and variables → Actions
  2. Click New repository secret
  3. Name: SECRET_NAME
  4. Value: secret_value
  5. Click Add secret
  6. Update workflow file to use \$\{\{ secrets.SECRET_NAME \}\}

Monitoring

GitHub Actions

  • View workflow runs: Actions tab
  • View logs: Click on workflow run → Click on job
  • Download artifacts: Scroll to bottom of job logs

Codecov

CodeQL

  • View security alerts: Security tab → Code scanning alerts

Best Practices

For Developers

  1. Always create feature branches from dev

    bash
    git checkout dev
    git pull origin dev
    git checkout -b feature/my-feature
  2. Run checks locally before pushing

    bash
    npm run lint
    npm run format:check
    npm run build
    npm test
  3. Keep commits small and focused

    • One feature/fix per commit
    • Write clear commit messages
  4. Update CHANGELOG.md

  5. Test in dev before merging to main

    • Merge to dev first
    • Verify deployment works
    • Then merge devmain

For Maintainers

  1. Review all PRs thoroughly

    • Check CI passes
    • Review code changes
    • Test locally if needed
  2. Monitor deployments

    • Check Discord notifications
    • Verify deployed sites work
    • Watch for errors in logs
  3. Keep secrets up to date

    • Rotate tokens regularly
    • Update when they expire
    • Document changes
  4. Maintain Firestore indexes

    • Keep firestore.indexes.json in sync with queries
    • Deploy indexes to both dev and prod
    • Monitor index creation status in Firebase Console

See Also


Quick Reference

Commands

bash
# Run CI checks locally
npm run lint           # ESLint
npm run format:check   # Prettier
npm run build          # Build app
npm test              # Run tests

# Deploy Firebase rules manually
firebase deploy --only firestore:rules --project lantern-app-dev
firebase deploy --only firestore:indexes --project lantern-app-dev

# Generate Firebase CI token
firebase login:ci

URLs

Workflow Status

Check status: github.com/cattreedev/lantern_app/actions

Built with VitePress