使用 Azure DevOps 的 semantic release 手动进行版本 bump
Source: Dev.to

semantic-release 包(https://github.com/semantic-release/semantic-release)自动化项目的版本管理和发布流程。它根据遵循 Conventional Commits 规范的提交信息来确定下一个版本号,生成发布说明,并自动发布。
然而,一些开发者更倾向于自行控制何时递增主版本号和次版本号。像 JetBrains 这样的公司会使用年份作为主版本号,使用自动递增的整数作为次版本号,如下图所示。

由于 semantic-release 提供了诸如发布说明生成器等多种内置工具,继续使用它并仅更改版本递增逻辑是很有吸引力的。这正是我将在本文中展示的内容。
Source: …
Azure DevOps pipeline for Manual bumping
我的做法是创建一个 Azure DevOps pipeline,运行 semantic-release,其步骤如下:
- pipeline 会提示用户指定 bump 类型(major、minor 或 patch)以及期望的版本号。
- 用户确认选择后,如果用户选择了 major 或 minor,pipeline 将进入审批状态。
- 获得批准后,pipeline 按所选的 bump 类型递增版本。
- pipeline 会验证 bump 是否与期望的版本号一致。
下面的代码片段展示了包含上述步骤的 Azure DevOps pipeline YAML。parameters 用于请求用户的 bump 类型(默认 patch)和目标版本。它们的值被设为环境变量,在 semantic‑release 配置中使用。
parameters:
- name: bumpType
type: string
default: "patch"
values:
- major
- minor
- patch
- name: bumpNumber
type: string
default: "0"
pool:
vmImage: ubuntu-latest
jobs:
- job: approval
pool: server
steps:
- task: ManualValidation@1
# …
condition: ne('${{ parameters.bumpType }}', 'patch')
- job: create_tag
dependsOn: approval
steps:
# …
- script: |
npx semantic-release
displayName: Run semantic-release setting ${{ parameters.bumpType }} version to ${{ parameters.bumpNumber }}
env:
SEMANTIC_RELEASE_BUMP_TYPE: ${{ parameters.bumpType }}
SEMANTIC_RELEASE_BUMP_NUMBER: ${{ parameters.bumpNumber }}
就是这样!有了这个设置,你可以手动 bump 项目的版本,而无需担心提交信息。
下面的片段展示了 semantic‑release 的配置文件(release.config.cjs)。对于插件 @semantic-release/commit-analyzer(它通常会根据提交信息 bump 版本),我们将其设置为始终按照用户选择的 bump‑type 进行递增。
// file release.config.cjs
module.exports = {
// …
plugins: [
// …
[
'@semantic-release/commit-analyzer',
{
releaseRules: [
{ release: process.env.SEMANTIC_RELEASE_BUMP_TYPE }
]
}
],
'./verify-release.js'
]
};
verify-release.js 插件会验证新版本是否如预期递增。这确保如果 pipeline 使用相同的输入再次执行时,会因 bump 产生不期望的值而失败(例如在 JetBrains 示例中将 major 版本设置为下一年)。下面的代码片段展示了 verify-release.js。
// file verify-release.js
module.exports = {
verifyRelease: async (pluginConfig, context) => {
const { lastRelease = {}, nextRelease = {}, logger = console } = context;
const bumpType = process.env.SEMANTIC_RELEASE_BUMP_TYPE;
const bumpNumber = process.env.SEMANTIC_RELEASE_BUMP_NUMBER;
logger.log('Verifying expected release.');
if (!bumpType) {
logger.log('SEMANTIC_RELEASE_BUMP_TYPE not set — skipping version verification.');
return;
}
if (bumpType == 'patch') {
logger.log('Bump type set to patch. Nothing to verify.');
return;
}
const actual = nextRelease && nextRelease.version;
const match = actual.match(/^(\d+)\.(\d+)\.\d+$/);
if (!match) {
throw new Error(`Invalid tag format: ${lastRelease}`);
}
const actualMajor = Number(match[1]);
const actualMinor = Number(match[2]);
if (bumpType == 'major' && actualMajor != bumpNumber) {
logger.error(`Major version mismatch: expected ${bumpNumber} but will publish ${actualMajor}`);
throw new Error(`Version verification failed: expected major version ${bumpNumber}, got ${actualMajor}`);
}
if (bumpType == 'minor' && actualMinor != bumpNumber) {
logger.error(`Minor versi
on mismatch: expected ${bumpNumber} but will publish ${actualMinor}`);
throw new Error(`Version verification failed: expected minor version ${bumpNumber}, got ${actualMinor}`);
}
}
};
下图展示了一个将 major 版本提升的流水线执行示例。

构建新版本
一旦发布准备就绪,您可以开始构建应用程序。此示例使用用 Go 编写的简单 Hello World CLI。
管道的步骤如下:
- 检出带标签的代码
- 获取当前提交的基于标签的描述 —— 如果提交没有标签,则基于最新标签创建描述性版本。
- 将 Azure DevOps 构建号设置为上一步的描述。
- 生成 CLI 可执行文件 并将其发布为构建产物。
Azure Pipelines YAML
variables:
appName: hello-world
buildDir: build
steps:
- checkout: self
fetchTags: true
fetchDepth: 0
- script: |
export VERSION=$(git describe --tags)
echo "##vso[build.updatebuildnumber]${VERSION}"
displayName: "Set build number"
- task: GoTool@0
inputs:
version: "1.25"
- script: |
mkdir -p $(buildDir)
go build -o $(buildDir)/$(appName) ./cmd
displayName: "Build Go binary"
- script: |
cd $(buildDir)
zip $(appName)-$(Build.BuildNumber).zip $(appName)
displayName: "Create ZIP with version"
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: "$(buildDir)"
ArtifactName: "release"
publishLocation: "Container"
以下图片展示了成功的构建。

本文使用的代码已在 GitHub 仓库中提供。
