Badge policy¶
This document is the canonical reference for the README badge strip and the infrastructure that publishes its dynamic data sources. Every badge in the project's README resolves through one of three forms specified here; new badges added in the future follow the same forms or are rejected at review.
1. Background¶
The README originally carried static https://img.shields.io/badge/X-Y-Z
URLs whose color and message never changed. Static-only badges drift away
from the project's real state — a release-v0.1.0-blue badge keeps reading
v0.1.0 long after v0.2.0 ships. The remediation replaces the drifting
badges with dynamic forms while keeping a small set of genuinely-static
badges (license, Python target version) where the displayed value rarely
changes and the static form is honest.
2. Architecture¶
Three forms cover every badge in the project:
- GitHub-native workflow status badges. A workflow's badge URL has the
shape
https://github.com/<owner>/<repo>/actions/workflows/<file>/badge.svgand reflects the latest run on the default branch. GitHub serves the badge from the repo's metadata; the badge works for collaborators on private repos without exposing internal status to the public. - shields.io endpoint badges. A shields.io endpoint badge resolves an
https://img.shields.io/endpoint?url=<published-json>URL where the JSON conforms to the shields.io endpoint schema (schemaVersion,label,message,color). The published JSON lives atapothem.ahmedgad.com/badges/*.json; its content is generated by a CI workflow on every successful CI run. Endpoint badges keep private-repo metrics off public infrastructure (no public Gist, no third-party metric store) while still reflecting real-time state. - shields.io static badges. A static badge has the shape
https://img.shields.io/badge/<label>-<message>-<color>with no reactive data source. Reserved for values that genuinely rarely change (license identifier, Python target version).
Public-Gist publication of project metrics is excluded by design — it would leak private-repo data through a public surface. Authentication-gated shields.io endpoints over project-controlled domain are the substitute.
3. Per-badge specification¶
| Badge | Form | Source | URL shape |
|---|---|---|---|
| CI status | GitHub-native | .github/workflows/ci.yml |
https://github.com/Gad360/apothem/actions/workflows/ci.yml/badge.svg?branch=main |
| Last commit | GitHub-native | repo metadata | https://img.shields.io/github/last-commit/Gad360/apothem |
| License | shields.io static | LICENSE (MIT) |
https://img.shields.io/badge/License-MIT-yellow.svg |
| Latest release | shields.io endpoint | badges/release.json |
https://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/release.json |
| Coverage | shields.io endpoint | badges/coverage.json |
https://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/coverage.json |
| Security | shields.io endpoint | badges/security.json |
https://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/security.json |
| Supply chain | shields.io endpoint | badges/supply-chain.json |
https://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/supply-chain.json |
| Docs | shields.io endpoint | badges/docs.json |
https://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/docs.json |
| Python version | shields.io static | pyproject.toml python_requires |
https://img.shields.io/badge/python-3.10%2B-3776AB?logo=python&logoColor=white |
The CodeQL workflow is currently embedded in the security-scan job of
ci.yml rather than a separate codeql.yml file; the security badge
therefore consumes the endpoint JSON published by the badges workflow
rather than a dedicated CodeQL workflow badge. If CodeQL later moves to
its own workflow, the security badge can be promoted to a GitHub-native
workflow badge without changing the README's surrounding markup.
4. JSON publication mechanism¶
The endpoint JSONs are produced by .github/workflows/badges.yml. The
workflow:
- Triggers on
workflow_runafter theciworkflow completes (and on manualworkflow_dispatchfor re-publication without a CI run). - Resolves the latest tag from
git describe --tags --abbrev=0and the parent CI conclusion from theworkflow_runevent. - Generates each JSON via
jq -nso the output strictly matches the shields.io endpoint schema (schemaVersion: 1, pluslabel,message,color). - Validates every emitted JSON with
jq -ebefore upload. - Uploads the directory as a workflow artifact named
badges. - Stops at a non-functional deploy placeholder until the static-site publication workflow lands; the placeholder exits non-zero so a regression cannot ship a silently-broken pipeline.
The deployment of the artifacts to apothem.ahmedgad.com/badges/
is the responsibility of the static-site publication workflow that
consumes the badges artifact uploaded above. Until that workflow
lands, the README badges resolve to the same apothem.ahmedgad.com
URL but the underlying JSON is published by a future workflow rather
than this one's deploy step.
5. Maintenance protocol¶
Add a new badge by deciding the form first:
- If the badge reflects the run-state of a CI workflow, prefer the GitHub-native form. The badge's URL is fixed by the workflow file; no publication infrastructure is needed.
- If the badge reflects a metric (a count, a percentage, a version
string, a graded label) that updates over time, use the shields.io
endpoint form. Add a JSON-generation step to
.github/workflows/badges.ymlalongside the existing five and add theapothem.ahmedgad.com/badges/<name>.jsonconsumer URL to the README badge strip. - If the badge reflects a value that genuinely rarely changes (license, language target, governance mode), use the shields.io static form. Document the change cadence in this file's per-badge table when adding the badge.
Retire a badge by removing it from the README first, then removing its JSON-generation step from the badges workflow, then removing its row from the per-badge table above. Keep the deletion atomic so the README never references a JSON the workflow no longer produces.
6. Cross-references¶
- The CI workflow whose run-state the GitHub-native CI badge reflects:
.github/workflows/ci.yml. - The badges workflow that publishes endpoint JSONs:
.github/workflows/badges.yml. - The static-site domain that serves the JSONs:
apothem.ahmedgad.com. - The shields.io endpoint schema reference: https://shields.io/badges/endpoint-badge.