Skip to content

GitHub Actions CI/CD for Policies

This guide shows how to automate policy deployment using GitHub Actions.

Prerequisites

  • Fulcrum CLI installed in your CI environment
  • API key stored as GitHub secret (FULCRUM_API_KEY)
  • Fulcrum API URL stored as GitHub secret (FULCRUM_API_URL)

Workflow Examples

Validate on Pull Request

Validates policy files on every PR:

# .github/workflows/policy-validate.yml
name: Validate Policies

on:
  pull_request:
    paths:
      - 'policies/**'

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

      - name: Install Fulcrum CLI
        run: |
          curl -sSL https://get.fulcrumlayer.io/cli | bash
          echo "$HOME/.fulcrum/bin" >> $GITHUB_PATH

      - name: Validate Policies
        run: fulcrum policies validate --strict ./policies/

      - name: Show Diff
        env:
          FULCRUM_API_KEY: ${{ secrets.FULCRUM_API_KEY }}
          FULCRUM_API_URL: ${{ secrets.FULCRUM_API_URL }}
        run: |
          fulcrum policies diff ./policies/ > diff.txt
          cat diff.txt

      - name: Comment Diff on PR
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const diff = fs.readFileSync('diff.txt', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Policy Changes\n\n\`\`\`\n${diff}\n\`\`\``
            });

Deploy on Merge

Deploys policies when changes are merged to main:

# .github/workflows/policy-deploy.yml
name: Deploy Policies

on:
  push:
    branches:
      - main
    paths:
      - 'policies/**'

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

      - name: Install Fulcrum CLI
        run: |
          curl -sSL https://get.fulcrumlayer.io/cli | bash
          echo "$HOME/.fulcrum/bin" >> $GITHUB_PATH

      - name: Configure CLI
        env:
          FULCRUM_API_KEY: ${{ secrets.FULCRUM_API_KEY }}
          FULCRUM_API_URL: ${{ secrets.FULCRUM_API_URL }}
        run: |
          fulcrum auth login --api-key "$FULCRUM_API_KEY"

      - name: Validate Policies
        run: fulcrum policies validate --strict ./policies/

      - name: Deploy Policies
        run: fulcrum policies import --update ./policies/

Multi-Environment Deployment

Deploy to staging first, then production:

# .github/workflows/policy-deploy-multi.yml
name: Deploy Policies (Multi-Environment)

on:
  push:
    branches:
      - main
    paths:
      - 'policies/**'

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4

      - name: Install Fulcrum CLI
        run: |
          curl -sSL https://get.fulcrumlayer.io/cli | bash
          echo "$HOME/.fulcrum/bin" >> $GITHUB_PATH

      - name: Deploy to Staging
        env:
          FULCRUM_API_KEY: ${{ secrets.FULCRUM_API_KEY_STAGING }}
          FULCRUM_API_URL: ${{ secrets.FULCRUM_API_URL_STAGING }}
        run: |
          fulcrum auth login --api-key "$FULCRUM_API_KEY"
          fulcrum policies import --update ./policies/staging/
          fulcrum policies import --update ./policies/shared/

  deploy-production:
    runs-on: ubuntu-latest
    needs: deploy-staging
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Install Fulcrum CLI
        run: |
          curl -sSL https://get.fulcrumlayer.io/cli | bash
          echo "$HOME/.fulcrum/bin" >> $GITHUB_PATH

      - name: Deploy to Production
        env:
          FULCRUM_API_KEY: ${{ secrets.FULCRUM_API_KEY_PRODUCTION }}
          FULCRUM_API_URL: ${{ secrets.FULCRUM_API_URL_PRODUCTION }}
        run: |
          fulcrum auth login --api-key "$FULCRUM_API_KEY"
          fulcrum policies import --update ./policies/production/
          fulcrum policies import --update ./policies/shared/

Scheduled Sync Check

Detect drift between git and Fulcrum:

# .github/workflows/policy-drift.yml
name: Policy Drift Check

on:
  schedule:
    - cron: '0 */6 * * *'  # Every 6 hours
  workflow_dispatch:

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

      - name: Install Fulcrum CLI
        run: |
          curl -sSL https://get.fulcrumlayer.io/cli | bash
          echo "$HOME/.fulcrum/bin" >> $GITHUB_PATH

      - name: Check for Drift
        id: drift
        env:
          FULCRUM_API_KEY: ${{ secrets.FULCRUM_API_KEY }}
          FULCRUM_API_URL: ${{ secrets.FULCRUM_API_URL }}
        run: |
          fulcrum policies diff ./policies/ > drift.txt
          if grep -E "^[~+-]" drift.txt; then
            echo "drift=true" >> $GITHUB_OUTPUT
          else
            echo "drift=false" >> $GITHUB_OUTPUT
          fi

      - name: Create Issue on Drift
        if: steps.drift.outputs.drift == 'true'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const drift = fs.readFileSync('drift.txt', 'utf8');

            // Check for existing drift issue
            const issues = await github.rest.issues.listForRepo({
              owner: context.repo.owner,
              repo: context.repo.repo,
              labels: 'policy-drift',
              state: 'open'
            });

            if (issues.data.length === 0) {
              github.rest.issues.create({
                owner: context.repo.owner,
                repo: context.repo.repo,
                title: 'Policy Drift Detected',
                body: `Policies in Fulcrum have drifted from git.\n\n\`\`\`\n${drift}\n\`\`\``,
                labels: ['policy-drift']
              });
            }

Complete Example

Here's a complete repository structure with all workflows:

.
├── .github/
│   └── workflows/
│       ├── policy-validate.yml    # Validate on PR
│       ├── policy-deploy.yml      # Deploy on merge
│       └── policy-drift.yml       # Scheduled drift check
├── policies/
│   ├── production/
│   │   ├── budget-limits.yaml
│   │   └── rate-limits.yaml
│   ├── staging/
│   │   └── test-policies.yaml
│   └── shared/
│       └── content-filters.yaml
└── README.md

Secrets Configuration

Configure these secrets in your GitHub repository settings:

Secret Description
FULCRUM_API_KEY API key for Fulcrum access
FULCRUM_API_URL Fulcrum API URL (e.g., https://api.fulcrumlayer.io)

For multi-environment setups:

Secret Description
FULCRUM_API_KEY_STAGING Staging environment API key
FULCRUM_API_URL_STAGING Staging API URL
FULCRUM_API_KEY_PRODUCTION Production environment API key
FULCRUM_API_URL_PRODUCTION Production API URL

Best Practices

  1. Use environments for production deployments with required reviewers
  2. Validate strictly (--strict) in CI to catch warnings
  3. Comment diffs on PRs for easy review
  4. Monitor for drift to detect out-of-band changes
  5. Keep secrets secure using GitHub encrypted secrets
  6. Use branch protection to require validation before merge

Troubleshooting

CLI Not Found

Ensure the CLI is in PATH:

- name: Install CLI
  run: |
    curl -sSL https://get.fulcrumlayer.io/cli | bash
    echo "$HOME/.fulcrum/bin" >> $GITHUB_PATH

Authentication Failed

Check that secrets are configured correctly:

- name: Debug Auth
  env:
    FULCRUM_API_KEY: ${{ secrets.FULCRUM_API_KEY }}
  run: |
    echo "API Key length: ${#FULCRUM_API_KEY}"
    fulcrum auth status

Validation Fails but Works Locally

Ensure you're using --strict mode locally to match CI:

fulcrum policies validate --strict ./policies/