Git 规约
提交消息/Commit Message
格式
推荐使用《约定式提交 (Convertional Commits)》格式书写 Commit Message。(请完整阅读后再继续以下内容)如果你在使用 npx f2elint@latest 初始化项目时启用了 Commitlint, 那么约定式提交规则也将一并启用。
<类型>[范围]: <描述>
[正文]
[脚注]语言
- 国际化项目和开源项目推荐使用英文书写 Commit Message,以增强协作。
- 其他项目推荐使用开发团队最普遍使用的语言,以提升效率。
- 不要使用其他人无法理解的语言书写 Commit Message。
- 不要使用拼音或不常见的缩写书写 Commit Message,以避免歧义。
类型/type
type 用来描述本次提交的改动类型,可选值及对应含义如下:
- feat: 新增功能
- fix: 修复 bug
- docs: 文档相关的改动
- style: 对代码的格式化改动,代码逻辑并未产生任何变化(例如代码缩进,分号的移除和添加)
- test: 新增或修改测试用例
- refactor: 重构代码或其他优化举措
- chore: 项目工程方面的改动,代码逻辑并未产生任何变化
- revert: 恢复之前的提交
注意:
- commit message 的 type 和 changelog 的 type 存在紧密联系,然而它们两者之间并非一一对应,比如在 changelog 中一般不会指出文档 docs 或测试用例 test 等方面发生的变化
- css 样式文件的修改一般属于 feat 或者 fix,并不是 style
范围/scope
scope 用来描述本次提交所涉及到的改动范围(例如模块、功能或其他任何限定的范围)。
scope 的具体取值视项目而定。以淘宝详情页为例,取值可以是:header, footer, favorite, sku, etc...
如果是 monorepo 的项目,scope 取值可以是 subpackage 的名称。例如 babel 项目中对某个 package 的修改:
chore(babel-helper-plugin-utils): add npmignore描述/description
description 用来概括和描述本次提交的改动内容,需注意以下几点:
时态方面使用一般现在时,不要使用过去时。虽然查看 message 时,message 内容本身都发生在过去,然而对于主题来说,使用现在时的时态更简洁明确,并且更易达成一致性:
# good docs: delete redundant docs # bad docs: deleted redundant docsStackOverflow: Should I use past or present tense in git commit messages
句式使用祈使句。即一般情况不要增加主语。因为在绝大情况下,主语都是作者『我』:
# good docs: delete redundant docs # bad docs: i delete redundant docs句首无需大写,句尾无需结束标点。因为主题(或标题)本身不用形成完整的句子:
# good docs: delete redundant docs # bad docs: Delete redundant docs.
正文/body
日志的内容主体 body 用来描述详细的提交内容,可写可不写,需注意以下几点:
- 时态方面使用一般现在时,不要用过去时态。
- 句式视情况而定,一般使用祈使句式。
- 标点方面遵循一般的文档格式规约。
脚注/footer
footer 通常用于代码评审过程记录、作者签名等。例如:
Reported-by: User1 <user1@example.com>
Helped-by: User2 <user2@example.com>
Reviewed-by: User3 <user3@example.com>
Signed-off-by: Author <author@example.com>为什么要有签名区?
因为一个提交的元信息中只有作者(author)、提交者(committer)两个字段,而一段代码的诞生,参与的人往往不止于此,还可能有问题报告者(Reported-by)、代码评审者(Reviewed-by)、上游 Committer 的签名(Signed-off-by)。为此一些开源项目(如 Git、Linux)的一个约定俗成的习惯,是在提交的最后加上签名,每个贡献者一行,从上到下可以看到这段代码诞生的过程。
还可以添加其他元信息,例如:
引用 Issues
可以在 commit 信息里使用关键字 + Issue ID(Gitlab / Github / Redmine 或其他平台的),来表明该提交解决了某个 Issue。推荐使用的关键字有:
close closes closed fix fixes fixed resolve resolves resolved关键字的选用可以根据当前语义、关联的 Issue 是否在当前仓库下,甚至是 commit 消息的长度限制来决定。
- close: 关闭当前仓库的 Issue
- fix: 关闭当前或其他仓库的 Issue, 一般指 Bug 修复
- resolve: 关闭当前或其他仓库的 Issue
关闭多个 Issues 使用如下格式:
Close #1, #2, #3 Close #1, close #2, close #3 Fix #1, #2, #3 Fix #1, close #2, close #3 Resolve #1, #2, #3 Resolve #1, close #2, close #3破坏性变动(Breaking changes)
如果本次提交的改动是破坏性的,需要在这里声明:
BREAKING CHANGE: 为了组件 API 规范的统一,本次升级将 size 属性的 value 值从 `s|m|l` 替换为 `small|medium|large`。 请按照如下方式升级: <Button size="s">提交</Button> --> <Button size="small">提交</Button> 继续使用 size="m" 可能会导致样式错误。
流水线/GitLab CI
node-ci-template 用于前端 Web 应用和 npm 包项目接入统一 GitLab CI。业务项目通过 include 引入模板后,可获得依赖安装、构建、单元测试、Sonar 扫描、Docker 镜像构建、npm 包发布、SBOM 上传和部署扩展能力。
模板支持 pnpm 和 npm,默认使用 pnpm。
快速接入
业务项目 .gitlab-ci.yml:
stages:
- build
- test
- release
- verify
- deploy
include:
- project: 'cq/dev/ci/node-ci-template'
ref: main
file:
- '/.template/workflow.yml'
- '/.template/anchor.yml'
- '/.template/build.yml'
- '/.template/test.yml'
- '/.template/release.yml'
- '/.template/verify.yml'
- '/.template/deploy.yml'
variables:
PRODUCT_NAME: 'your-product-name'
DOCKER_HARBOR_PROJECT: 'cq-saas'
PACKAGE_MANAGER: 'pnpm'
NEXUS_UPLOAD_ENABLED: 'false'如需项目自定义部署逻辑,可在业务仓库增加 .ci/deploy.yml 并额外 include:
- local: '/.ci/deploy.yml'
rules:
- exists:
- '.ci/deploy.yml'业务项目要求
package.json中提供build脚本,或设置BUILD_COMMAND。- 如启用集成验证,提供
verify脚本,或设置VERIFY_COMMAND。 - 默认单元测试使用 Vitest,项目需安装
vitest和@vitest/coverage-v8。 - 如启用 Docker 镜像构建,项目需提供一个或多个
Dockerfile。 - 如启用发布说明检查,创建发布 tag 前需维护
CHANGELOG.md并包含该 tag。 - 业务镜像的
FROM应使用内部 Harbor 镜像,不应直接使用公网镜像仓库。
最低测试依赖示例:
{
"devDependencies": {
"vitest": "^2.0.0",
"@vitest/coverage-v8": "^2.0.0"
}
}流水线触发
| 场景 | 条件 | 主要执行内容 |
|---|---|---|
| MR 检查 | MR 目标分支为 dev* | 构建、单元测试、Sonar 扫描 |
| 开发分支构建 | push 到 dev* | 构建、测试、可选开发镜像、集成验证、可选部署 |
| 预发布分支构建 | push 到 release* | 构建、集成验证、可选部署 |
| 正式发布 | push V* / v* tag | 构建正式镜像、可选 npm 发布、上传 SBOM、可选上传附件和 CHANGELOG 检查 |
以下情况会被模板跳过:dev* -> release* MR、release* -> main MR、非 dev* / release* 分支 push、非 V / v 开头的 tag。
必填变量
| 变量 | 说明 |
|---|---|
PRODUCT_NAME | 产品名,用于镜像仓库路径 |
DOCKER_HARBOR_PROJECT | Harbor 项目名,通常为 cq-saas |
常用可选变量
| 变量 | 默认值 | 说明 |
|---|---|---|
CI_NODE_IMAGE | $CI_REGISTRY/cq-common/ci/node/node22-pnpm-kaniko:20260414P2 | Node CI 镜像 |
PACKAGE_MANAGER | pnpm | 可选 pnpm 或 npm |
PNPM_VERSION | 10.0.0 | 使用 pnpm 时的版本 |
NPM_CONFIG_REGISTRY | https://nexus.in.whatspos.cn/repository/pax-npm-group/ | npm/pnpm 依赖安装源 |
INSTALL_COMMAND | 自动 | 覆盖依赖安装命令 |
BUILD_COMMAND | 自动 | 覆盖构建命令 |
TEST_COMMAND | 自动 | 覆盖单元测试命令;设置后不收集默认 JUnit/覆盖率报告 |
VERIFY_COMMAND | 自动 | 覆盖集成验证命令 |
BUILD_WORKSPACE_CACHE_PATHS | ./[!.]* | 构建后传递给后续 job 的工作区路径或 glob |
DOCKERFILE_SEARCH_ROOT | . | Dockerfile 搜索根目录;monorepo 推荐设置为 ./apps |
DOCKER_IMAGE_NAME | "" | 镜像名基准值;空值时使用 CI_PROJECT_NAME |
DOCKER_BUILD_ENABLED | true | 是否在发布流水线构建 Docker 镜像 |
DEPLOY_DEV_ENABLED | true | 是否构建开发镜像并启用开发部署 |
SONAR_SCAN_ENABLED | true | 是否运行 SonarQube 扫描 |
NEXUS_UPLOAD_ENABLED | false | tag 发布时是否发布 npm 包到 Nexus |
SBOM_UPLOAD_ENABLED | true | tag 发布时是否生成并上传 SBOM |
SEAFILE_UPLOAD_ENABLED | false | tag 发布时是否启用 Seafile 上传 job |
INTEGRATION_TEST_ENABLED | true | 是否运行集成验证 |
CHANGELOG_CHECK_ENABLED | false | tag 发布时是否检查 CHANGELOG.md |
NEXUS_NPM_REGISTRY | 自动 | npm 发布仓库;未设置时使用 ${NEXUS_URL}/repository/npm-releases/ |
SONAR_SCAN_VERSION | 4.3.6 | Sonar 扫描器版本 |
CDXGEN_VERSION | 11.1.5 | SBOM 生成器版本 |
示例:
variables:
PRODUCT_NAME: 'payment'
DOCKER_HARBOR_PROJECT: 'cq-saas'
PACKAGE_MANAGER: 'pnpm'
DOCKER_IMAGE_NAME: 'payment-web'
DOCKERFILE_SEARCH_ROOT: './apps'
NEXUS_UPLOAD_ENABLED: 'false'Docker 镜像命名
镜像路径:
harbor.in.whatspos.cn/{DOCKER_HARBOR_PROJECT}/{PRODUCT_NAME}/{MODULE_NAME}:{TAG}MODULE_NAME 规则:
| Dockerfile 位置 | 模块名 |
|---|---|
根目录 Dockerfile | ${DOCKER_IMAGE_NAME:-CI_PROJECT_NAME} |
子目录 Dockerfile | ${DOCKER_IMAGE_NAME:-CI_PROJECT_NAME}-目录名 |
tag 规则:
| 场景 | tag |
|---|---|
| 开发镜像 | ${CI_COMMIT_SHORT_SHA}-DEV |
| 正式镜像 | CI_COMMIT_TAG |
示例:GitLab 项目名 payment-web,PRODUCT_NAME=payment。
Dockerfile -> harbor.in.whatspos.cn/cq-saas/payment/payment-web:V1.0.0
admin/Dockerfile -> harbor.in.whatspos.cn/cq-saas/payment/payment-web-admin:V1.0.0设置 DOCKER_IMAGE_NAME=payment-frontend 后:
Dockerfile -> harbor.in.whatspos.cn/cq-saas/payment/payment-frontend:V1.0.0
admin/Dockerfile -> harbor.in.whatspos.cn/cq-saas/payment/payment-frontend-admin:V1.0.0Monorepo 建议
对于 pnpm workspace / turbo 这类多应用仓库:
variables:
DOCKERFILE_SEARCH_ROOT: './apps'
BUILD_WORKSPACE_CACHE_PATHS: 'apps/'推荐将应用 Dockerfile 放在固定目录,例如:
apps/
admin/Dockerfile
portal/Dockerfile生成镜像名示例:
payment-web-admin
payment-web-portalnpm 包发布
NEXUS_UPLOAD_ENABLED=true 时,tag 发布流水线会执行 npm 包发布。
- SPA 或纯 Web 镜像项目通常设置
NEXUS_UPLOAD_ENABLED=false。 - npm 包项目需保证
package.json中name和version正确。 - 如需指定仓库,设置
NEXUS_NPM_REGISTRY。
发布前检查
- 正式发布请创建
V*或v*开头的 Git tag。 - 如设置
CHANGELOG_CHECK_ENABLED=true,CHANGELOG.md必须包含当前发布 tag。 - 如项目不需要 Docker 镜像,设置
DOCKER_BUILD_ENABLED=false。 - 如项目不需要 npm 发布,设置
NEXUS_UPLOAD_ENABLED=false。 - 如项目不需要 SBOM 上传,设置
SBOM_UPLOAD_ENABLED=false。
分支管理/Branch Management
1. 设计原则
- 长期分支只保留
main、release、dev - 日常开发统一走
feature-* -> dev -> release -> main - 版本以 Git Tag 为准,分支不承担长期版本编号语义
- 开发阶段不打源码 Tag
- 开发阶段镜像 Tag 使用
${CI_COMMIT_SHORT_SHA}-DEV - 开发镜像仅在
DOCKER_BUILD_ENABLED=true且DEPLOY_DEV_ENABLED=true时构建 - 正式发布镜像 Tag 与源码 Tag 完全一致:
V...[-SNAPSHOT] - 不设计长期并行
dev/V*/release/V* - 仅在少数特殊场景下允许短期
dev/V*,完成后必须合并回dev并删除
2. 分支清单
长期分支
| 分支 | 说明 | 直接 push |
|---|---|---|
main | 公共基线与正式发布结果归档面,只接受来自 release 的 merge | ❌ 禁止 |
release | 发布稳定化分支,用于集成测试、回归、验收与发版确认 | ❌ 禁止 |
dev | 唯一日常集成分支,接受来自 feature-* 的 merge | ❌ 禁止 |
短命分支
| 分支 | 命名规范 | 来源 | 目标 | 合并后删除 |
|---|---|---|---|---|
| 功能分支 | feature-{需求ID} | dev | dev | ✅ |
| BUG 修复分支 | feature-{BUG ID} | dev | dev | ✅ |
| 临时版本分支 | dev/V{版本标识} | dev 或约定起点 | dev | ✅ |
dev/V*仅用于少数场景下的短期隔离开发,不属于长期分支体系。
3. 标准工作流(日常)
适用于:绝大多数日常开发场景。
feature-{需求ID} ──► dev ──► release ──► main
│ │
│ └── 打正式 Tag:V1.00.01-20260420-P1[-SNAPSHOT]
└── 生成开发镜像:{CI_COMMIT_SHORT_SHA}-DEV流程步骤
开发阶段
- 从
dev切出feature-{需求ID} - 开发完成后发起 MR →
dev,经 Code Review 合并 dev触发dev_branch_build流水线- 当
DOCKER_BUILD_ENABLED=true且DEPLOY_DEV_ENABLED=true时,开发流水线生成开发镜像,镜像 Tag 使用${CI_COMMIT_SHORT_SHA}-DEV - 开发环境基于该镜像完成验证
发布准备 6. 需要发版时,将 dev 合并到 release 7. release 触发 release_branch_build 流水线,执行测试、回归、验收等发布前确认8. release 分支本身不承担长期开发,只用于稳定化与发布确认
发布 9. 在 release 分支打正式 Tag,格式见 标签命名 10. Tag 触发 tag_release 流水线,构建并推送正式镜像到 Harbor 11. 正式镜像 Tag 与源码 Tag 完全一致,例如 V1.00.01-20260420-P1[-SNAPSHOT] 12. 部署验证通过后,将 release 合并到 main
4. 开发阶段镜像策略
开发阶段的目标是持续验证,不是形成正式版本,因此开发镜像与正式发布镜像必须分开管理。
规则
- 开发阶段不打源码 Tag
- 开发镜像仅用于开发环境验证
- 开发镜像 Tag 使用
${CI_COMMIT_SHORT_SHA}-DEV - 仅当
DOCKER_BUILD_ENABLED=true且DEPLOY_DEV_ENABLED=true时构建开发镜像 -DEV仅表示开发临时镜像,不代表正式版本- 正式发布阶段继续使用源码 Tag 作为正式镜像 Tag
示例
| 场景 | 镜像 Tag 示例 | 含义 |
|---|---|---|
| 开发阶段 | a1b2c3d4-DEV | dev 分支某次提交生成的开发临时镜像 |
| 正式发布 | V1.00.01-20260420-P1 | 正式发布镜像 |
| 预发布/试发 | V1.00.01-20260420-P1-SNAPSHOT | 非正式发布镜像 |
5. 短期 dev/V* 使用规则
dev/V* 只用于低频的特殊隔离场景,不作为长期并行版本线存在。
适用场景
- 某次短期需求需要与日常
dev隔离 - 某个版本窗口内需要临时冻结一部分变更
- 某次特殊修复不适合直接在
dev上持续叠加
约束规则
dev/V*仅在确有必要时创建- 不配套长期
release/V* - 不将
dev/V*视为长期维护分支 - 工作完成后必须合并回
dev - 合并完成后应删除该分支
示例流程
dev ──► dev/V1.00.00 ──► dev若未来并行开发成为常态,应单独重设计分支模型,而不是在当前规范上不断叠加例外。
6. 与 CI 流水线的对应关系
| 操作 | 触发分支 / Tag | CI 流水线类型 | 执行内容 |
|---|---|---|---|
MR 到 dev | MR 事件 | mr_check | 编译 + 单元测试 |
push 到 dev | dev | dev_branch_build | 编译 + 单元测试 + 按开关条件构建开发镜像 |
push 到 release | release | release_branch_build | 编译 + 发布前验证 |
打 V* / v* Tag | Tag | tag_release | 构建正式镜像 + 推送 Harbor |
7. 分支保护规则(GitLab 配置建议)
| 分支 | 允许 push | 允许 merge | 需要 Code Review |
|---|---|---|---|
main | 无 | Maintainer | ✅ |
release | 无 | Maintainer | ✅ |
dev | 无 | Developer+ | ✅ |
feature-* | Developer+ | Developer+ | — |
dev/V* | 无 | Developer+ | ✅ |
标签命名/Tag Naming
Git Tag 通常用来标记发布的版本。
1. 正式版与非正式版
| 类型 | Git tag 要求 | 说明 |
|---|---|---|
| 正式版 | 不得包含子串 -SNAPSHOT | 对外可交付、可长期追溯的发布标识 |
| 非正式版(试发、重复构建、内部验证等) | 应带后缀 -SNAPSHOT | 表示非最终发布,可与 Maven -SNAPSHOT 语义对齐(见第 5 节) |
判定规则:tag 名称中是否出现 -SNAPSHOT(区分大小写按团队统一,建议与 Maven 一致为 大写 SNAPSHOT)。
2. 推荐 tag 形态
整体结构:
V{主版本}-{YYYYMMDD}-P{序号}[-SNAPSHOT]| 段 | 必填 | 含义 | 示例 |
|---|---|---|---|
V | 是 | 前缀,与 CI workflow 中 V* / v* 规则一致;若统一用大写 V,则全仓库一致 | V |
{主版本} | 是 | 对外语义化版本,建议 主.次.修订 形式 | 1.00.01 |
- | 是 | 分隔符 | |
{YYYYMMDD} | 是 | 封版或打 tag 的日期(8 位) | 20260416 |
- | 是 | 分隔符 | |
P{序号} | 是 | 同一天、同一主版本下的第几次修订;P 字面量 + 正整数,建议从 P1 起 | P1 |
-SNAPSHOT | 否 | 仅非正式版附加 | 正式版省略 |
2.1 示例
| 场景 | 合法示例 |
|---|---|
| 正式版 | V1.00.01-20260416-P1 |
| 非正式版 | V1.00.01-20260416-P1-SNAPSHOT |
| 同一天第二次正式修订 | V1.00.01-20260416-P2 |
2.2 与 v* 小写前缀
若仓库同时允许 v1.00.01-20260416-P1,须在组织内约定 仅大写 V 或 大小写二选一,并与 GitLab CI workflow.rules 中的正则 保持一致,避免 tag 能推但流水线不触发。
3. 递增与唯一性建议
- 主版本
{主版本}:随产品里程碑变更,由产品/研发在发布前确定。 - 日期
YYYYMMDD:取 打 tag 当日(或封版日),与发布说明、变更日志日期对齐。 - 同日修订
P{n}:同一主版本 + 日期下,正式版从P1递增;若先打 SNAPSHOT 再转正,建议 新打正式 tag(去掉-SNAPSHOT并视情况升P),避免移动 tag。 - 唯一性:同一仓库内 勿重复使用 已推远程的正式版 tag;
-SNAPSHOT类 tag 若允许覆盖,须在团队内明确 禁止对正式 tag 重写。
4. 与 CI / 镜像 tag 的关系
- 当前模板约定:业务镜像仅在
tag_release(匹配V*/v*)时构建并推送,镜像:TAG=CI_COMMIT_TAG。 - 因此 Git tag 字符串即镜像 tag,命名时需考虑 Harbor 长度与字符限制(过长仅影响可读性,一般可接受)。
- 开发阶段镜像使用
${CI_COMMIT_SHORT_SHA}-DEV,仅作为开发环境临时镜像标识,不属于 Git Tag / 正式版本命名规则范畴。
5. 与 Maven -SNAPSHOT 的边界(说明性)
- Git tag 中的
-SNAPSHOT仅表示「非正式 Git 发布线」,不会自动改变 Maven 的pom.xml版本或 Nexus 仓库类型。 - 若希望 Git 命名与 Maven 仓库(releases / snapshots)严格一致,需在业务工程或发布脚本中 单独约定
pom版本与maven-deploy目标仓库,本文不替代 Maven 版本策略文档。