Badges are served at https://badges.hoppou.dev. The docs cover
every parameter, with examples:
GET /badge?style=pill&title=Built%20with&label=Rust&color=f74c00&icon=simple:rust
GET /ci/{owner}/{repo}/{workflow}?style=pillTwo Cloudflare Workers share badges.hoppou.dev:
| Directory | Worker | Serves |
|---|---|---|
backend/ |
badges |
The badge API (Rust), on the routes /badge* and /ci/* |
frontend/ |
badges-docs |
Everything else (SvelteKit). It holds the Custom Domain, so it is the fallback |
Routes run before a Custom Domain, so badge paths reach the API and all other paths fall through to
the frontend. badge* also matches paths such as /badges, so frontend pages must not start with
/badge.
The API is the badges crate in backend/, a member of the Cargo workspace at the repo root. Cargo
commands run from the root, and wrangler runs from backend/.
Git hooks run through prek, configured in prek.toml. Before each commit
they:
- fix trailing whitespace and missing final newlines, and reject large files
- format TOML with tombi and Rust with rustfmt
- lint and format JS, TS and Svelte with ESLint (
frontend/eslint.config.js) - lint and format JSON, CSS and HTML with Biome (
biome.json) - lint and rewrap Markdown with rumdl (
.rumdl.toml) - run clippy
A formatter that changes a file fails the commit, so review and stage its edits, then commit again.
On git commit without a conventional -m, koji prompts
for one, with scopes autocompleted from history (.koji.toml).
Install the tools and hooks once per clone. ESLint and Biome come from the frontend's dependencies:
cargo install cargo-binstall # once, fetches prebuilt binaries for the line below
cargo binstall prek koji rumdl
uv tool install tombi # or npm i -g tombi (the crates.io crate is only a placeholder)
(cd frontend && pnpm install)
prek install# Test
cargo test
# Render a badge locally (any format)
cargo run --example render -- "title=Built with" label=Sass color=cd6699 format=png > sass.png
# Render a workflow status badge in any state, without GitHub (or use /ci/...?state=running)
cargo run --example render -- state=running style=pill > running.svg
# Preview the API locally (put GITHUB_TOKEN=... in backend/.dev.vars for /ci)
cd backend && npx wrangler devThe frontend is a SvelteKit app in frontend/ that uses pnpm. Its
README covers its commands and how to write docs pages.
Pushing a v* tag deploys both Workers (the API from backend/, the frontend from frontend/)
and, once both succeed, publishes a GitHub release with notes from
cocogitto, rendered by changelog.tera. The tag must match the
backend/Cargo.toml version, so cut releases with cog bump from the repo root, configured in
cog.toml. It refuses to run off main, with uncommitted changes or with non-conventional commits
since the last tag. It then sets the version in backend/Cargo.toml and Cargo.lock through
cargo-release
(cargo binstall cocogitto cargo-release), commits, tags and pushes:
# Preview the next version and its release notes
cog bump --auto --dry-run
cog changelog "$(git describe --tags --abbrev=0).."
cog bump --auto # or --patch, --minor, --major, --version X.Y.ZThe workflow needs a CLOUDFLARE_API_TOKEN repository secret.
# From backend/
# Required for /ci
npx wrangler secret put GITHUB_TOKEN
# Manual API deploy, bypassing a release
npx wrangler deployTo deploy the frontend outside a release:
cd frontend
pnpm build
pnpm exec wrangler deployNever commit tokens: backend/.dev.vars is ignored by git, and production uses the Worker secret.
This project is licensed under the MIT License. See LICENSE.md.
The bundled Inter and JetBrains Mono fonts are licensed under the SIL Open Font License (Inter, JetBrains Mono). Icons come from Simple Icons (CC0) and Lucide (ISC, bundled marks).