登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go Goose 迁移中的环境变量为什么没有被自动替换

来源:17golang原创

时间:2026-10-29 13:49:17 166浏览 收藏

Goose 里“环境变量没有被替换”通常不是变量名写错,而是把两套机制混在了一起:.env 和 GOOSE_* 负责给 Goose CLI 提供驱动、连接串和迁移目录;SQL 文件里的 ${VAR} 替换则默认关闭,必须用 -- +goose ENVSUB ON 显式打开。

官方地址:https://github.com/pressly/goose

要点速览
  • GOOSE_DRIVER、GOOSE_DBSTRING、GOOSE_MIGRATION_DIR 是 CLI 配置,不会自动开启 SQL 文本替换。
  • Goose 默认读取 .env;使用 -env=none 会关闭加载,使用 -env=路径 可指定文件。
  • SQL 迁移要在需要的语句前写 -- +goose ENVSUB ON,结束后写 -- +goose ENVSUB OFF。

先区分两套环境变量机制

Goose 的配置入口和迁移内容是两个层次。命令行启动时,环境变量可以提供驱动、数据库连接串、迁移目录和版本表名;而 SQL 文件中的占位符替换属于迁移文本处理能力,官方为了兼容旧脚本将它设为默认关闭。

Goose CLI 环境变量与 .env 配置来源关系说明图:进程环境和 .env 汇入 CLI 配置,SQL 替换开关独立存在
图1:Goose CLI 配置来源边界说明图,区分进程环境、.env 文件和 SQL 迁移文本。
配置项作用是否等于 SQL 替换
GOOSE_DRIVER选择数据库驱动不是
GOOSE_DBSTRING提供数据库连接串不是
GOOSE_MIGRATION_DIR指定迁移目录不是
ENVSUB ON开启后续 SQL 的变量替换是

确认 .env 文件确实被加载

Goose CLI 默认从名为 .env 的文件加载环境变量。如果你的文件叫 .env.dev、放在仓库父目录,或者命令启动位置与预期不同,就不要把“文件存在”当成“本次命令一定读到了”。用 -env 明确指定路径更容易在发布脚本中复现。

# .env 文件只放 CLI 配置;敏感连接串不要提交到公共仓库。
GOOSE_DRIVER=postgres
GOOSE_DBSTRING=postgres://migration_user:secret@127.0.0.1:5432/app_db?sslmode=disable
GOOSE_MIGRATION_DIR=./migrations
GOOSE_TABLE=public.goose_db_version

# 默认加载当前命令可见的 .env;自定义文件时显式给出路径。
goose -env=.env.dev status

# 只有在明确不使用 dotenv 时才关闭文件加载。
goose -env=none postgres "$GOOSE_DBSTRING" status

排查时先看配置来源,再看 SQL 内容:是否通过进程环境注入、是否用了正确的 -env 路径、是否意外使用了 -env=none。注意,GOOSE_MIGRATION_DIR 只决定去哪里找迁移文件,它不会让文件里的 ${TABLE_NAME} 自动展开。

在 SQL 迁移中显式开启替换

SQL 替换必须由迁移文件中的 Goose 注释控制。开关打开后持续生效,直到遇到 ENVSUB OFF;没有开关时,占位符不会因为变量已经存在于进程环境里就自动替换。

Goose SQL 迁移 ENVSUB ON/OFF 与占位符作用范围说明图:默认关闭,打开后处理变量、默认值和缺失报错语法
图2:Goose ENVSUB 作用范围说明图,展示 SQL 占位符与显式开关的关系。
-- +goose Up
-- 只在这段 SQL 前打开替换,避免影响不需要替换的内容。
-- +goose ENVSUB ON

-- 变量存在时使用它;缺失时使用 public 作为默认 schema。
CREATE TABLE ${DB_SCHEMA:-public}.${TABLE_NAME} (
    id BIGSERIAL PRIMARY KEY,
    title TEXT NOT NULL
);

-- 这个变量缺失时让迁移报错,而不是静默写入错误对象。
-- ${REQUIRED_REGION?REQUIRED_REGION must be set}

-- +goose ENVSUB OFF

-- 关闭替换后,后续 SQL 中的美元符号按原文保留。
INSERT INTO audit_note(note) VALUES ('migration prepared');

-- +goose Down
DROP TABLE ${DB_SCHEMA:-public}.${TABLE_NAME};

官方支持 ${VAR} 或 $VAR,也支持 ${VAR:-default}、${VAR-default} 和 ${VAR?err_msg}。${VAR:?err_msg} 不在支持范围内;不要只把冒号位置换来换去,发布前应按 Goose 支持的语法统一占位符。

把占位符和发布检查收口

生产迁移建议把“连接配置”和“SQL 替换”分别收口。连接串从部署系统或受保护的环境文件注入;迁移文件只保留明确的占位符和开关边界。这样既能定位变量来源,也不会把敏感值复制到普通日志。

  1. 先确认迁移命令使用的驱动、连接串和目录来源,再确认 -env 是否指向预期文件。
  2. 在 SQL 中搜索 ENVSUB ON 和 ENVSUB OFF,确认每个需要替换的查询都位于作用范围内。
  3. 把必须存在的值改用 ${VAR?err_msg},把可选值才写成带默认值的形式。
  4. 不要把密码、完整连接串或替换后的 SQL 写入调试日志;检查只记录变量名、文件名和缺失原因。

如果迁移仍然看到原样的 ${VAR},优先检查 ENVSUB 标记;如果 Goose 连错数据库或找不到迁移文件,优先检查 GOOSE_* 和 -env。两类问题的修复入口不同,混在一起改只会增加排查范围。

常见问题

设置了 GOOSE_DBSTRING,为什么 SQL 中的 ${TABLE_NAME} 还是原样?

GOOSE_DBSTRING 只给 CLI 提供数据库连接串,不负责 SQL 文本替换。请在迁移语句前加 -- +goose ENVSUB ON,并在不需要替换的位置关闭。

一定要把配置写进 .env 吗?

不一定。可以直接使用进程环境变量,也可以用默认的 .env 或 -env=文件路径 加载;关键是让发布命令明确知道配置来源。

为什么有些 SQL 需要关闭 ENVSUB?

函数体、美元引用或其他包含美元符号的 SQL 可能不希望被插值。把 ENVSUB ON/OFF 缩小到真正需要替换的语句,可以减少旧脚本受到意外改写的风险。

这类问题的判断顺序可以固定为:先查 CLI 配置来源,再查 ENVSUB 作用范围,最后查占位符语法。这样不会把“没有加载 .env”和“没有开启 SQL 替换”误认为同一个故障。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>