🐎 第5章 防微杜渐:Hooks 事件驱动自动化
📄 一句话概括

钩子配置能放在六个地方:企业策略、命令行参数、项目本地、项目共享、用户级、插件。 和技能不一样,钩子是合并的——几处注册同一条规则,它就跑几次。 这件事不报错、不提示,只让你觉得「保存怎么有点慢」。

📝 本章和前面几章的关系

上一节讲事件,这一节讲配置写在哪儿。位置决定「谁看得见、谁改得动、能不能进版本库」。 这一节要和第 3 章 3.8 节对着看:同样是六个位置,技能是覆盖,钩子是合并。

一、六个位置

企业策略:组织统一下发,优先级最高,个人改不了。放合规红线和审计上报。 命令行参数:临时覆盖,这一次会话有效。适合排查问题和临时试验。 项目本地:只在本机生效,不进版本库。适合自己调试时加的钩子。

项目共享:进版本库,团队共用。这是放「项目规矩」的地方。 用户级:跟着人走,跨项目生效。放通用小工具,别放项目相关的东西。 插件:装进来就带着,优先级最低。别人写的,你只是用。

二、合并,不是覆盖

技能同名是整份覆盖,钩子不是。几处注册同一条规则,全部生效——改一个文件会跑三次格式化。 这件事最阴的地方是它不报错、不提示。你只会觉得保存有点慢,而慢的原因是三份格式化和一份没区别。 演示里三处都注册了同一个「改完 py 文件就格式化」:用户级、项目共享、项目本地。 三处分别由三个人维护,谁也不知道另外两处的存在。

「同名」在钩子上还有个更细的规则:看的是「位置加事件加匹配条件」,不是钩子脚本的名字。 所以给脚本改名没用。 这带来一个反直觉的结论:钩子的配置越多,不代表管得越细,可能只是跑得越多。

三、优先级管什么

优先级从企业策略往下递减。它管的是同一个位置内部、同类配置的取舍顺序。 但因为钩子是合并的,优先级在大多数情况下只影响执行顺序,不影响「生不生效」。 唯一会真的「顶掉」的情况,是同一位置里对同一条匹配写了两次不同的配置——那时候后者算数。

这和技能那种「高优先级整份盖掉低优先级」是两套逻辑,混着记一定会错。 记一条就够:技能问「谁赢」,钩子问「跑几次」。

四、摆放建议

企业策略:少而硬。合规红线、必须上报的审计。条数应该个位数。 项目共享:这个项目的格式化、lint、测试门控。所有人都该跑,所以进版本库。 项目本地:临时试验的钩子。不进库,也就不会和别人撞。

用户级:跨项目通用的小工具,比如「每次会话开始打印当前分支」。 一条自查办法:把自己所有的钩子配置按「事件加匹配」排一遍,同一条规则出现两次以上的,删到只剩一处。 合并是特性,重复是事故。前者是你有意让两个不同规则都跑,后者是你忘了自己写过。

五、动手 Demo:六个位置都注册同一条钩子,最后跑几次

先看六个位置和优先级,再看三处注册同一个「格式化」钩子会发生什么。 最后按位置给一份摆放建议。

reg = [
    (u"用户级", u"PostToolUse", u"Edit(*.py)"),
    (u"项目共享", u"PostToolUse", u"Edit(*.py)"),
    (u"项目本地", u"PostToolUse", u"Edit(*.py)"),
]
# 钩子是合并的:三处都生效,改一个文件跑三次

实跑输出:

--------------------------------------------------------------------
六个位置都注册同一条钩子,最后跑几次
--------------------------------------------------------------------

  钩子配置可以放在六个地方。这一节要回答的问题是:多处注册同一条,是合并还是覆盖。

  ① 六个位置与优先级
  --------------------------------------------------------------------
| 位置 | 优先级 | 用途 |
|---|---:|---|
| 企业策略 | 5 | 组织下发,所有人统一 |
| 命令行参数 | 4 | 临时覆盖,这一次有效 |
| 项目本地 | 3 | 只在本机生效,不进版本库 |
| 项目共享 | 2 | 进版本库,团队共用 |
| 用户级 | 1 | 跟着人走,跨项目 |
| 插件 | 0 | 装进来就带着 |

  优先级高的会覆盖同名的低优先级配置。
  但「同名」这件事在钩子上有个更细的规则:看的是「位置加事件加匹配条件」,不是钩子脚本的名字。

  ② 三处注册同一个「格式化」钩子
  --------------------------------------------------------------------
| 位置 | 事件 | 匹配 | 结果 |
|---|---|---|---|
| 用户级 | PostToolUse | Edit(*.py) | 生效 |
| 项目共享 | PostToolUse | Edit(*.py) | 生效 |
| 项目本地 | PostToolUse | Edit(*.py) | 生效 |

  三处都生效——钩子是合并的,不像技能那样整份覆盖。改一个文件会跑三次格式化。
  这件事不报错,也不提示。你只会觉得「保存怎么有点慢」。

  ③ 合并带来的两个后果
  --------------------------------------------------------------------
  第一个后果是重复执行。同一条规则写在三个地方,就跑三次。
  三个地方分别由不同的人维护,谁也不知道另外两处的存在。
  第二个后果是顺序。多个钩子同时匹配时,执行顺序按来源和配置里的先后——不保证你期望的那个顺序。
  所以「把规则写在哪」这件事,在钩子上比在技能上更要紧:技能重复了顶多占点常驻预算,
  钩子重复了会真的多跑几遍。

  ④ 实操上的摆放建议
  --------------------------------------------------------------------
| 位置 | 放什么 | 注意 |
|---|---|---|
| 企业策略 | 合规红线、审计上报 | 少而硬 |
| 项目共享 | 这个项目的格式化、lint | 进版本库,团队一致 |
| 项目本地 | 个人临时试验的钩子 | 不进库,别和别人撞 |
| 用户级 | 跨项目通用的小工具 | 别放项目相关的东西 |

  ! 一条自查办法:把自己所有的钩子配置按「事件加匹配」排一遍,同一条规则出现两次以上的,
  删到只剩一处。合并是特性,重复是事故。

①六个位置和优先级:企业策略最高,插件最低。位置决定「谁看得见、谁改得动、能不能进版本库」。 ②三处都注册同一个格式化钩子,结果是三处都生效——钩子是合并的,不像技能那样整份覆盖。 改一个文件跑三次格式化。这件事不报错、不提示,你只会觉得保存有点慢。 ③合并带来两个后果:重复执行,以及顺序不可控(多个同时匹配时按来源和配置先后执行)。 所以「把规则写在哪」在钩子上比在技能上更要紧:技能重复了顶多占常驻预算,钩子重复了会真的多跑几遍。 ④摆放建议那一张表可以照着用:企业层少而硬、项目共享放团队规矩、项目本地放个人试验、 用户级放跨项目小工具。 自查办法只有一句:所有钩子配置按「事件加匹配」排一遍,同一条规则出现两次以上的删到只剩一处。 合并是特性,重复是事故。

30 秒实验

  1. 把三处注册改成两处(删掉项目本地那份),重跑第 ② 步。改一个文件跑几次?
  2. 在三处里的某一处把匹配改成「改任意文件」,重跑第 ② 步。改一个 md 文件会跑几次? 这说明匹配条件的作用是什么?
  3. 把同一个钩子从项目共享挪到企业策略,重跑一遍。执行次数变了吗?变动的是什么?

常见误区

  1. 以为钩子像技能一样「高优先级盖掉低优先级」。钩子是合并的,几处注册就跑几次。
  2. 给钩子脚本改名来避免重复。判定看的是「位置加事件加匹配」,脚本名不算。
  3. 把项目专用的钩子放用户级。它跨项目生效,会在别的项目里莫名其妙地跑。
  4. 在项目本地放团队该共用的钩子。它不进版本库,别人根本收不到。
  5. 配置越堆越多,觉得「管得更细」。多出来的往往只是重复执行。
自测三问(点开对答案) 1. 钩子的配置能放在哪六个位置? —— 企业策略、命令行参数、项目本地、项目共享、用户级、插件。优先级从企业往下递减。 2. 钩子是合并还是覆盖?和技能有什么不同? —— 钩子是合并:几处注册同一条规则就全跑。技能是覆盖:同名只留优先级最高的那份。 3. 为什么重复注册特别难发现? —— 因为它不报错、不提示,表现只是「有点慢」。 而且要按「位置加事件加匹配」比对才能看出来,改脚本名没用。

配置位置定了,下一节讲钩子真正干活的那一段:三种处理器,成本和确定性一级级往上走。

相关笔记