源码指纹自升级(Source Fingerprint Self-Upgrade)—— 参考资料
⚠️ 本 skill 是参考资料,不是可执行功能。AI 不会自动触发它去做任何事;只有你主动 @ 它、或明确要求查阅时才会加载。其价值在于:把「源码指纹自升级」这套设计思路归档成规范形态,供你阅读、或往自己项目里套。
这是什么
"源码指纹自升级"是一种让程序在启动时自己判断该不该启动、要不要替换掉已经在跑的旧程序的做法。
核心思路:不靠人手动输出版本号,而是直接对程序自身的源码算一个哈希指纹。源码有任何改动,指纹就变;没动,指纹就不变。
启动流程
- 程序启动,先算自己的源码指纹
FP_new。 - 检查目标端口有没有已经在跑的旧程序。
- 没有旧程序 → 直接正常启动。
- 有旧程序 → 向它要它的指纹
FP_old。FP_old === FP_new→ 两份代码一模一样,说明"我"就是已经在跑的那个 → 直接退出(不要重复启动)。FP_old !== FP_new→ 代码升级了 → 先优雅关闭旧程序(释放端口),再启动自己(替掉旧的)。
为什么不用版本号
版本号是"人写的字符串",容易漏改:
- 改了代码但忘了 bump 版本号 → 两份不同代码被认为"同一版本" → 旧程序不会被替换,bug 还在。
- 没改代码但误 bump 版本号 → 被认为是"新版本" → 白白重启一次。
源码指纹是"代码算出来的",代码变它才变,不存在"忘了改"的问题。
大公司通常结合使用:版本号(给人看)+ commit 哈希 / 构建摘要(给机器比)+ 健康检查接口。源码指纹是其中"给机器比"的一种,且不需要 git、不需要构建步骤,纯 JS 项目直接可用。
采集哪些指纹(规则)
指纹模块 demo/fingerprint.js 的规则如下:
| 规则 | 说明 |
|---|---|
只采集 .js 文件 |
这是真正的"源码",会影响程序行为。非 JS 脚本(.sh/.py/.bat 等)不算。 |
| 递归扫描 | 从根目录开始,子目录、子子目录全部递归进去,包括任意层嵌套子模块。 |
| 按路径排序 | 先 sort() 再拼接,保证不同机器、不同文件名顺序下指纹一致。 |
| 算法 sha256 | 用 Node 内置 crypto,无第三方依赖。 |
| 取前 16 位十六进制 | digest("hex").slice(0,16),够区分又好展示。 |
文件间用 \0 分隔 |
每个文件内容后加一个 \0,避免两个文件首尾拼接产生误判。 |
| 排除运行期目录 | node_modules、runtime、以 . 开头的目录不计入(这些不是源码)。 |
| 读不到就跳过 | 单个文件读失败不影响整体,保证健壮性。 |
关键:非 JS 脚本(如 demo/scripts/run.sh)是程序的一部分,但按规则不计入指纹。 因为改个部署脚本不该触发"代码升级重启"。
文件结构
source-fingerprint-self-upgrade/
├── SKILL.md # 本文件(skill 入口)
├── README.md # 详细原理与规则文档
├── package.json # type: module
├── demo/ # 示例程序(可运行)
│ ├── fingerprint.js # 指纹计算模块(核心,纯函数)
│ ├── main.js # 入口:HTTP 服务 + 自升级协调逻辑
│ ├── lib/ # 递归扫描验证用的嵌套模块
│ └── scripts/ # 非 JS 脚本(不计入指纹)
└── test/
└── test.js # 最简单的测试
怎么跑(验证用,非 skill 功能)
cd source-fingerprint-self-upgrade
node demo/main.js # 启动示例服务(默认端口 9421)
# 另开终端
curl http://localhost:9421/version # 看指纹
curl http://localhost:9421/health # 健康检查
node test/test.js # 跑测试(验证指纹行为)
验证指纹是否生效:
- 改一个
.js文件(例如给lib/util.js加一行注释)→ 指纹立刻变化。 - 改
scripts/run.sh→ 指纹不变(它不计入指纹)。
给 AI 的提示(套用到你自己的项目时看)
如果你(AI)要帮用户实现这套机制:
- 把
fingerprint.js当成一个纯函数模块引入,不要在里面写副作用。 - 入口文件在启动逻辑最前面调用
computeFingerprint(),并在/version接口返回它。 - 自升级协调要遵守顺序:先让旧程序释放端口,再启动自己。绝不允许"先启动新的、再杀旧的"——新程序会因端口被占而启动失败。
- 关闭旧程序优先用程序自己提供的优雅停机接口(如
POST /shutdown);没有的话再回退到按 PID 杀进程。 - 指纹不一致且旧程序无响应时,要有超时兜底,避免卡死。
- 永远不要为了"方便"把
node_modules、上传目录、截图目录算进指纹,否则指纹会天天变。
备注:更完整的背景说明见同目录
README.md。