AdvisoryHub Contributor Guide¶
This guide is for the developer — the person who changes AdvisoryHub's
code and docs. It covers the local dev environment, tests, the code-quality
gates, and the commit conventions. Its companions: the
../specification/ set (what the system is —
authoritative), the ../operations/ manual (running the
service in production), and releasing.md (the maintainer
runbook for cutting a release).
1. Before you change anything¶
The specification set in ../specification/ is the
single source of truth for what this system is and does. Read the
relevant file before making non-trivial changes, and cite INV-* IDs (from
invariant.md) in commits and PRs. Every
behavior change must update the affected spec file(s) in the same
commit/PR — a code/spec mismatch is a defect in whichever side drifted.
Any deviation from the spec requires explicit maintainer confirmation
before implementation.
2. Development environment¶
docker-compose.yml is dev-only — every value it sets is a fixture,
no real secrets, and no .env file is required. The one variable that's
genuinely random (the OIDC client secret) is minted by the kanidm
bootstrap script and written to a file compose loads automatically.
First-run flow:
docker login dhi.io # one-time: app images base on DHI (free Docker account)
docker compose up -d kanidm # start the dev OIDC provider
bash dev/kanidm/setup.sh # one-time: cert, users, OAuth2 client
docker compose up # web + worker pick up the secret
After that, plain docker compose up is enough.
Then in another terminal:
docker compose exec web python manage.py migrate
docker compose exec web python manage.py seed_demo \
--with-publish-repo /tmp/advisoryhub-pub.git
The app is served at http://localhost:8000. Sign in with
alice@example.org / correcthorsebatterystaple (the demo users seeded
by dev/kanidm/setup.sh align 1:1 with what seed_demo creates Django-side)
and walk a draft → review → publish flow end-to-end.
To reset the dev environment:
docker compose down -v # wipes Postgres + kanidm volumes
docker compose build # down -v keeps cached images; rebuild after a Dockerfile/uv.lock change
docker compose up -d kanidm
bash dev/kanidm/setup.sh
docker compose up
3. With mise (optional)¶
If you use mise, it wraps the flows above so you don't have to remember the individual commands:
mise trust && mise run setup # install uv + prek, sync the locked .venv, wire git hooks
mise run kanidm-up # start the dev OIDC provider
mise run kanidm-setup # one-time: cert, users, OAuth2 client
mise run up # web + worker (full stack)
mise run migrate && mise run seed
mise tasks lists them all (test, lint, fix, typecheck, ty,
check, build, reset, docs-build, docs-serve, …). mise is a convenience wrapper only: tool versions live in
uv.lock, the Python version in .python-version, and CI runs these same tasks —
the raw uv / docker compose commands above stay canonical.
4. Configuration¶
docker-compose.yml's x-django-env anchor is the canonical dev
configuration (reused by web and worker); don't edit env files for
dev. For production, .env.example documents every knob with
secret-vs-config markers — it is a reference for whatever secret manager
or platform manifest your deploy uses (Kubernetes Secrets, Docker Swarm
secrets, AWS SSM, …) and is not loaded by docker-compose. The full
env-var inventory with groups, defaults, and descriptions is in
architecture.md §7, and the step-by-step
operator guide (install, run, integrate, operate) is in
../operations/.
5. Running tests¶
Tests run against PostgreSQL (the same engine as prod), so start it first:
docker compose up -d postgres # or `mise run up` for the full dev stack
DJANGO_SETTINGS_MODULE=config.settings.test pytest
config.settings.test defaults to the local compose Postgres; set
TEST_DATABASE_URL to target a different host/port. --reuse-db (in
addopts) keeps the test database between runs — pass --create-db after a
migration change. Tests do not require a real OIDC provider, real email
delivery, or a real Git remote (the publication tests use a temporary local
bare repo and skip if git isn't on PATH). Testing strategy and conventions
are documented in architecture.md §9.
6. Code quality¶
Lint, format, type, and Django checks run locally through
prek — the fast Rust reimplementation of
pre-commit — from .pre-commit-config.yaml. The
hooks invoke the Python tools out of the project venv, so they run the exact
versions pinned in uv.lock: what passes locally is what CI runs.
mise run setup # one-shot: installs uv+prek, syncs .venv, wires hooks
# …or by hand:
uv sync --extra dev # install the pinned ruff / mypy the hooks call
uv tool install prek # or: pipx install prek / cargo install prek / mise install
prek install # wire up the pre-commit AND pre-push git hooks
Once installed the hooks fire automatically:
- on commit — file hygiene (trailing whitespace, end-of-file, merge
markers, private-key detection, …) plus
ruff check --fixandruff format. - on push — additionally
mypy(+ django-stubs),manage.py makemigrations --check,manage.py check, and apip-auditdependency audit (mirrors CI's security job; needs network).
Run them on demand any time:
prek run --all-files # commit-stage checks
prek run --all-files --hook-stage pre-push # + type, Django & dependency-audit checks
prek run --all-files --hook-stage manual # advisory `ty` type-check
Vendored assets & their updates¶
Upstream-verbatim files committed into the repo — htmx (static/htmx.min.js), the
Inter fonts, the ALTCHA widget (static/altcha/), the neoteroi docs CSS, and the
OSV/CSAF/CVE/CVSS JSON schemas (publication/schemas/) — are each pinned in a
*.VERSION (or SCHEMAS.VERSION) file recording the upstream version + SHA-256.
mise run verify-vendor (a commit-stage hook + CI) fails if a committed file drifts
from its pinned hash.
Updates are automated by a scoped, self-hosted Renovate workflow
(.github/workflows/renovate.yml) that tracks only those .VERSION files —
Dependabot still owns Python/Actions/Docker, so the two never collide. On a new
upstream release Renovate bumps the version and runs dev/update_vendored_assets.py
(mise run update-vendor) to re-download, rehash, and re-apply the OSV ECL- patch,
then opens a PR.
Auto-merge policy: the JSON schemas are payload-validated by the publication test
suite (plus the OSV ecosystem drift guard), so a breaking change fails CI — their PRs
auto-merge on green. The frontend assets (htmx / ALTCHA / Inter / neoteroi) have
no behavioral tests, so their PRs are review-only — do a quick manual smoke (the
ALTCHA widget on /report/, an HTMX action, the OAD-rendered API docs page) before
merging. Re-vendor by hand any time with mise run update-vendor.
7. Commits & pull requests¶
- Every commit message follows the Conventional Commits specification.
- Every commit is signed (
-S) and signed-off (-s). - Cite the
INV-*IDs your change touches in the commit message and PR description, and update the affected spec file(s) in the same commit/PR (see §1). - AI-assisted commits carry an
Assisted-by: <agent>:<model>trailer in the footer — e.g.Assisted-by: Claude:claude-sonnet-4-6— and never aCo-Authored-Bytrailer.
8. Releasing¶
Releases are tag-driven: one signed vX.Y.Z tag produces the container
image, the Helm chart, the GitHub release, and the versioned documentation
site automatically. The maintainer runbook — version lockstep, cutting and
verifying a release, failure recovery — is releasing.md.
9. Documentation¶
Everything under docs/ is published as a versioned site at
https://eclipse-csi.github.io/advisoryhub/ (.github/workflows/docs.yml):
latest is the newest release, numbered versions are immutable per-release
snapshots (deployed on every vX.Y.Z tag), and dev tracks main. The
site is built with MkDocs + mkdocs-material and versioned with
mike onto the gh-pages branch — the
branch is only the version-state store; the workflow deploys its content to
Pages itself (source: GitHub Actions), so nothing goes live on a bare
branch push.
mise run docs-serve # live preview at http://127.0.0.1:8001
mise run docs-build # strict build — the PR and deploy gate
mise run docs-deploy -- dev # rehearse a mike deploy into a LOCAL gh-pages branch
Link rules (enforced by mkdocs build --strict on every docs PR):
- Inside
docs/, link relative to the current file and always to the.mdfile itself (../operations/README.md, not../operations/). - Anything outside
docs/(source files, charts, repo configs) gets an absolute GitHub URL — those files aren't part of the site.