package.json 对比 go.mod:版本字段去哪儿了?
如果你从 JavaScript 转到 Go,有一件事会让你感到惊讶。
打开一个 package.json 文件。你会看到顶部有一个 version 字段。它易于阅读,你可以在 pull request 中修改它。它就存在于你的代码之中。
现在打开一个 go.mod 文件。
版本信息并不在那里。这不是一个错误,而是一种选择。
Go 不会为你的模块使用版本字段,而是使用 git tags。
工作原理:
• 你运行 git tag v1.2.3
• 你将 tag 推送到你的仓库
• 该 tag 就成为了你的版本
当有人运行 go get 时,Go 会查看你的 git tags 来寻找正确的 commit。该 tag 是唯一的真相来源 (single source of truth)。
这种设计有一个巨大的优势:版本永远不会指向错误的代码。在 npm 中,发布的代码和版本字段可能会出现脱节。而在 Go 中,它们是同一回事。版本即 commit。
然而,这改变了你的工作流:
- 可见性:你无法通过查看文件来查看版本。你必须运行 git 命令才能找到它。
- 代码审查:版本升级不会出现在代码 diff 中。它是一个 tag push,而不是代码变更。
- 本地构建:没有 tag 的 commit 会显示一个混乱的 pseudo-version。只有在对 commit 打上 tag 后,你才能获得一个整洁的版本。
- 主版本号:这是最大的变化。在 Go 中,v2 及以上版本必须在导入路径 (import path) 中包含版本号。
示例:
v1 使用 github.com/you/my-app
v2 使用 github.com/you/my-app/v2
这允许一个程序在不产生冲突的情况下,使用同一个库的两个不同的主版本。
大多数其他语言都将版本保留在文件中:
• Node: package.json
• Rust: Cargo.toml
• Python: pyproject.toml
• Java: pom.xml
Go 是个异类。它严格依赖 git tags。
如果你怀念 npm 的体验,可以在构建时使用 ldflags 将版本注入到你的二进制文件中。这样你的应用就可以响应版本查询命令了。
权衡很简单: 版本字段易于阅读和审查,但它可能会“撒谎”。 git tag 难以察觉,但它永远真实。
Go 选择真实,而非便利。
致 Go 开发者:这是最好的模型吗?如果工具能根据 git tag 验证它,你会更倾向于在 go.mod 中使用版本字段吗?
Source: https://dev.to/dalirnet/packagejson-vs-gomod-where-did-the-version-field-go-3301