Badge policy
Specify dynamic and static badge sources for the README, covering GitHub-native workflow status, shields.io endpoint JSON, and static badge forms.
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
Static https://img.shields.io/badge/X-Y-Z URLs never change on their
own, so they drift away from the project's real state — a
release-vN-blue badge keeps reading vN long after vN+1 ships. The
policy therefore requires dynamic forms for every value that moves, and
reserves static badges for the small set of values (license, Python
target version) that rarely change, where 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 public metadata. - 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 metrics under project-controlled publication rather than a third-party mutable store. - 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. Shields.io endpoints over the project-controlled domain are the canonical substitute.
3. Per-badge specification
| Badge | Form | Source | URL shape |
|---|---|---|---|
| CI status | GitHub-native | .github/workflows/ci.yml | https://github.com/ahmed-g-gad/apothem/actions/workflows/ci.yml/badge.svg?branch=main |
| Last commit | GitHub-native | repo metadata | https://img.shields.io/github/last-commit/ahmed-g-gad/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 |
CodeQL runs as a dedicated .github/workflows/codeql.yml workflow that
analyzes the Python and Actions surfaces; the security-scan job in
ci.yml runs bandit only, leaving CodeQL SARIF publication to
codeql.yml. The security badge consumes the endpoint JSON published by
the badges workflow rather than a GitHub-native workflow badge, keeping
the badge stable across the underlying scanner topology.
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.
The deployment of the artifacts to apothem.ahmedgad.com/badges/
is handled by .github/workflows/publish-static-site.yml. During the site
build, that workflow downloads the latest successful badges artifact and
stages it under site/dist/badges/. If the artifact is absent or expired, the
site still deploys and records the missing badge artifact in workflow logs
instead of publishing broken JSON.
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 artifact the workflow does not produce.
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.