CI/CD Integration

Run MSR Firebase migrations in continuous integration and deployment pipelines.

Table of contents

  1. GitHub Actions
    1. Using CLI Flags (Recommended)
    2. Using Environment Variables
    3. With Validation
  2. GitLab CI
    1. With Manual Approval
  3. CircleCI
  4. Docker
    1. Using CLI Flags
    2. Using Environment Variables
  5. Kubernetes
    1. Job for One-Time Migrations
    2. Init Container for Application Pods
  6. Best Practices
    1. Security
    2. Validation
    3. Backup Strategy
    4. Environment Separation
    5. Error Handling
    6. Monitoring

GitHub Actions

Store service account key as a secret and use CLI flags:

name: Run Migrations

on:
  push:
    branches: [main]

jobs:
  migrate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Create service account key file
        run: echo '$' > key.json

      - name: Run migrations
        run: |
          npx msr-firebase migrate \
            --database-url $ \
            --credentials ./key.json \
            --backup-mode full

      - name: Cleanup
        if: always()
        run: rm -f key.json

Using Environment Variables

- name: Run migrations
  env:
    DATABASE_URL: $
    GOOGLE_APPLICATION_CREDENTIALS: ./key.json
  run: npx msr-firebase migrate

With Validation

- name: Validate migrations
  run: npx msr-firebase validate

- name: Run migrations
  run: |
    npx msr-firebase migrate \
      --database-url $ \
      --credentials ./key.json

GitLab CI

migrate:
  stage: deploy
  image: node:20-alpine
  before_script:
    - npm ci
    - echo "$FIREBASE_SERVICE_ACCOUNT_KEY" > key.json
  script:
    - npx msr-firebase migrate
        --database-url $FIREBASE_DATABASE_URL
        --credentials ./key.json
        --backup-mode full
  after_script:
    - rm -f key.json
  only:
    - main

With Manual Approval

migrate:
  stage: deploy
  image: node:20-alpine
  before_script:
    - npm ci
    - echo "$FIREBASE_SERVICE_ACCOUNT_KEY" > key.json
  script:
    - npx msr-firebase migrate --database-url $FIREBASE_DATABASE_URL --credentials ./key.json
  after_script:
    - rm -f key.json
  when: manual  # Requires manual trigger
  only:
    - main

CircleCI

version: 2.1

jobs:
  migrate:
    docker:
      - image: cimg/node:20.10
    steps:
      - checkout
      - run:
          name: Install dependencies
          command: npm ci
      - run:
          name: Create credentials file
          command: echo $FIREBASE_SERVICE_ACCOUNT_KEY | base64 -d > key.json
      - run:
          name: Run migrations
          command: |
            npx msr-firebase migrate \
              --database-url $FIREBASE_DATABASE_URL \
              --credentials ./key.json
      - run:
          name: Cleanup
          command: rm -f key.json
          when: always

workflows:
  version: 2
  deploy:
    jobs:
      - migrate:
          filters:
            branches:
              only: main

Docker

Using CLI Flags

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY migrations ./migrations
COPY serviceAccountKey.json ./key.json

CMD ["npx", "msr-firebase", "migrate", \
     "--database-url", "${DATABASE_URL}", \
     "--credentials", "./key.json"]

Run:

docker build -t msr-firebase-migrations .
docker run --env DATABASE_URL=https://your-project.firebaseio.com msr-firebase-migrations

Using Environment Variables

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY migrations ./migrations

CMD ["npx", "msr-firebase", "migrate"]

Run:

docker run \
  --env DATABASE_URL=https://your-project.firebaseio.com \
  --env GOOGLE_APPLICATION_CREDENTIALS=/app/key.json \
  --volume $(pwd)/key.json:/app/key.json:ro \
  msr-firebase-migrations

Kubernetes

Job for One-Time Migrations

apiVersion: batch/v1
kind: Job
metadata:
  name: firebase-migrations
spec:
  template:
    spec:
      containers:
      - name: migrations
        image: your-registry/msr-firebase-migrations:latest
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: firebase-secrets
              key: database-url
        - name: GOOGLE_APPLICATION_CREDENTIALS
          value: /secrets/firebase-key.json
        volumeMounts:
        - name: firebase-credentials
          mountPath: /secrets
          readOnly: true
      volumes:
      - name: firebase-credentials
        secret:
          secretName: firebase-service-account
      restartPolicy: Never
  backoffLimit: 3

Init Container for Application Pods

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      initContainers:
      - name: run-migrations
        image: your-registry/msr-firebase-migrations:latest
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: firebase-secrets
              key: database-url
        - name: GOOGLE_APPLICATION_CREDENTIALS
          value: /secrets/firebase-key.json
        volumeMounts:
        - name: firebase-credentials
          mountPath: /secrets
          readOnly: true
      containers:
      - name: app
        image: your-app:latest
      volumes:
      - name: firebase-credentials
        secret:
          secretName: firebase-service-account

Best Practices

Security

Store Credentials Securely:

  • Use CI/CD secrets management
  • Never commit service account keys to git
  • Use short-lived credentials when possible
  • Clean up credential files after use

Example Cleanup:

- name: Cleanup
  if: always()
  run: rm -f key.json

Validation

Always validate before deploying:

npx msr-firebase validate
npx msr-firebase migrate --dry-run
npx msr-firebase migrate

Backup Strategy

Use appropriate backup mode:

# Production: full backup and restore
npx msr-firebase migrate --backup-mode full

# Development: no backup needed
npx msr-firebase migrate --backup-mode manual

Environment Separation

Use different databases:

# Development
DATABASE_URL: https://dev-project.firebaseio.com

# Staging
DATABASE_URL: https://staging-project.firebaseio.com

# Production
DATABASE_URL: https://prod-project.firebaseio.com

Or use shift paths:

# Shared database with namespacing
npx msr-firebase migrate --shift development
npx msr-firebase migrate --shift staging
npx msr-firebase migrate --shift production

Error Handling

Exit codes:

  • 0 - Success
  • 1 - General error
  • 7 - Database connection error

Example with error handling:

- name: Run migrations
  run: npx msr-firebase migrate --credentials ./key.json || exit 1

- name: Notify on failure
  if: failure()
  run: curl -X POST $SLACK_WEBHOOK -d '{"text":"Migration failed"}'

Monitoring

Log output:

npx msr-firebase migrate --log-level debug --logger console

JSON output for parsing:

npx msr-firebase list --format json > migration-status.json