Skip to content
Apothem
Governance

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:

  1. GitHub-native workflow status badges. A workflow's badge URL has the shape https://github.com/<owner>/<repo>/actions/workflows/<file>/badge.svg and reflects the latest run on the default branch. GitHub serves the badge from the repo's public metadata.
  2. 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 at apothem.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.
  3. 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

BadgeFormSourceURL shape
CI statusGitHub-native.github/workflows/ci.ymlhttps://github.com/ahmed-g-gad/apothem/actions/workflows/ci.yml/badge.svg?branch=main
Last commitGitHub-nativerepo metadatahttps://img.shields.io/github/last-commit/ahmed-g-gad/apothem
Licenseshields.io staticLICENSE (MIT)https://img.shields.io/badge/License-MIT-yellow.svg
Latest releaseshields.io endpointbadges/release.jsonhttps://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/release.json
Coverageshields.io endpointbadges/coverage.jsonhttps://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/coverage.json
Securityshields.io endpointbadges/security.jsonhttps://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/security.json
Supply chainshields.io endpointbadges/supply-chain.jsonhttps://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/supply-chain.json
Docsshields.io endpointbadges/docs.jsonhttps://img.shields.io/endpoint?url=https://apothem.ahmedgad.com/badges/docs.json
Python versionshields.io staticpyproject.toml python_requireshttps://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_run after the ci workflow completes (and on manual workflow_dispatch for re-publication without a CI run).
  • Resolves the latest tag from git describe --tags --abbrev=0 and the parent CI conclusion from the workflow_run event.
  • Generates each JSON via jq -n so the output strictly matches the shields.io endpoint schema (schemaVersion: 1, plus label, message, color).
  • Validates every emitted JSON with jq -e before 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.yml alongside the existing five and add the apothem.ahmedgad.com/badges/<name>.json consumer 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.

On this page