4. 制品元数据管理
制品元数据是发布治理的“身份证”和“履历表”。没有元数据,制品仓库里只有一堆文件名和哈希;有了元数据,才能回答“这个包是谁构建的、依赖了什么、对应哪个提交、能否回滚、是否通过安全扫描”。本节围绕名称、版本、依赖、构建信息四类核心字段,讲清如何设计标准、如何用 GitLab CI 自动注入、以及如何通过 GitLab API 与制品仓库 API 把元数据落到可查询的存储中。
一、先定义元数据模型,再谈采集
元数据不是越多越好,而是“发布决策真正用得到的字段必须齐全”。建议把元数据分为四组:
- 标识组:制品名称、版本号、制品类型(jar/npm/wheel/docker image)、制品坐标(group/artifact)。
- 依赖组:直接依赖清单、锁文件哈希、基础镜像 digest、SBOM 引用地址。
- 构建组:项目路径、流水线 ID、Job ID、提交 SHA、分支/Tag、构建时间、Runner 标签、构建工具版本。
- 治理组:环境(dev/staging/prod)、审批状态、扫描结论、签名指纹、保留策略。
版本号建议统一采用语义化版本 MAJOR.MINOR.PATCH[-预发布][+构建元数据],例如 2.4.1-rc.1+20240612.sha8f3a1c。其中 + 后的构建元数据不参与优先级比较,只用于追溯,正好承载提交短 SHA 与构建日期。依赖组必须记录“锁文件哈希”,因为同一个 package.json 在不同时间解析出的依赖树可能不同,锁文件哈希才是可复现的锚点。
二、用 CI 变量自动注入,避免人工填写
GitLab CI 在流水线运行时预置了大量变量,可直接作为元数据来源:CI_PROJECT_PATH、CI_PIPELINE_ID、CI_JOB_ID、CI_COMMIT_SHA、CI_COMMIT_REF_NAME、CI_COMMIT_TAG、CI_PIPELINE_CREATED_AT、CI_RUNNER_TAGS。这些变量由 GitLab 注入,不需要在脚本里手工赋值,天然保证与流水线一致。
下面是一个生成元数据文件的 Job 片段,它在构建完成后产出 metadata.json,并作为制品上传,供后续发布阶段读取:
generate-metadata:
stage: package
script:
- |
cat > metadata.json <<EOF
{
"name": "${CI_PROJECT_NAME}",
"version": "${CI_COMMIT_TAG:-0.0.0-${CI_PIPELINE_ID}}",
"type": "jar",
"dependencies": {
"lockfile_sha256": "$(sha256sum pom.xml | cut -d' ' -f1)"
},
"build": {
"project": "${CI_PROJECT_PATH}",
"pipeline_id": "${CI_PIPELINE_ID}",
"job_id": "${CI_JOB_ID}",
"commit_sha": "${CI_COMMIT_SHA}",
"ref": "${CI_COMMIT_REF_NAME}",
"created_at": "${CI_PIPELINE_CREATED_AT}",
"runner_tags": "${CI_RUNNER_TAGS}"
},
"governance": {
"environment": "${DEPLOY_ENV:-dev}",
"scan_status": "pending"
}
}
EOF
artifacts:
paths:
- metadata.json
expire_in: 30 days
这里有两个关键点。第一,版本号优先取 Tag;没有 Tag 时用 0.0.0-${CI_PIPELINE_ID} 作为临时版本,避免出现空版本。第二,scan_status 初始为 pending,由后续安全扫描 Job 回写,形成“构建—扫描—发布”的状态链,而不是一次性写死。
三、把元数据写入可查询的存储
元数据文件随制品上传只是第一步,真正可用的元数据必须能被检索。推荐两条路径并行:
- 制品仓库原生元数据:GitLab Package Registry 对 Maven、npm、PyPI 等格式会自动记录坐标、版本、上传时间,可通过
GET /projects/:id/packages查询。这是最省力的基础层。 - 集中式元数据表:用 GitLab API 在流水线中把
metadata.json推送到外部数据库或对象存储,形成跨项目、跨格式的统一索引,支持按提交 SHA、流水线 ID、依赖哈希反查。
下面示例用 GitLab API 读取当前流水线信息,并与本地元数据合并后写入集中存储:
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/pipelines/$CI_PIPELINE_ID" \
| jq '{status, ref, sha, created_at}' > pipeline.json
jq -s '.[0] * .[1]' metadata.json pipeline.json > merged.json
curl -X POST "$META_STORE_URL/artifacts" \
-H "Content-Type: application/json" \
-d @merged.json
合并后的记录应至少包含:制品坐标、版本、提交 SHA、流水线状态、依赖锁哈希、扫描结论。这样当生产环境出现问题时,可以用提交 SHA 或镜像 digest 直接反查“这个制品由哪条流水线、哪个 Job、哪次提交产生”,把排障时间从小时级压缩到分钟级。
四、元数据采集的校验与治理
自动注入不等于可以放任。建议在流水线中加入校验 Job:检查版本号是否符合语义化版本正则、依赖锁哈希是否存在、提交 SHA 是否为 40 位十六进制、必填字段是否为空。任一校验失败即阻断发布,避免“脏元数据”进入制品库。
最后,元数据要设置保留与清理策略。构建元数据随制品保留期一致即可;治理组中的审批与扫描结论建议长期保留,用于审计。对同一坐标的多次构建,用流水线 ID 或构建时间区分,避免覆盖导致追溯断链。