Skip to content

feat(badges): publish shields.io for-the-badge style as the canonical SVG#14

Merged
DevelopmentCats merged 3 commits into
mainfrom
cat/badges-for-the-badge-style
Jun 25, 2026
Merged

feat(badges): publish shields.io for-the-badge style as the canonical SVG#14
DevelopmentCats merged 3 commits into
mainfrom
cat/badges-for-the-badge-style

Conversation

@DevelopmentCats

Copy link
Copy Markdown
Collaborator

Changes

/api/v1/skills/<ns>/<slug>/badge/{status,score}.svg now serves the tall, ALL-CAPS, pill-rounded for-the-badge style from shields.io directly, replacing the narrow inline two-rect flat SVG. status_badge_svg() and score_badge_svg() call a new fetch_shields_io_svg() helper that hits https://img.shields.io/static/v1 with our label/message/color/style and a coder-skill-scanner User-Agent (shields 403s the default urllib UA). On any non-200 or network failure they fall back to the existing _flat_badge_svg() inline renderer so a publish job never ships a missing badge.

The JSON endpoints (/badge/<name>.json) are unchanged — third-party README authors who want a different style can still hit img.shields.io/endpoint?url=<our-json>&style=<whatever> themselves.

9 new pytest cases cover URL construction, the User-Agent header, HTTP-error and exception fallbacks, the shields-first happy path, and the score-badge parameter flow. The two existing SVG-content tests are mocked to the fallback so they keep exercising the inline renderer. 59/59 green, ruff clean. Real-payload smoke run writes 18 files of height-28 SVGs (MALICIOUS / 100/100 etc).

Why this was needed

The registry decision is to embed badges directly from the scanner rather than going through shields.io at view time — fewer runtime dependencies, one cache layer (the GitHub Pages CDN), and the "official" look encoded in the scanner output rather than the consumer URL. That means the scanner has to publish the SVG in the style we want, not just the JSON payload shields.io would render. for-the-badge is the chosen style — reviewed against flat / flat-square / plastic in the registry-server mockup branch.

This PR was prepared with help from Coder Agents.

… SVG

Decision: the scanner's /api/v1/skills/<ns>/<slug>/badge/{status,score}.svg endpoints now serve the tall, ALL-CAPS, pill-rounded for-the-badge style from shields.io directly, replacing the narrow inline two-rect flat SVG. registry.coder.com and any third-party README embedding the URL gets the same canonical look without going through shields.io themselves.

How: status_badge_svg() and score_badge_svg() call a new fetch_shields_io_svg() helper that hits https://img.shields.io/static/v1?label=...&message=...&color=...&style=for-the-badge with a 10s timeout and a coder-skill-scanner User-Agent (shields 403s the default urllib UA). On any non-200 or network failure they fall back to the existing _flat_badge_svg() inline renderer so a publish job never ships a missing badge.

The JSON endpoints (/badge/<name>.json) are unchanged - third-party consumers who want a different shields.io style can still hit img.shields.io/endpoint?url=<our-json>&style=<whatever> themselves.

Tests: 9 new pytest cases covering URL construction, User-Agent identification, HTTP-error and exception fallbacks, shields-first happy path, score-badge parameter flow. Existing fallback-path tests preserved via monkeypatch. 59/59 green, ruff clean. Real-payload smoke run produces 18 files, height-28 SVGs, MALICIOUS / 100/100 etc.
Copilot AI review requested due to automatic review settings June 25, 2026 18:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the scanner’s SVG badge endpoints to publish shields.io-rendered for-the-badge SVGs as the canonical output (with an inline flat-style fallback) so the registry can embed the “official” look without relying on shields at view time.

Changes:

  • Added a fetch_shields_io_svg() helper plus DEFAULT_BADGE_STYLE, and switched status_badge_svg() / score_badge_svg() to prefer shields.io SVGs with a local fallback.
  • Expanded pytest coverage for shields URL/header construction and fallback behavior, and adjusted existing SVG tests to exercise the fallback renderer.
  • Relaxed the API wiring test to accept the for-the-badge uppercase rendering by comparing case-insensitively.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
scanner/badges.py Adds shields.io SVG fetching and makes shields-rendered for-the-badge the default SVG output with an inline fallback.
tests/test_badges.py Updates existing SVG tests to force fallback, and adds new tests for shields fetch behavior and parameter flow.
tests/test_api.py Updates badge SVG assertion to be case-insensitive due to for-the-badge uppercasing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scanner/badges.py
Comment thread scanner/badges.py Outdated
Two Copilot review comments addressed.

1. scanner/badges.py module docstring rewritten. The old text claimed the SVG endpoint was a flat-style inline renderer with 'no shields.io dependency, no network hop' and that the two-rect flat layout was the contract. After this branch's switch to for-the-badge as the canonical SVG, that text is no longer true. New docstring lays out the two surfaces (JSON contract unchanged from v1; SVG is shields-fetched for-the-badge with an inline flat fallback) and explains why the fallback is intentionally a different visual style.

2. Per-run circuit breaker on fetch_shields_io_svg. Before: every badge in a publish job hit shields.io independently, so a slow or down shields.io could stall the job for (n_skills * n_badges) * timeout seconds in the worst case (60s for the current 3 skills * 2 badges with the 10s timeout). After: the first failure inside a run flips a module-level flag and every subsequent call short-circuits to None without touching the network, bounding the worst-case stall at exactly one timeout per publish.

Added reset_shields_circuit_breaker() helper exposed for tests, an autouse pytest fixture in tests/test_badges.py that resets the flag between tests (otherwise fail-path tests would leak state into success-path tests), and two new pytest cases covering the breaker (short-circuits after first failure; resets cleanly). 61/61 green, ruff clean.
Drops ~55 net lines of prose from scanner/badges.py and tests/test_badges.py without changing behaviour. Module docstring goes from a structured essay to four lines that name the two surfaces. Per-line constant comments cut to one line each (or removed when the constant is self-explanatory like the 10s timeout). Circuit-breaker variable explanation goes from a paragraph to two lines. Log warning suffixes go from 'disabling shields fetch for the rest of this run' to 'disabling fetch this run'. Test fixture docstring goes from five lines to one. 61/61 green, ruff clean.
@DevelopmentCats DevelopmentCats merged commit cee7caf into main Jun 25, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants