--- name: houndarr-ci description: Houndarr's CI workflow reference and branch protection. Loads when reading or editing .github/workflows/. Covers the 11 required status checks (exact names matter for branch protection), the additional non-required workflows, the paths-ignore + ci-skip pattern that keeps docs-only PRs green, and the rule against changing required check job names. paths: - ".github/workflows/**" --- # Houndarr CI conventions ## Required checks (11; branch protection enforced) | Check name | Workflow file | What it runs | |------------|---------------|--------------| | Lint (ruff) | `quality.yml` | `ruff check .` | | Format (ruff) | `quality.yml` | `ruff format --check .` | | Type check (mypy) | `quality.yml` | `mypy src/` | | Test (Python 3.13) | `tests.yml` | `pytest -q --tb=short` + compile check + `--help` | | Dependency audit (pip-audit) | `security.yml` | `pip-audit` against `requirements.txt` + `requirements-dev.txt` generated on the fly via `uv export --frozen` from `pyproject.toml` + `uv.lock` | | SAST (bandit) | `security.yml` | `bandit -r src/ -c pyproject.toml` | | Trivy filesystem scan | `security.yml` | `trivy fs .` (CRITICAL/HIGH with known fix) | | Dependency review | `dependency-review.yml` | PR dependency diff vs GitHub Advisory Database | | Build (no push) | `docker.yml` | Multi-arch Docker build (amd64/arm64), no push | | Trivy image scan | `docker.yml` | Trivy scan of built Docker image (CRITICAL/HIGH with known fix) | | Security smoke test | `security-smoke-test.yml` | Live container: unauthenticated sweep, CSRF, XFF, rate limiting, API key exposure, container security | The six main workflows (`quality`, `tests`, `security`, `dependency-review`, `docker`, `security-smoke-test`) use `paths-ignore: ["docs/**", "**/*.md", "website/**", ".claude/**"]`. When a PR touches only those paths, `ci-skip.yml` provides passing no-op jobs with identical check names so branch protection is satisfied. ## Additional workflows (not required checks) | Workflow | Trigger | Purpose | |----------|---------|---------| | `version-check.yml` | PRs changing `VERSION` or `CHANGELOG.md` | Validates VERSION format, CHANGELOG heading match, allowed `###` headers, `---` separator | | `release.yml` | `v*` tag push | Validates VERSION == tag, extracts CHANGELOG block, creates GitHub Release | | `chart.yml` | `v*` tag push | Packages `charts/houndarr/` with version from `VERSION` file, pushes to `oci://ghcr.io/av1155/charts` | | `dockerfile-lint.yml` | Changes to `Dockerfile` | `hadolint Dockerfile` | | `workflow-lint.yml` | Changes to `.github/workflows/**` | `actionlint` via reviewdog | | `api-snapshot-refresh.yml` | Weekly (Monday 10:00 UTC) + manual | Fetches upstream Radarr/Sonarr/Whisparr/Lidarr/Readarr OpenAPI specs, updates `docs/api/` snapshots and `tests/test_docs_api.py` hashes, opens a PR if changed | | `pages.yml` | Pushes to `main` touching `website/**` | Deploys docs site to GitHub Pages | | `test-deploy.yml` | PRs touching `website/**` | Tests Docusaurus build without deploying | | `link-check.yml` | PRs touching `**/*.md`, `**/*.mdx`, `lychee.toml` + weekly (Monday 08:00 UTC) + manual | Runs `lychee` against every Markdown file to catch broken external links; rules live in `lychee.toml` | | `cleanup-actions-cache.yml` | Weekly (Monday 05:00 UTC) + manual | Prunes stale GitHub Actions caches | ## Branch protection on `main` - 11 required status checks (strict; branch must be up to date) - PRs required, stale reviews dismissed, conversation resolution required - Required approving reviews is 0, so no approval blocks a merge - Linear history enforced (no merge commits) - No force pushes, no branch deletions - Admin enforcement is off. The rules above bind contributors; the maintainer account can push to `main` and merge past a failing check. The hooks in `.claude/settings.json` are the real block on direct edits and pushes while on `main`. - CODEOWNERS assigns `@av1155` to every file, but code owner review is not a required gate ## Don't break this - Do not modify the 11 required check job names; branch protection depends on exact name matches. - Do not change `ci-skip.yml` job names without updating branch protection. - Keep `paths-ignore` patterns in sync across the six main workflows. - If mypy CI fails with "merge ref not found": push an empty commit to retrigger. - Keep every action pinned to a full 40-character commit SHA with the release version as the last token of a trailing comment (`uses: owner/repo@ # vX.Y.Z`). Dependabot rewrites the SHA and the comment together, but only while the version ends the comment. A tag or branch ref can be repointed by the action owner; a SHA cannot. When pinning by hand, dereference annotated tags to their commit: for those actions `git/ref/tags/vN` returns a tag object whose SHA the runner cannot resolve.