Masterminds/semver
GitHub: Masterminds/semver
semver 是一个 Go 语言库,用于解析、排序和约束检查语义化版本号,帮助开发者在依赖管理和发布流程中精确处理版本逻辑。
Stars: 1422 | Forks: 166
# SemVer
`semver` 包提供了在 Go 中处理[语义化版本](http://semver.org)的能力。具体来说,它提供了以下功能:
* 解析语义化版本
* 排序语义化版本
* 检查语义化版本是否符合一组约束条件
* 可选地处理 `v` 前缀
[](https://masterminds.github.io/stability/active.html)
[](https://github.com/Masterminds/semver/actions)
[](https://pkg.go.dev/github.com/Masterminds/semver/v3)
[](https://goreportcard.com/report/github.com/Masterminds/semver)
## 包版本
请注意,导入 `github.com/Masterminds/semver/v3` 以使用最新版本。
`semver` 包有三个主要版本。
* 3.x.x 是稳定且活跃的版本。此版本专注于与其他语言工具中范围处理的约束兼容性。它的 API 与 v1 版本类似。此版本的开发在 master 分支上进行。此版本的文档如下。
* 2.x 主要是为 [dep](https://github.com/golang/dep) 开发的。没有打标签的发布版本,开发由 [@sdboyer](https://github.com/sdboyer) 完成。与 v1 存在 API 破坏性变更。此版本位于 [2.x 分支](https://github.com/Masterminds/semver/tree/2.x)。
* 1.x.x 是最初的发布版本。它已不再维护。您应该改用 v3 版本。您可以在此处阅读 1.x.x 版本的文档[这里](https://github.com/Masterminds/semver/blob/release-1/README.md)。
## 解析语义化版本
有两个函数可以解析语义化版本。`StrictNewVersion` 函数仅解析规范中规定的有效 2.0.0 版本的语义化版本。`NewVersion` 函数尝试将版本强制转换为语义化版本并进行解析。例如,如果存在前导 v 或未列出所有 3 部分的版本(例如 `v1.2`),它将尝试将其强制转换为有效的语义化版本(例如 1.2.0)。在这两种情况下,都会返回一个 `Version` 对象,该对象可以进行排序、比较,并用于约束条件中。
解析版本时,如果解析过程中出现问题,将返回错误。例如,
```
v, err := semver.NewVersion("1.2.3-beta.1+build345")
```
Version 对象提供了多种方法,可用于获取版本各部分、将其与其他版本进行比较、将其转换回字符串以及获取原始字符串。如果语义化版本被强制转换为了有效格式,获取原始字符串将会非常有用。
一些包级别的变量会影响 `NewVersion` 处理解析的方式。
- `CoerceNewVersion` 默认为 `true`。当设置为 `true` 时,它会将不合规的版本强制转换为 SemVer。例如,允许在 major、minor 或 patch 部分使用前导 0。这使得即使在不符合 SemVer 规范的情况下,也能在版本中使用 CalVer。当设置为 `false` 时,会减少强制转换的操作。
- `DetailedNewVersionErrors` 提供更详细的错误信息。它仅在 `CoerceNewVersion` 设置为 `false` 时生效。当 `DetailedNewVersionErrors` 设置为 `true` 时,它可以提供更多关于版本为何无效的见解。将 `DetailedNewVersionErrors` 设置为 `false` 在性能上会更快,但如果版本解析失败,提供的错误信息详细程度会降低。
## 排序语义化版本
可以使用标准库中的 `sort` 包对一组版本进行排序。例如,
```
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
vs := make([]*semver.Version, len(raw))
for i, r := range raw {
v, err := semver.NewVersion(r)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
vs[i] = v
}
sort.Sort(semver.Collection(vs))
```
## 检查版本约束
有两种比较版本的方法。一种是在 `Version` 实例上使用比较方法,另一种是使用 `Constraints`。这两种比较方法之间有一些重要的区别需要注意。
1. 当使用 `Compare`、`LessThan` 等函数比较两个版本时,它将遵循规范,并在比较中始终包含预发布版本。它提供的答案符合 https://semver.org/#spec-item-11 上规范中关于比较部分的规定。
2. 当使用约束检查进行检查或验证时,它将遵循另一套规则,这套规则在 npm/js 和 Rust/Cargo 等工具的范围处理中很常见。这包括在范围不包含预发布版本时,将预发布版本视为无效。如果您希望包含预发布版本,一个简单的解决方案是在您的范围中包含 `-0`。
3. 约束范围可以有一些复杂的规则,包括 ~ 和 ^ 的简写用法。有关这些内容的更多详细信息,请参阅下方的选项。
这两种检查版本的方法之间存在差异,因为 `Version` 上的比较方法遵循规范,而比较范围并不是规范的一部分。不同的包和工具各自制定了范围规则。这导致了差异。例如,npm/js 和 Cargo/Rust 遵循相似的模式,而 PHP 对 ^ 有不同的模式。此包中的比较功能遵循 npm/js 和 Cargo/Rust 的方向,因为使用它的应用程序在其版本方面也遵循了类似的模式。
检查版本是否符合版本约束是该包功能最丰富的部分之一。
```
c, err := semver.NewConstraint(">= 1.2.3")
if err != nil {
// Handle constraint not being parsable.
}
v, err := semver.NewVersion("1.3")
if err != nil {
// Handle version not being parsable.
}
// Check if the version meets the constraints. The variable a will be true.
a := c.Check(v)
```
### 基本比较
比较包含两个元素。首先,比较字符串是一个由空格或逗号分隔的 AND 比较列表。然后这些比较通过 || (OR) 进行分隔。例如,`">= 1.2 < 3.0.0 || >= 4.2.3"` 会寻找大于等于 1.2 且小于 3.0.0,或者大于等于 4.2.3 的版本。
基本的比较操作符有:
* `=`:等于(别名为无操作符)
* `!=`:不等于
* `>`:大于
* `<`:小于
* `>=`:大于或等于
* `<=`:小于或等于
### 处理预发布版本
对于不熟悉预发布版本的人来说,预发布版本用于在稳定版本或正式发布版本之前的软件发布。预发布版本的例子包括开发版、alpha 版、beta 版和发布候选版(RC)。预发布版本可能是类似于 `1.2.3-beta.1` 的版本,而稳定版本则是 `1.2.3`。在优先级顺序中,预发布版本排在其相关联的发布版本之前。在这个例子中 `1.2.3-beta.1 < 1.2.3`。
根据语义化版本规范,预发布版本在 API 方面可能与其对应的发布版本不兼容。规范指出,
SemVer 在使用没有预发布比较器的约束进行比较时,会跳过预发布版本。例如,在查看发布列表时 `>=1.2.3` 会跳过预发布版本,而 `>=1.2.3-0` 则会评估并找到预发布版本。
在上面的比较示例中,之所以使用 `0` 作为预发布版本,是因为根据规范,预发布版本只能包含 ASCII 字母数字字符和连字符(以及 `.` 分隔符)。同样根据规范,排序是基于 ASCII 排序顺序进行的。在 ASCII 排序顺序中,最小的字符是 `0`(参见 [ASCII 表](http://www.asciitable.com/))
了解 ASCII 排序顺序很重要,因为 A-Z 排在 a-z 之前。这意味着 `>=1.2.3-BETA` 会返回 `1.2.3-alpha`。您所期望的大小写敏感性在这里并不适用。这是由于规范规定了使用 ASCII 排序顺序。
从 `semver.NewConstraint()` 返回的 `Constraints` 实例具有一个 `IncludePrerelease` 属性,当设置为 true 时,在调用 `Check()` 和 `Validate()` 时将返回预发布版本。
### 连字符范围比较
处理范围有多种方法,第一种是连字符范围。它们的形式如下:
* `1.2 - 1.4.5` 等价于 `>= 1.2 <= 1.4.5`
* `2.3.4 - 4.5` 等价于 `>= 2.3.4 <= 4.5`
请注意,不带空格的 `1.2-1.4.5` 的解析方式完全不同;它会被解析为带有_预发布版本_ `1.4.5` 的单一约束 `1.2.0`。
### 比较中的通配符
`x`、`X` 和 `*` 字符可用作通配符。这适用于所有比较操作符。当在 `=` 操作符上使用时,它会回退到 patch 级别的比较(参见下文的波浪号)。例如,
* `1.2.x` 等价于 `>= 1.2.0, < 1.3.0`
* `>= 1.2.x` 等价于 `>= 1.2.0`
* `<= 2.x` 等价于 `< 3`
* `*` 等价于 `>= 0.0.0`
### 波浪号范围比较
当指定了 minor 版本时,波浪号 (`~`) 比较操作符用于 patch 级别的范围;如果缺少 minor 号,则用于 major 级别的更改。例如,
* `~1.2.3` 等价于 `>= 1.2.3, < 1.3.0`
* `~1` 等价于 `>= 1, < 2`
* `~2.3` 等价于 `>= 2.3, < 2.4`
* `~1.2.x` 等价于 `>= 1.2.0, < 1.3.0`
* `~1.x` 等价于 `>= 1, < 2`
### 抑音符范围比较
一旦发生了稳定 (1.0.0) 版本发布,抑音符 (`^`) 比较操作符就用于 major 级别的更改。在 1.0.0 版本发布之前,minor 版本充当 API 稳定性级别。这在比较 API 版本时非常有用,因为 major 级别的更改会破坏 API。例如,
* `^1.2.3` 等价于 `>= 1.2.3, < 2.0.0`
* `^1.2.x` 等价于 `>= 1.2.0, < 2.0.0`
* `^2.3` 等价于 `>= 2.3, < 3`
* `^2.x` 等价于 `>= 2.0.0, < 3`
* `^0.2.3` 等价于 `>=0.2.3 <0.3.0`
* `^0.2` 等价于 `>=0.2.0 <0.3.0`
* `^0.0.3` 等价于 `>=0.0.3 <0.0.4`
* `^0.0` 等价于 `>=0.0.0 <0.1.0`
* `^0` 等价于 `>=0.0.0 <1.0.0`
## 验证
除了根据约束测试版本外,还可以根据约束验证版本。当验证失败时,将返回一个包含版本为何不满足约束原因的错误切片。例如,
```
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
if err != nil {
// Handle constraint not being parseable.
}
v, err := semver.NewVersion("1.3")
if err != nil {
// Handle version not being parseable.
}
// Validate a version against a constraint.
a, msgs := c.Validate(v)
// a is false
for _, m := range msgs {
fmt.Println(m)
// Loops over the errors which would read
// "1.3 is greater than 1.2.3"
// "1.3 is less than 1.4"
}
```
## 贡献
如果您发现问题或想要贡献,请提交一个 [issue](https://github.com/Masterminds/semver/issues) 或[创建 pull request](https://github.com/Masterminds/semver/pulls)。
## 安全
安全是这个项目的一个重要考量。该项目目前使用以下工具来帮助发现安全问题:
* [CodeQL](https://codeql.github.com)
* [gosec](https://github.com/securego/gosec)
* 每日 Fuzz 测试
如果您认为自己发现了安全漏洞,可以通过 [GitHub 安全页面](https://github.com/Masterminds/semver/security)进行私密披露。
标签:EVTX分析, 日志审计