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 文件中的占位符替换属于迁移文本处理能力,官方为了兼容旧脚本将它设为默认关闭。

| 配置项 | 作用 | 是否等于 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 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 替换”分别收口。连接串从部署系统或受保护的环境文件注入;迁移文件只保留明确的占位符和开关边界。这样既能定位变量来源,也不会把敏感值复制到普通日志。
- 先确认迁移命令使用的驱动、连接串和目录来源,再确认
-env是否指向预期文件。 - 在 SQL 中搜索
ENVSUB ON和ENVSUB OFF,确认每个需要替换的查询都位于作用范围内。 - 把必须存在的值改用
${VAR?err_msg},把可选值才写成带默认值的形式。 - 不要把密码、完整连接串或替换后的 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 替换”误认为同一个故障。
-
477 收藏
-
109 收藏
-
298 收藏
-
192 收藏
-
335 收藏
-
329 收藏
-
494 收藏
-
422 收藏
-
446 收藏
-
402 收藏
-
496 收藏
-
397 收藏
-
389 收藏
-
476 收藏
-
138 收藏
-
229 收藏
-
397 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习