关于 SupaForge
SupaForge 是用于比较和同步 Supabase 环境的命令行工具,面向维护开发、生产等项目的团队。它覆盖数据库之外的项目配置,并可先在临时副本验证修复。
主要功能
- 01比较架构、RLS、角色与授权
- 02检查存储、认证及 Edge Functions
- 03按依赖顺序事务式应用修复
- 04预览变更并验证修复结果
- 05快照、恢复、克隆和迁移管理
适用场景
- 01发现开发与生产环境的配置漂移
- 02验证修复后再更新目标环境
- 03将远程数据库克隆到本地
- 04通过 CI 检查并输出机器可读结果
MCP 相关
README 提到 SupaForge 可供 AI Agent 使用,并提供编辑器 MCP 服务。该服务可让 Agent 使用其环境比较与同步能力。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
5收录的工具列表 · 更新于 1 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| skip | array | 否 | 跳过指定检查,等同于 CLI 的 --skip 标志。此项会与配置文件中的 checks.exclude 合并。无需修改项目配置,即可用它避开耗时或失败的层。 |
| checks | array | 否 | 将扫描限制为指定检查(例如,["rls", "rls-coverage"])。 |
| source | string | 否 | 源环境名称(覆盖配置默认值)。 |
| target | string | 否 | 目标环境名称(覆盖配置默认值)。 |
| configPath | string | 否 | supaforge.config.json 的绝对路径。除非服务器以 --allow-config-path 启动,否则会拒绝此路径;此时将使用服务器自身工作目录中的配置。 |
详细介绍
supaforge
对比并同步你的 Supabase 环境。
SupaForge 会对比两个 Supabase 项目(例如开发和生产环境),涵盖构成项目的所有内容,而不仅仅是架构。它会报告每一处差异,并使目标环境与源环境一致。修复操作会按依赖顺序在一个事务中执行,而且可以先在临时副本上验证,不会提前更改任何内容。只有一个项目时,它可以为项目创建快照、恢复快照并克隆项目。
- 完整覆盖整个项目。 涵盖架构、RLS、存储、身份验证、Edge Functions、定时任务、webhook、Realtime、队列、分区集、Vault、扩展、授权、参考数据和迁移历史。
- 默认安全。 每次写入都需要
--apply,任何会导致数据丢失或开放访问权限的操作还需要--allow-destructive。应用失败时,目标环境会保持原样。 - 考虑生产环境。 每项修复都会说明其对线上数据库的影响:会持有哪个表上的锁、是否可能重写表或扫描所有行,以及能否在事务中运行。
- 可验证。
--prove会预演迁移,并拒绝应用无法重现源环境或会导致行丢失的迁移。 - 随处可用。 可用于 Supabase Cloud、自托管部署、普通 PostgreSQL 目标、CI 管道和 AI Agent(MCP)。
由 Akal 构建。
快速开始
npm install -g @akal/supaforge
supaforge init # create supaforge.config.json
supaforge diff # what has drifted?
supaforge diff --detail # with the SQL
supaforge diff --apply # fix the target
所有写入操作都会先预览。添加 --apply 即可执行。
安装
| npm | npm install -g @akal/supaforge,或通过 npx @akal/supaforge 运行 |
| Homebrew | brew install akalsoftware/tap/supaforge |
| Docker | docker run --rm -v "$PWD:/work" ghcr.io/akalsoftware/supaforge diff |
| GitHub Actions | akalsoftware/supaforge-action — 参见 CI |
| VS Code 及其分支版本 | SupaForge 扩展:提供命令,以及供编辑器 Agent 使用的 MCP 服务。每个版本都包含一个 .vsix 文件 |
| Cursor、VS Code、Claude | 一键安装 MCP 服务 — 参见 MCP |
需要 Node.js 20 或更高版本。其差异引擎 DBDiff 会以适用于你平台的原生二进制文件随附,因此无需另行安装。Docker 镜像无需任何额外依赖,并包含 --prove 所用的 PostgreSQL 客户端工具;通过 -e 传入连接字符串,并将项目挂载到 /work。
对比内容
| 检查项 | 读取内容 | 修复方式 |
|---|---|---|
| 架构 | @dbdiff/cli:你拥有的所有架构 | SQL |
| 参考数据 | checks.data.tables 中各表的行 | SQL |
| RLS 策略 | pg_policies,按含义而非文本进行比较 | SQL |
| RLS 覆盖情况 | 目标数据库中已关闭 RLS 的表(仅目标数据库) | SQL,需选择启用 |
| Edge Functions | Management API、Studio 或函数目录 | 部署命令 |
| 存储 | 存储桶及其策略(不会复制对象) | API 和 SQL |
| Auth 配置 | Management API,或自托管环境中的 GoTrue | API(托管环境) |
| Cron 任务 | cron.job | SQL |
| 队列 | Supabase Queues(pgmq.meta),通过 pgmq 创建和删除 | SQL |
| 分区集 | pg_partman 注册项(part_config),通过 create_parent 创建 | SQL |
| Webhook | 调用 supabase_functions.http_request 的触发器 | SQL |
| Realtime | 发布,以及 realtime.messages 上的策略 | SQL |
| Vault | 密钥名称和描述(无法读取值) | 要运行的命令 |
| 扩展 | pg_extension | SQL |
| 迁移历史 | 本地迁移文件与 supabase_migrations 的对比(仅目标数据库) | SQL,需选择启用 |
| 角色和授权 | 自定义角色,以及对表、视图、序列和函数的授权,包括 anon、authenticated、service_role | SQL |
架构检查涵盖表、列、键、索引、分区、视图、物化视图、函数、聚合函数、触发器、事件触发器、规则、序列、枚举、复合类型和范围类型、域、类型转换、扩展统计信息、文本搜索配置和字典、外部服务器、用户映射和外部表、策略、默认权限、复制标识、统计目标、扩展和注释。重命名的列、表或序列会被重命名,并保留其中的行,而不是先删除再重新创建。检查会在除 Supabase 自有架构(auth、storage 等)和 ignoreSchemas 中列出的架构以外的所有架构中运行。由扩展拥有的对象归属于该扩展,不会进行比较。不比较的对象类型(例如操作符类)会列在检查项下,供你手动查看;它们不会计为差异。
无法运行的检查(例如缺少 API 凭据时)会说明这一点及原因,绝不会报告为检查通过。
命令
supaforge init Create a config interactively
supaforge diff Report drift (alias: hukam)
supaforge sync Alias for diff --apply
supaforge snapshot --apply Capture an environment's structure and configuration
supaforge restore --apply Rebuild a database from a snapshot or migrations
supaforge clone --apply Copy a remote database to a local one
supaforge migrate create Write a migration file from the current drift
supaforge migrate list Local migrations, applied and pending
supaforge migrate run --apply Run pending migrations
supaforge migrate baseline --apply Mark migrations applied without running them
supaforge report Recent runs, from a local log
supaforge mcp MCP server for AI agents
supaforge help <command> Every flag, e.g. help diff
实用的 diff 标志:
| 标志 | 作用 |
|---|---|
--check=rls / --skip=storage | 仅运行一项检查,或跳过部分检查(--skip 可重复使用) |
--tables=orders,items / --exclude-tables='*_log' | 限定架构和数据检查的范围 |
--only=<id,...> | 仅应用这些发现项(ID 来自 --json,支持 glob) |
--dry-run | 按运行顺序打印修复项,但不运行 |
--prove | 先在一次性副本上演练修复 |
--source-dir <dir> | 将架构文件目录(如 supabase/schemas)与目标数据库进行比较,而非与源数据库比较 |
--allow-destructive | 同时应用删除和删行操作 |
--apply-posture | 同时应用仅目标数据库检查的修复 |
--no-transaction | 逐项应用修复,保留成功的部分 |
--no-snapshot | 单独读取每条查询。默认情况下,每个数据库的数据都读取自同一时刻,因此扫描期间所做的更改会被全部读取或完全不被读取;与 pg_dump 一样,扫描结束前,读取到的数据会保持锁定 |
--json / --ci | 输出机器可读内容;提供 CI 注解和退出代码 |
安全地应用修复
- 先预览。
diff、snapshot、restore、clone和migrate run在未指定--apply时不会写入任何内容。 - 破坏性修复需要额外的标志。 删除架构、表或列,删除或截断行,以及移除 RLS 策略都会报告,但在添加
--allow-destructive前会跳过。migrate run使用相同的门槛。 - 依赖顺序。 修复按依赖顺序执行:先处理函数,再处理调用它们的触发器;先处理列,再处理其索引;最后执行删除。需要删除并重建的变更对象会作为单个修复处理。
- 单个事务。 如果某项修复失败,整组修复都会回滚,目标端保持不变。若目标端无法承载某项修复(例如需要目标端缺少的扩展或 Supabase 架构),该修复会跳过并说明原因,其余修复仍会应用。
--prove。 修复会按照实际应用时的方式,在目标端自己的服务器上,对目标结构的副本进行重放,并抽取每张表的部分行作为样本。只有当结果与源端匹配且行数据保留时,才会应用任何修复;如果验证无法运行,则不会应用任何修复。它还会列出修复会重写的表。副本无法承载的变更(例如 cron 作业,因为 pg_cron 在每台服务器上只存在于一个数据库中)会列为未经验证。
supaforge diff --dry-run # the plan, in order
supaforge diff --apply --prove # rehearse, then apply
supaforge diff --apply --only='schema-create-*'
安全态势检查
RLS 覆盖情况和迁移历史只检查目标端,因此无论对哪一对对象运行 diff,报告的结果都相同。它们单独评分(Posture score);除非传入 --fail-on-posture,否则不会影响退出代码。只有使用 --apply-posture 应用修复,或像 --check=rls-coverage --apply 这样指定检查项时,修复才会应用。盲目应用这些修复会使目标端偏离源端。
快照、恢复和克隆
supaforge snapshot --env=prod --apply # capture
supaforge snapshot --env=prod --migration --apply # and write what changed since the last one
supaforge restore --env=local --from-snapshot=latest --apply
supaforge clone --env=prod --apply # local copy, data included
快照记录项目的结构和配置:架构、RLS 策略、cron、webhook、扩展、存储、身份验证、Edge Functions、参考数据、Realtime、Vault 名称和授权。它不是备份。 除了 checks.data.tables 中的数据外,它不包含任何行,也不包含存储的文件。其架构由 pg_dump 写入为 SQL,而 pg_dump 的版本必须不低于服务器版本;没有 pg_dump 时,仍可对快照进行差异比较,但无法恢复;除非 --allow-schema-json-only 指明仅有 JSON 架构也足够,否则 snapshot 会以 1 退出。
restore --from-snapshot 会还原:
| 已还原 | 说明 |
|---|---|
| 扩展、架构、RLS 策略、cron 作业、webhook、Realtime 发布、授权 | 以 SQL 形式按依赖顺序还原。策略或授权中提及的角色会先创建 |
| 存储策略和存储桶 | 目标存在 storage 架构时会创建存储桶行;不会还原文件 |
| 参考数据 | checks.data.tables 中的行,按先父表后子表的顺序还原,并将序列调整到大于已还原的 ID |
它无法还原身份验证配置、Edge Functions(使用 supabase functions deploy 部署)或 Vault 密钥值。输出中会列出这些内容。
- 目标。 恢复会作为单个事务运行,并要求数据库为空。
--force会先清除快照自身的架构(绝不会清除 Supabase 的架构),然后还原依赖于已清除内容且位于这些架构之外的触发器和策略。如果恢复或应用操作很可能需要的锁超过目标在单个事务中允许的数量,程序会在开始前说明;使用--no-transaction则会在执行过程中逐步提交。 - 原生 PostgreSQL。 可以还原到原生 PostgreSQL。无法在其中存在的内容会被跳过并列出:指向
auth.users的外键、webhook、服务器未提供的扩展,以及依赖这些内容的一切。 - 退出码。 如果恢复时遗漏了任何内容,则以 1 退出。拒绝执行时也会如此。
- JSON。
--json只输出 JSON,预览时也一样。
clone 使用 pg_dump 和 pg_restore 将远程数据库复制到本地服务器(使用 --schema-only 可不复制数据行)。会先安装本地服务器已有的扩展。无法还原的对象会按原因列出。之后,配置中的 target 会指向克隆,因此不带参数的 diff --apply 会写入克隆,而不是远程数据库。
迁移
supaforge migrate create --name=add_orders # from the drift between source and target
supaforge migrate run --env=prod # preview
supaforge migrate run --env=prod --apply
- 文件和历史记录。 文件存放在
supabase/migrations中(设置checks.migrations.dir可更改此目录)。已应用的版本记录在 Supabase CLI 的表supabase_migrations.schema_migrations中。 - 破坏性语句。
migrate create会列出它写入的所有破坏性语句,migrate run需要--allow-destructive才能运行这些语句。 - 历史记录表权限。 在自托管堆栈中,历史记录表可能属于管理员角色。此时,SupaForge 会指出所需的
GRANT,而不是只报一个权限错误。 - 未跟踪历史记录的项目。
checks.migrations.mode决定迁移历史记录检查如何报告这些项目:auto(没有跟踪任何内容时提示一次)、warn或ignore。
配置
{
"environments": {
"dev": { "dbUrl": "$DEV_DATABASE_URL", "projectRef": "abc123", "accessToken": "$SUPABASE_ACCESS_TOKEN" },
"prod": { "dbUrl": "$PROD_DATABASE_URL", "projectRef": "xyz789", "accessToken": "$SUPABASE_ACCESS_TOKEN" }
},
"source": "dev",
"target": "prod",
"checks": {
"data": { "tables": ["plans", "feature_flags"] },
"exclude": ["edge-functions"]
}
}
| 字段 | 含义 |
|---|---|
dbUrl | PostgreSQL 连接字符串。$VAR 和 ${VAR} 从环境中读取 |
projectRef, accessToken | 启用 Management API 检查(身份验证、Edge Functions) |
apiUrl | 自托管 API 网关,用来代替 projectRef |
studioUrl, functionsPath | 自托管 Edge Functions 的读取位置 |
source, target | 比较对象:以 source 为准,修正 target |
ignoreSchemas | 要排除的自有 Schema |
checks.data.tables | 要比较行数据并创建快照的表 |
checks.exclude | 永不运行的检查(与 --skip 合并) |
checks.tables, checks.excludeTables | Schema 和数据检查的默认范围 |
checks.migrations.dir, checks.migrations.mode | 迁移文件所在位置,以及历史记录检查的报告方式 |
- 按环境配置。
checks.exclude和checks.schema.timeout也可以设在环境级别,并在该环境作为 target 时生效。 .env文件。.env文件按 Next.js 的方式加载:.env.{NODE_ENV}.local、.env.local、.env.{NODE_ENV}、.env。环境中已有的值优先。- 单个数据库。 只有一个环境且没有
source/target的配置也可以使用。使用snapshot、clone和restore。
| 环境变量 | 默认值 | 用途 |
|---|---|---|
SUPAFORGE_DBDIFF_TIMEOUT | 600 | Schema 或数据差异比较放弃前等待的秒数 |
SUPAFORGE_DBDIFF_MEMORY | 1G | @dbdiff/cli 的内存限制(2G,-1 表示不限制) |
SUPAFORGE_CONNECT_TIMEOUT | 15 | 等待连接的秒数 |
SUPAFORGE_CHECK_CONCURRENCY | 4 | 同时运行的检查数量。连接池资源紧张时请调低 |
SUPAFORGE_PROVE_ROWS | 1000 | --prove 从每个表复制以检查能否保留的数据行数;0 仅检查结构 |
SUPAFORGE_PG_BIN | — | PostgreSQL 客户端工具(pg_dump、psql)所在目录;优先于 PATH 使用,适用于服务器版本比 PATH 中工具更新的情况 |
自托管 Supabase
将 apiUrl 设为网关地址,将 accessToken 设为 service-role 密钥。所有检查都会运行。
- Edge Functions。 自托管环境没有用于 Edge Functions 的 Management API 端点,因此请设置
studioUrl(Studio 的端口,而非网关端口)或functionsPath(挂载的 Functions 目录)。两个设置可以在不同环境中混用。Studio 的 Functions API 无需身份验证,因此不要公开暴露。 - Auth 配置。 在自托管环境中,Auth 配置从 GoTrue 的设置端点读取;该端点返回的键较少,且无法写入。请改为更改部署环境。
比较克隆
本地克隆是纯 PostgreSQL。Storage、Auth、Edge Functions、Vault、Realtime 和 Supabase 的角色在其中都无可比较之处,因此请跳过这些检查:
{ "checks": { "exclude": ["storage", "auth", "edge-functions", "vault", "realtime", "roles"] } }
当 source 是克隆而 target 不是时,diff 会警告:--apply 会将克隆的结构(包括缺失的对象)推送到 target。
CI 和退出代码
GitHub Action 会运行
diff --ci,并在作业页面上添加差异摘要:
- uses: akalsoftware/supaforge-action@v1
env:
DEV_DATABASE_URL: ${{ secrets.DEV_DATABASE_URL }}
PROD_DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
在其他地方,请运行 CLI:
npx @akal/supaforge diff --ci
| 代码 | 含义 |
|---|---|
0 | 完成了所要求的操作,包括无事可做的情况 |
1 | 发现差异、拒绝执行操作,或发生故障或有内容未处理 |
2 | 无法运行:用法错误,或在 --ci 模式下有检查未能完成 |
默认情况下,--ci 在发现严重差异时失败。使用
--fail-on=warning 或 --fail-on=any 设置阈值。
MCP(AI Agent)
SupaForge 以 MCP 服务的形式运行,因此编辑器中的 AI Agent 可以扫描差异并修复,同时会在写入任何内容前征求你的同意。
-
Claude Desktop:下载适用于你平台的扩展(
supaforge-<version>-<platform>.mcpb),可在最新版本中找到,然后打开。它会要求你指定项目目录。 -
Claude Code:安装插件,它会添加 MCP 服务和一项用于安全应用修复的技能:
/plugin marketplace add akalsoftware/claude-plugins /plugin install supaforge@akalsoftware或者仅添加服务:
claude mcp add supaforge -- npx -y @akal/supaforge mcp -
其他客户端:
{ "mcpServers": { "supaforge": { "command": "npx", "args": ["-y", "@akal/supaforge", "mcp", "--project", "/path/to/project"] } } }
它已列入 MCP Registry,标识为 io.github.akalsoftware/supaforge。
该服务提供以下工具:
| 工具 | 功能 | 注释 |
|---|---|---|
list_environments | 配置中的环境,不包含其凭据 | 只读 |
diff | 所有差异,以及 sync 将执行的计划 | 只读 |
get_check_result | 上次 diff 或 sync 中某项检查的结果 | 只读 |
sync | 在目标上执行计划;prove: true 会先在副本上预演 | 具有破坏性 |
create_migration | 将差异写入迁移文件,与 migrate create 的行为相同 | 写入文件 |
take_snapshot | 将环境快照保存到本地文件 | 写入文件 |
diff 和 sync 使用相同的参数,因此 Agent 向你展示的计划就是实际执行的计划。除非设置了 allowDestructive,否则会阻止删除表或列的修复。
它会读取 --project 指定目录(或启动时所在目录)中的 supaforge.config.json;除非使用 --allow-config-path 启动,否则会拒绝客户端传入的 configPath。
钩子
import { HookBus, scan, createDefaultRegistry, loadConfig } from '@akal/supaforge'
const bus = new HookBus()
bus.on('supaforge.check.after', ({ check, result }) => {
if (result.status === 'drifted') console.log(`Drift in ${check}`)
})
await scan(createDefaultRegistry(), { config: await loadConfig() }, bus)
开发
cd packages/cli
npm install
npm test # unit tests
npm run lint # type-check
./bin/dev.js diff # run from source
| 测试套件 | 命令 | 依赖项 |
|---|---|---|
| 集成测试 | npm run test:integration | Docker 或 Podman |
| 数据库端到端测试 | npm run test:e2e | Docker 或 Podman |
| 场景测试 | npx vitest run -c vitest.scenarios.config.ts | Docker 或 Podman、pg_dump |
| Supabase 端到端测试 | npm run test:e2e:supabase | Supabase CLI |
| 候选发布版本 | scripts/test-release-candidates.sh --dbdiff ../DBDiff --pg-conformance ../pg-conformance | Podman 或 Docker |
候选发布版本脚本会像正式发布时那样,从本地检出版本构建 DBDiff 二进制文件和 npm 软件包,将 SupaForge 安装到这些版本上(模拟用户操作),并在该安装上运行上述所有测试套件。它会在这三个项目中的任何一个发布之前,测试涉及它们的变更。
场景测试套件会对 @akal/pg-conformance 测试集中的每个案例执行双向迁移,并根据测试集自身的状态判定器以及 --prove 检查结果。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。