徽章策略
为 README 规定动态与静态徽章来源,涵盖 GitHub 原生工作流状态、shields.io 端点 JSON 以及静态徽章形式。
本文档是 README 徽章条以及发布其动态数据源的基础设施的规范参考。项目 README 中的每个徽章都通过此处规定的三种形式之一解析;将来新增的徽章遵循相同的形式,否则会在评审时被拒绝。
1. 背景
静态 https://img.shields.io/badge/X-Y-Z URL 永远不会自行变化,因此会偏离项目的真实状态——一个 release-vN-blue 徽章在 vN+1 发布很久之后仍然显示 vN。因此,本策略要求每个会变动的值都采用动态形式,并将静态徽章保留给那一小组很少变化的值(许可证、Python 目标版本),对这些值而言,静态形式是如实的。
2. 架构
四种形式涵盖项目中的每个徽章:
- GitHub 原生工作流状态徽章。 工作流的徽章 URL 形如
https://github.com/<owner>/<repo>/actions/workflows/<file>/badge.svg,反映默认分支上的最新一次运行。GitHub 从仓库的公开元数据中提供该徽章。 - shields.io 端点徽章。 shields.io 端点徽章解析一个
https://img.shields.io/endpoint?url=<published-json>URL,其中的 JSON 符合 shields.io 端点 schema(schemaVersion、label、message、color)。发布的 JSON 位于apothem.ahmedgad.com/badges/*.json;其内容由一个 CI 工作流在每次成功的 CI 运行时生成。端点徽章将指标置于项目可控的发布之下,而不是放在第三方的可变存储中。 - shields.io 静态徽章。 静态徽章形如
https://img.shields.io/badge/<label>-<message>-<color>,没有响应式数据源。保留给确实很少变化的值(许可证标识符、Python 目标版本)。 - 注册表与 Scorecard 徽章。 其值由第三方根据其已持有的公开数据计算得出的徽章:npm 注册表(版本和下载量,由 shields.io 渲染)以及 OpenSSF Scorecard API。项目不为这些徽章发布任何内容;第三方即是来源。
按设计,排除通过公开 Gist 发布项目指标的做法。基于项目可控域名的 Shields.io 端点是规范的替代方案。
3. 逐徽章规范
下表按顺序列出 README 徽章条。最后一列标出那些仅在仓库公开时才会渲染图像的徽章:对于私有仓库,GitHub 只向已登录的成员提供工作流徽章,而 Scorecard 只扫描公开仓库。
| 徽章 | 形式 | 来源 | URL 形状 | 需要公开仓库 |
|---|---|---|---|---|
| 最新发布 | shields.io 端点 | badges/release.json | https://img.shields.io/endpoint?url=https%3A%2F%2Fapothem.ahmedgad.com%2Fbadges%2Frelease.json | 否 |
| CI 状态 | GitHub 原生 | .github/workflows/ci.yml | https://github.com/ahmed-g-gad/apothem/actions/workflows/ci.yml/badge.svg?branch=main | 是 |
| 许可证 | shields.io 静态 | LICENSE(MIT) | https://img.shields.io/badge/license-MIT-0F172A | 否 |
| npm 版本 | 注册表 | npm 注册表 | https://img.shields.io/npm/v/%40ahmed-g-gad%2Fapothem?color=10B981&label=npm | 否 |
| 覆盖率闸门 | shields.io 静态 | pyproject.toml fail_under = 80 | https://img.shields.io/badge/coverage%20gate-%E2%89%A580%25-2563EB | 否 |
| OpenSSF Scorecard | Scorecard | .github/workflows/scorecard.yml 结果 | https://api.securityscorecards.dev/projects/github.com/ahmed-g-gad/apothem/badge | 是 |
| 反馈 | shields.io 静态 | GitHub Issues | https://img.shields.io/badge/feedback-GitHub%20issues-7C3AED | 否 |
| 文档 | shields.io 静态 | 文档站点 | https://img.shields.io/badge/docs-Fumadocs-0F172A | 否 |
| npm 下载量 | 注册表 | npm 注册表 | https://img.shields.io/npm/dm/%40ahmed-g-gad%2Fapothem?color=10B981&label=downloads | 否 |
每个徽章在 github.com 上的链接目标(发布、工作流、许可证文件、问题)都仅在仓库公开时才能打开。
badges 工作流还会为将来的徽章发布 coverage.json、security.json、supply-chain.json 和 docs.json 端点文件;README 目前不显示它们。
CodeQL 作为专用的 .github/workflows/codeql.yml 工作流运行,分析 Python 和 Actions 两个表面;ci.yml 中的 security-scan 作业只运行 bandit,将 CodeQL SARIF 的发布交给 codeql.yml。安全徽章使用 badges 工作流发布的端点 JSON,而不是 GitHub 原生工作流徽章,从而使该徽章在底层扫描器拓扑变化时保持稳定。
4. JSON 发布机制
端点 JSON 由 .github/workflows/badges.yml 生成。该工作流:
- 在
ci工作流完成后由workflow_run触发(也可通过手动workflow_dispatch触发,以便在没有 CI 运行的情况下重新发布)。 - 在完整历史的检出上(
fetch-depth: 0;在深度为 1 的检出上,没有任何带标签的提交可达,徽章会显示unreleased)用git describe --tags --abbrev=0 --match 'v[0-9]*.[0-9]*.[0-9]*'解析最新的发布标签,并从workflow_run事件解析父级 CI 的结论。 - 通过
jq -n生成每个 JSON,使输出严格符合 shields.io 端点 schema(schemaVersion: 1,以及label、message、color)。 - 在上传之前用
jq -e校验每个生成的 JSON。 - 将该目录作为名为
badges的工作流制品上传。
将这些制品部署到 apothem.ahmedgad.com/badges/ 由 .github/workflows/publish-static-site.yml 负责。在站点构建期间,该工作流下载最新一次成功的 badges 制品,并将其暂存到 site/dist/badges/ 下。如果该制品缺失或已过期,站点仍会部署,并在工作流日志中记录缺失的徽章制品,而不是发布损坏的 JSON。
5. 维护协议
新增徽章时,先决定其形式:
- 如果徽章反映某个 CI 工作流的运行状态,优先采用 GitHub 原生形式。徽章的 URL 由工作流文件确定;不需要任何发布基础设施。
- 如果徽章反映一个随时间更新的指标(计数、百分比、版本字符串、分级标签),使用 shields.io 端点形式。在
.github/workflows/badges.yml中现有的五个步骤旁添加一个 JSON 生成步骤,并将apothem.ahmedgad.com/badges/<name>.json使用方 URL 添加到 README 徽章条中。 - 如果徽章反映一个确实很少变化的值(许可证、目标语言、治理模式),使用 shields.io 静态形式。新增该徽章时,在本文件的逐徽章表格中记录其变化节奏。
退役徽章时,先将其从 README 中移除,然后从 badges 工作流中移除其 JSON 生成步骤,再从上方的逐徽章表格中移除其所在行。保持删除操作的原子性,使 README 永远不会引用工作流不生成的 JSON 制品。
6. 交叉引用
- GitHub 原生 CI 徽章所反映的运行状态所属的 CI 工作流:
.github/workflows/ci.yml。 - 发布端点 JSON 的 badges 工作流:
.github/workflows/badges.yml。 - 提供这些 JSON 的静态站点域名:
apothem.ahmedgad.com。 - shields.io 端点 schema 参考:https://shields.io/badges/endpoint-badge。