How to Set Up Automated Environment Scans
Catch drift early with CI checks and scheduled scans.
npm install -g @akal/supaforge. A hosted dashboard is on the way - to hear when it opens.Why Automate Scans?
A manual diff catches drift when someone remembers to run it. An automated one catches it the day it appears - especially in teams where changes are made in the Supabase dashboard as well as through migrations.
CI Mode
supaforge diff --ci is built for pipelines:
- Annotations - each finding becomes a GitHub Actions error or warning, on stderr.
- JSON - a machine-readable summary is the only thing on stdout, so
supaforge diff --ci > report.jsongives you a clean artifact. - Exit codes -
0clean,1drift,2a check failed to run. - Threshold -
--fail-on=critical(the default),warningorany.
Credentials
$VAR and ${VAR} in supaforge.config.json are read from the environment, so the config can be committed and the secrets supplied by CI:
{
"environments": {
"staging": {
"dbUrl": "$STAGING_DATABASE_URL",
"projectRef": "STAGING_REF",
"accessToken": "$SUPABASE_ACCESS_TOKEN"
},
"production": {
"dbUrl": "$PRODUCTION_DATABASE_URL",
"projectRef": "PROD_REF",
"accessToken": "$SUPABASE_ACCESS_TOKEN"
}
},
"source": "staging",
"target": "production"
}Option 1 - GitHub Actions
The SupaForge action runs diff --ci: findings become annotations, the job page gets a summary of the drift, and the step fails at your threshold.
name: Drift Check
on:
pull_request:
schedule:
- cron: '0 8 * * *' # daily
permissions:
contents: read
jobs:
diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: akalsoftware/supaforge-action@v1
id: drift
with:
args: diff --ci --fail-on=warning
env:
STAGING_DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
PRODUCTION_DATABASE_URL: ${{ secrets.PRODUCTION_DATABASE_URL }}
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: drift-report
path: ${{ steps.drift.outputs.report }}Its outputs are the exit code and the path of the JSON report; version pins the SupaForge release it runs.
Option 2 - GitLab CI
drift-check:
image: node:22
script:
- npx @akal/supaforge diff --ci --fail-on=warning
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "schedule"Option 3 - Cron + Webhook
Without CI, run it from cron and send the summary wherever your team looks:
# every 6 hours
0 */6 * * * cd /path/to/project && npx @akal/supaforge diff 2>&1 | curl -s -X POST --data-binary @- https://hooks.slack.com/services/YOUR/WEBHOOK/URLReading the Results
- Drift score - out of 100, where 100 means the environments match.
- Posture score - the target on its own: tables without RLS, migrations it hasn't applied. Scored separately, and only part of the exit code with
--fail-on-posture. - Per-check status - drifted, clean, or skipped with the reason. A skipped check is never counted as clean.
- Findings - each with a severity and its fix.
Best Practices
- Diff in the direction changes flow - staging as the source, production as the target.
- Start daily, then gate pull requests once the baseline is clean.
- Scope it when you need speed -
--check,--skipand--tableskeep a pull-request check fast. - Pin the version - the action's
versioninput, or@akal/supaforgein your devDependencies, so CI upgrades when you choose to.
Summary
With --ci, a drift check is one line in a pipeline: annotations for reviewers, JSON for tooling and an exit code that fails the build when the environments diverge.
Ready to try SupaForge?
Install the CLI and run your first diff in a minute. Nothing is changed until you add --apply.
npm install -g @akal/supaforge