Terragrunt hcl fmt 命令完全指南:递归格式化 HCL 文件的原理与实战
【免费下载链接】terragruntTerragrunt is a flexible orchestration tool that allows Infrastructure as Code written in OpenTofu/Terraform to scale.项目地址: https://gitcode.com/GitHub_Trending/te/terragrunt
导读
terragrunt hcl fmt是 Terragrunt 提供的 HCL 格式化命令,用于递归查找工作目录下所有 HashiCorp Configuration Language(HCL)文件,并将其重写为 HashiCorp 官方推荐的规范化格式。本文以 Terragrunt 仓库中该命令的官方文档定义(commands 数据文件、flag 定义文件)为骨架,结合底层实现源码(format.go、cli.go)展开讲解,读完你能够掌握该命令的全部参数、环境变量、退出码约定、CI/CD 集成方式,以及其“先语法校验、再并行格式化”的底层实现原理。
一、命令概览与定位
按照 Terragrunt 官方文档的定义(见 hcl/fmt.mdx),hcl fmt命令属于configuration类别,官方描述为:
Recursively find HashiCorp Configuration Language (HCL) files and rewrite them into a canonical format.
其核心定位有两个关键词:
- Recursively(递归):默认行为是在工作目录下递归遍历目录树,找出所有
.hcl文件; - Canonical format(规范化格式):重写遵循 HashiCorp 官方语言风格指南,即与
terraform fmt/tofu fmt完全一致的格式化风格。
在命令行中,hcl fmt是hcl format子命令的别名。源码 cli.go 中定义:
const ( CommandName = "format" CommandNameAlias = "fmt" )二、基本用法
最简单也是最常用的形式,是在 Terragrunt 配置目录下直接执行:
terragrunt hcl fmt该命令会从当前工作目录(opts.WorkingDir)出发,递归遍历整个目录树,对每个.hcl后缀文件执行格式化并写回原文件。从源码 format.go 的遍历逻辑看,其默认行为包括:
- 仅处理以
.hcl结尾的文件(strings.HasSuffix(path, ".hcl")); - 目录本身会被跳过,只收集文件;
- 存在一组默认排除目录(详见下文第五节)。
三、命令行参数详解
hcl fmt的全部专属参数、环境变量与说明,集中定义在 cli.go 与 docs/src/data/flags 目录下的 flag 文档中。下面逐一讲解。
1.--check:检查模式(只检查不修改)
| 属性 | 值 |
|---|---|
| 类型 | bool |
| 环境变量 | TG_CHECK(旧变量:TG_HCLFMT_CHECK、TERRAGRUNT_CHECK) |
开启后,Terragrunt 只检查 HCL 文件是否已正确格式化,不会做任何修改,非常适合在 CI/CD 流水线中强制格式一致性:
- 所有文件均已正确格式化 → 退出码
0; - 存在需要格式化的文件 → 退出码
1。
terragrunt hcl fmt --check从源码看,--check会复用格式化后的内容做字节级比较(!bytes.Equal(newContents, contents)),一旦发现差异便返回FileNeedsFormattingError(见 format.go、errors.go),由上层汇总为退出码1。
2.--diff:打印差异(不改写文件)
| 属性 | 值 |
|---|---|
| 类型 | bool |
| 环境变量 | TG_DIFF(旧变量:TG_HCLFMT_DIFF、TERRAGRUNT_DIFF) |
开启后,Terragrunt 会打印原始版本与格式化版本之间的差异(unified diff),让你在真正改写之前预览格式化器将要做的改动:
terragrunt hcl fmt --diff内部实现基于github.com/rogpeppe/go-internal/diff生成 unified diff(见 format.go),diff 标签统一使用斜杠分隔路径,以保证跨平台(含 Windows)输出一致。
3.--exclude-dir:排除指定目录
| 属性 | 值 |
|---|---|
| 类型 | string(可多次指定) |
| 环境变量 | TG_EXCLUDE_DIR(旧变量:TG_HCLFMT_EXCLUDE_DIR、TERRAGRUNT_EXCLUDE_DIR) |
跳过给定目录中的 HCL 文件,适合排除vendor、.terragrunt-cache等不需要格式化的目录:
terragrunt hcl fmt --exclude-dir=vendor --exclude-dir=.terragrunt-cache注意两点:
- 匹配依据是目录的 basename(
slices.Contains(opts.HclExclude, basename),见 format.go),因此--exclude-dir=vendor会排除目录树中任意层级名为vendor的目录; - 该参数与默认排除目录(第五节)是叠加生效的。
4.--file:格式化单个文件
| 属性 | 值 |
|---|---|
| 类型 | string |
| 环境变量 | TG_FILE(旧变量:TG_HCLFMT_FILE、TERRAGRUNT_HCLFMT_FILE) |
指定单个 HCL 文件进行格式化,替代默认的递归搜索:
terragrunt hcl fmt --file=./environments/prod/terragrunt.hcl相对路径会基于工作目录解析为绝对路径(见 format.go)。同时指定--file与--stdin会直接报错:“both stdin and path flags are specified”。
5.--filter:基于路径的过滤查询
| 属性 | 值 |
|---|---|
| 类型 | list(string) |
| 说明 | 对hcl fmt而言只支持路径型过滤表达式 |
--filter在hcl fmt中的行为与其它命令不同:它过滤的是单个 HCL 文件而非 unit/stack(目录级单位),因此只支持基于路径的过滤表达式;type=unit、name=my-app这类基于属性的过滤器在此处不可用(见 hcl-fmt-filter.mdx)。
路径匹配(支持 glob):
# 相对路径 + glob terragrunt hcl fmt --filter './envs/prod/**' # 格式化特定目录下所有 HCL 文件 terragrunt hcl fmt --filter './modules/**/*.hcl' # 绝对路径 terragrunt hcl fmt --filter '/absolute/path/to/envs/dev/apps/*'取反(!前缀):
terragrunt hcl fmt --filter '!./prod/**' terragrunt hcl fmt --filter '!./test/**'交集 / 精化(|运算符):
# 格式化 prod 目录下的 HCL 文件,但排除其中的测试 terragrunt hcl fmt --filter './prod/** | !./prod/test/**' # 格式化所有 HCL 文件,排除 test 目录 terragrunt hcl fmt --filter './**/*.hcl | !./**/test/**'并集(多个--filter,OR 逻辑):
# 同时格式化 dev 与 staging 两个目录 terragrunt hcl fmt --filter './dev/**' --filter './staging/**'在源码层面,收集到全部.hcl文件后,会调用filters.EvaluateOnFiles(l, files, workingDir)对文件列表求值(见 format.go)。更全面的过滤器语法可参考 Filters 功能文档。
6.--stdin:从标准输入读取并输出到标准输出
| 属性 | 值 |
|---|---|
| 类型 | bool |
| 环境变量 | TG_STDIN(旧变量:TG_HCLFMT_STDIN、TERRAGRUNT_HCLFMT_STDIN) |
从标准输入读取 HCL 内容、格式化后写到标准输出,非常适合与编辑器或其他工具集成:
echo 'locals { foo="bar" }' | terragrunt hcl fmt --stdin与--check/--diff组合时行为不同(见 hcl-fmt-stdin.mdx):
- 组合
--check:不打印格式化内容,输入需要格式化时退出码为1; - 组合
--diff:不打印格式化内容,打印标签为old/stdin与new/stdin的 unified diff; - 单独使用:格式化后的完整内容写入标准输出。
对应实现见 format.go,其中--check返回FileNeedsFormattingError{Path: stdinPath}。
7. 队列与并发参数
除上述专属参数外,hcl fmt还挂载了一组共享参数(见 cli.go):
--parallelism:控制同时格式化的文件数量上限(详见第五节);- 队列类参数:
--queue-exclude-dir、--queue-exclude-external、--queue-excludes-file、--queue-ignore-dag-order、--queue-ignore-errors、--queue-include-dir、--queue-include-external、--queue-include-units-reading、--queue-strict-include,用于精细控制遍历范围与错误容忍行为。
四、环境变量速查表
所有专属 flag 均可通过环境变量设置,旧版TG_HCLFMT_*/TERRAGRUNT_*变量仍受支持但已标记废弃(flags.WithDeprecatedEnvVars,见 cli.go):
| 参数 | 新环境变量 | 旧环境变量 |
|---|---|---|
--check | TG_CHECK | TG_HCLFMT_CHECK、TERRAGRUNT_CHECK |
--diff | TG_DIFF | TG_HCLFMT_DIFF、TERRAGRUNT_DIFF |
--exclude-dir | TG_EXCLUDE_DIR | TG_HCLFMT_EXCLUDE_DIR、TERRAGRUNT_EXCLUDE_DIR |
--file | TG_FILE | TG_HCLFMT_FILE、TERRAGRUNT_HCLFMT_FILE |
--stdin | TG_STDIN | TG_HCLFMT_STDIN、TERRAGRUNT_HCLFMT_STDIN |
例如在 CI 中可写作:
TG_CHECK=true terragrunt hcl fmt五、底层实现原理
1. 默认排除目录
即使不指定--exclude-dir,遍历时也会默认跳过三类目录(见 format.go):
var excludePaths = []string{ util.TerragruntCacheDir, // .terragrunt-cache util.DefaultBoilerplateDir, // 模板脚手架相关目录 config.StackDir, // stacks 目录 }匹配发生在vfs.WalkDir回调中,命中后直接返回fs.SkipDir剪枝,避免无谓下钻(见 format.go)。
2. 格式化内核:官方 hcl2 库
格式化本身依赖 HashiCorp 官方库github.com/hashicorp/hcl/v2/hclwrite,核心只有一行:
newContents := hclwrite.Format(contents)且格式化前会先用hclparse.NewParser().ParseHCL做一次语法解析(checkErrors,见 format.go),保证“先确认无语法错误、再落盘改写”——解析报错的坏文件不会被盲目改写。只有当新旧内容字节级不一致(fileUpdated)时才写回文件,并保留原文件权限位(info.Mode(),见 format.go)。
3. 并发格式化与 worker 上限
文件级格式化通过errgroup并行执行。worker 数由formatWorkers决定(见 format.go):
- 显式指定
--parallelism时按用户给定值执行; - 未指定时取
min(runtime.GOMAXPROCS(0), 8),即机器核数与 8 之间的较小值。仓库注释说明:实验测得的良好上限是 8 个 worker(即使 16 核机器也是如此),并注明“可能仍需调优”。
错误收集采用预分配切片 +errors.Join汇总的方式,避免并发追加的锁竞争(见 format.go)。
4. 与其他功能的联动
hcl fmt的格式化逻辑还被scaffold命令复用:RunForFiles支持只格式化调用方显式指定的文件列表(相对/绝对路径均可),用于 boilerplate 生成后仅格式化新增文件(见 format.go)。仓库的 format_test.go 提供了覆盖上述行为的测试用例,可作为深入研读的起点。
六、CI/CD 集成实战
场景一:格式门禁(check 模式)
在提交前强制代码格式一致,失败即阻断流水线:
#!/usr/bin/env bash set -euo pipefail terragrunt hcl fmt --check场景二:仅格式化变更目录(filter + exclude-dir)
terragrunt hcl fmt --filter './environments/prod/**' --exclude-dir=.terragrunt-cache场景三:编辑器集成(stdin)
通过--stdin可将任何编辑器选中的 HCL 文本就地格式化:
# 编辑器插件中:取选中文本 -> 管道输入 -> 回填输出 printf 'resource "a" "b" { count = 1 }' | terragrunt hcl fmt --stdin七、使用注意事项
- 属性过滤不可用:
hcl fmt --filter 'type=unit'不会生效,过滤仅限路径表达式; --stdin与--file互斥:同时指定会直接报错;--check/--diff与--stdin组合时不会输出格式化内容,只输出退出码或 diff;- 坏文件不会被改写:存在语法错误的 HCL 文件会先被解析器拦截并报错;
- 默认会跳过缓存与脚手架目录,如需强制格式化其中的文件,需自行调整目录结构或改用
--file显式指定。
综上,terragrunt hcl fmt在 Terragrunt 中承担着“配置即代码”的格式统一职责:它以官方hclwrite为内核、以并发遍历为手段,配合--check、--diff、--filter、--stdin等参数,即可在本地、CI 流水线与编辑器集成等场景中无缝复用。
【免费下载链接】terragruntTerragrunt is a flexible orchestration tool that allows Infrastructure as Code written in OpenTofu/Terraform to scale.项目地址: https://gitcode.com/GitHub_Trending/te/terragrunt
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考