文件匹配与 Hooks

filePatterns 的三种匹配模式与 minimatch 规则、after_write Hook 的配置与执行模型、--allow-hooks 授权机制、errorHandling 策略,以及背后的安全设计。

本文讲模板生成流程中两个“精确控制”能力:用 filePatterns 控制哪些文件参与渲染,用 Hooks 在文件写入后执行脚本。两者都在 .agilebuilder.config.yaml 中配置。

filePatterns:文件匹配规则

variables.filePatterns 决定哪些文件的内容会被 EJS 渲染:

variables:
  enabled: true
  filePatterns:
    mode: include
    patterns:
      - "**/*.ts"
      - "**/*.json"
      - "README.md"

三种 mode

mode行为典型场景
all渲染所有非二进制文件。小模板,到处都有变量。
include只渲染匹配任一 pattern 的文件。模板含大量不需要渲染的文档、示例。
exclude渲染未匹配任何 pattern 的文件。大部分文件要渲染,只排除少数目录。

要点:

  • 匹配对象是相对模板根目录的路径,统一使用 / 分隔符(Windows 下也会转换后匹配)。
  • pattern 使用 minimatch 语法,且 dot: true——.github/workflows/ci.yml 这类以点开头的文件路径可以被 ** 匹配到。
  • patterns 为空数组或缺失时按 ["**/*"] 处理;mode 非法时按 all 处理。
  • 不匹配的文件(include 未命中 / exclude 命中)按原样复制,内容不变。
  • 匹配只在 variables.enabled: true 时发生;为 false 时所有文件原样复制。
  • 二进制文件永远不参与渲染,与 mode 无关。

常用 pattern 示例:

pattern匹配
**/*.ts任意深度的 TypeScript 文件
package.json仅根目录的 package.json
src/**src/ 下所有文件
**/*.{ts,tsx}TypeScript 与 TSX 文件

路径渲染不受 filePatterns 影响

filePatterns 只控制文件内容是否渲染。只要 variables.enabled: true,文件路径中的 {{var}} 与 EJS 表达式总会被渲染。

after_write Hook

Hook 在全部文件写入目标目录之后执行。当前只处理 after_write 阶段,且只支持 scriptType: shell

hooks:
  after_write:
    scriptType: shell
    script: npm install
    errorHandling: warn
    env:
      NPM_CONFIG_REGISTRY: https://registry.npmmirror.com
字段默认说明
scriptTypeshell配置里可以写 nodejs / custom,但授权后执行会报 HOOK_TYPE_UNSUPPORTED——目前只执行 shell
script—(必填)目标目录中执行的 shell 命令。
errorHandlingstop失败处理策略,见下文。
env附加环境变量,与当前进程环境合并后传入。

执行模型:

  • 工作目录是 ag create --target 指定的目标目录,因此 npm installgit init 这类命令直接作用于生成结果。
  • Windows 下优先用 bash -c 执行;找不到 bash(例如未安装 Git Bash)时自动回退到系统默认 shell。其他平台直接用系统 shell。
  • 超时 5 分钟:超时后先发送 SIGTERM,5 秒宽限后 SIGKILL,并报 HOOK_TIMEOUT
  • Hook 的 stdout/stderr 直接透传到终端,便于排查。

授权机制:为什么 Hook 默认不执行

模板是从 Git 仓库拉取的第三方代码,而 Hook 是以你的用户权限执行的任意 shell 命令。如果 Hook 默认执行,任何人只要把恶意脚本写进模板的配置文件,就能在生成项目时在你的机器上运行它。因此 AgileBuilder 把 Hook 设计为显式授权

  • 默认不执行:未授权时,after_write 被记录为跳过(hooksSkipped),生成正常完成。
  • 单次授权:ag create ... --allow-hooks,优先级最高。
  • 默认授权:ag config set template.allowHooksDefault true,之后未传 --allow-hooks 时也执行 Hook。只建议在你信任所有模板来源的环境里开启。

未授权时 CLI 不会静默忽略——生成结果的 hooksSkipped 字段会列出被跳过的 Hook,人类输出和 --json 输出中都能看到。

errorHandling:失败处理策略

行为
stop(默认)Hook 失败即中止生成,报 HOOK_FAILED,进程非零退出。适合“Hook 失败等于项目不可用”的场景(如必须完成的依赖安装)。
warn失败记录为跳过(hooksSkipped),生成照常完成。适合锦上添花型脚本(如格式化、初始化提交)。
continuewarn 行为相同。

其他安全设计

生成流程还有几道与 Hook 无关、但同样保护你机器的防线:

  • 目标目录黑名单:拒绝写入盘符根目录(如 C:\)、Unix 根目录、C:\WindowsC:\Program FilesC:\Program Files (x86)C:\Program Files\Git 及其子路径,违规则报 UNSAFE_TARGET_DIR
  • 非空目录保护:目标目录已存在且非空时必须显式传 --overwrite,否则报 TARGET_NOT_EMPTY
  • 子目录逃逸防护--subdirsource.subdir 不允许绝对路径和 .. 段,解析结果必须留在克隆出的模板目录内,否则报 TEMPLATE_SUBDIR_UNSAFE
  • 配置文件不外泄.agilebuilder.config.yaml / .agilebuilder.config.json 不会复制到目标目录。
  • .git 默认跳过:除非传 --keep-git,生成结果不带模板仓库的 Git 历史。

排查建议

  • Hook 没执行?先确认传了 --allow-hooks 或配置了 template.allowHooksDefault: true,再检查生成输出里的 hooksSkipped
  • 变量没渲染?按顺序检查:variables.enabled 是否为 true → 文件是否命中 filePatterns → 文件是否为二进制。
  • 模板仓库里有不想渲染的示例文件(比如文档里的 EJS 片段)?把它们用 exclude 排除,或改用 include 精确圈定渲染范围。

下一步