Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,11 @@ Presentation/
> `Web/Components/Pages/` — `Home · Search · Activity · Settings · Mappings · Login · Logout · Setup`.

Each host is an **independently deployable microservice** from day one. **Adding a broadcaster** =
a new `Application/<Broadcaster>` slice + an Infrastructure HTTP client + a `Presentation/Agents/<Broadcaster>` host.
an Infrastructure HTTP client + an `IBroadcasterCrawler` adapter + a `Presentation/Agents/<Broadcaster>`
host — **no new Application slice**: `Application/Crawling` is shared and selects a crawler by provider
key. The four registrations that are easy to miss (slnx · AppHost · `Build.Publish` Services ·
the Newznab host's on-demand block, without which search never reaches the new broadcaster) are in
[`docs/adding-a-broadcaster.md`](docs/adding-a-broadcaster.md).

### Enforced

Expand Down Expand Up @@ -193,12 +197,15 @@ dotnet fallout Test # same thing via the tool, which is what CI invokes

`develop` is the **default branch and integration trunk**; `main` is production and is the only branch
tagged `v*`. Work goes `feat|fix|chore|docs/*` → PR into `develop`. A release is `develop` (or a
`release/*` window) rebase-merged into `main`, then tagged. `hotfix/*` is cut from `main` and **must**
`release/*` window) **fast-forwarded** into `main`, then tagged. `hotfix/*` is cut from `main` and **must**
be ported back to `develop`.

- Every non-docs push to `develop` republishes the images as `:edge` (`PushEdge`).
- `GitHubRelease` **refuses** a tag that is not reachable from `main` or `support/*` — the trunk is
never tagged for release.
- **Never merge a release PR with GitHub's button.** It rewrites the commits, which severs the
commit→PR link the generated release notes are built from (v0.3.0 lost two entries that way).
Advance `main` with `git merge --ff-only develop && git push origin main`.
- Full model in [`docs/branching-and-release.md`](docs/branching-and-release.md), pipeline in
[`docs/ci.md`](docs/ci.md), runbooks in [`docs/releasing.md`](docs/releasing.md).

Expand Down
87 changes: 87 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Contributing

Krautwatch is a self-hosted Newznab indexer and SABnzbd download client for German public
broadcasters. Contributions are welcome — especially **new broadcasters**, which have a documented
shape of their own: [docs/adding-a-broadcaster.md](docs/adding-a-broadcaster.md).

## Getting set up

```bash
dotnet tool restore # Fallout + the Aspire CLI are pinned local tools
./build.sh Test # restore + compile + unit/architecture tests
dotnet run --project src/Presentation/AppHost # the whole fleet, via Aspire
```

You need **.NET 10**, **Docker running** (the repository tests use a real Postgres container via
Testcontainers) and **ffmpeg on PATH** if you're working on downloads.

`./build.sh TestLive` runs the tests that hit ARD and ZDF for real. They're excluded from the CI
gate because external APIs drift and rate-limit — run them yourself when you touch a crawler.

## Where work lands

The repo runs **GitFlow**: `develop` is the default branch and the integration trunk, `main` is what
is released.

```bash
git switch develop && git pull --ff-only
git switch -c feat/my-change
./build.sh Test
gh pr create --base develop --label enhancement
```

The full model — where a fix belongs, how releases are cut, what CI does at each step — is in
[docs/branching-and-release.md](docs/branching-and-release.md), [docs/ci.md](docs/ci.md) and
[docs/releasing.md](docs/releasing.md).

## Pull requests

**A PR title is a changelog line.** It appears verbatim in the release notes, months later, out of
context — so write an imperative sentence: "Serve the SABnzbd surface on /api", not
`fix(api): sab endpoint`. No `feat(scope):` prefixes, no bare issue numbers. Full guidance in
[docs/agents/issue-and-pr-style.md](docs/agents/issue-and-pr-style.md).

**Label the PR when you create it**, in the same `gh pr create --label …` call — the labels *are*
the changelog. One category from [`.github/release.yml`](.github/release.yml): `enhancement`, `bug`,
`breaking-change`, `security`, `documentation`, `dependencies`, or `skip-changelog` for
housekeeping.

Note that `dependencies` and `skip-changelog` are both excluded from the notes, so a PR carrying
`security` **and** `dependencies` vanishes entirely — a CVE-fixing bump gets `security` alone.

## What the reviewer will check

- **`./build.sh Test` is green**, architecture tests included. Four ArchUnitNET rules enforce the
hexagon: Domain depends on nothing, Application only on Domain, Infrastructure never on
Presentation, and no slice depends on a sibling slice.
- **The layering is respected.** Ports live in `Domain/Interfaces`; adapters in `Infrastructure`;
use-cases as vertical slices in `Application` with the CQRS/A split marked by banner comments
inside each file. [`CLAUDE.md`](CLAUDE.md) is the working map of the layout, and
`docs/architecture/` holds the decision records — **DR-009, DR-010 and DR-011 are current**; read
them before a structural change.
- **Generated files aren't hand-edited.** `.github/workflows/*.yml` comes from the
`[GitHubActions]` attributes in `build/Build.CI.GitHubActions.cs`, and the compose file comes from
the Aspire AppHost. Editing either by hand is silently undone.
- **Anything user-visible is documented** in the README, and anything structural gets a plan in
`docs/plans/` first — the convention is `YYYY-MM-DD - <title>.md`, written before implementation.

## Adding a broadcaster

The catalog is built entirely by per-broadcaster crawlers behind one port, `IBroadcasterCrawler`, so
adding ORF, SRF or arte is a bounded unit of work: an HTTP client, an adapter, an agent host, and
four registrations. The walkthrough, with the traps called out, is in
**[docs/adding-a-broadcaster.md](docs/adding-a-broadcaster.md)**.

## Reporting issues

Bugs and feature requests both belong in [GitHub issues](https://github.com/Chrison-dev/Krautwatch/issues).
For a crawl or download failure, the useful details are: the broadcaster, the show, what Sonarr
asked for, and the agent's log lines around the failure. For a geo-restricted asset, say whether
`Download:ProxyUrl` was configured — that's the difference between a bug and the documented
fail-fast.

## Legal

Krautwatch downloads freely available content from German public broadcasters' own official APIs for
personal, offline use, and circumvents no DRM. Contributions that add DRM circumvention, scrape
paywalled or commercial catalogs, or bypass a broadcaster's access controls will not be merged.
16 changes: 12 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@
</ItemGroup>

<ItemGroup Label="TheTVDB — first-party client (Chrison-dev/TvdbApi), used rather than hand-rolled">
<PackageVersion Include="TvdbClient" Version="4.7.12" />
<PackageVersion Include="TvdbClient.Abstractions" Version="4.7.12" />
<PackageVersion Include="TvdbClient.Models" Version="4.7.12" />
<!-- 4.7.13 moves its Scrutor.Extensions.HttpClient dependency to 7.0.1, whose
Microsoft.Extensions.Http range is [9.0.0, 11.0.0). 4.7.12 carried the 5.0.1 range that
stopped below 10.0.0, which is what made every project here report NU1608. -->
<PackageVersion Include="TvdbClient" Version="4.7.13" />
<PackageVersion Include="TvdbClient.Abstractions" Version="4.7.13" />
<PackageVersion Include="TvdbClient.Models" Version="4.7.13" />
</ItemGroup>

<ItemGroup Label="Compression">
Expand All @@ -78,7 +81,12 @@

<ItemGroup Label="Testing">
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.13.0" />
<!-- 4.14.0 is the first release off SSH.NET 2026.0.0, which patches GHSA-q939-rpr3-3284
(high: recursive ScpClient download writes arbitrary files from server-controlled
names). SSH.NET arrives here only as a Testcontainers transitive, so bumping the
parent is the fix — pinning the transitive would leave us guessing what Testcontainers
tested against. -->
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.14.0" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,11 @@ Domain ← Application ← Infrastructure
Wolverine is the mediator + bus + transactional outbox (**Postgres transport** by default — durable,
no extra container; RabbitMQ opt-in for scale-out).

Each host is an independently deployable microservice. **Adding a broadcaster** = a new Application
slice + an Infrastructure HTTP client + a `Presentation/Agents/<Broadcaster>` host.
Each host is an independently deployable microservice. **Adding a broadcaster** = an Infrastructure
HTTP client + an `IBroadcasterCrawler` adapter + a `Presentation/Agents/<Broadcaster>` host. The
`Application/Crawling` slice is shared and broadcaster-agnostic — it selects a crawler by provider
key and never learns their names. Walkthrough:
[`docs/adding-a-broadcaster.md`](docs/adding-a-broadcaster.md).

Decision records live in [`docs/architecture/`](docs/architecture/). The current ones are
**[DR-009](docs/architecture/DR-009-architecture-reset.md)** (architecture reset),
Expand Down Expand Up @@ -419,8 +422,9 @@ dotnet ef migrations add <Name> --project src/Infrastructure --context AppDbCont

## Contributing

The repo runs **GitFlow**: `develop` is the default branch and the integration trunk, `main` is what
is released, and every release is a `v*` tag on `main`.
Start with [`CONTRIBUTING.md`](CONTRIBUTING.md). The repo runs **GitFlow**: `develop` is the default
branch and the integration trunk, `main` is what is released, and every release is a `v*` tag on
`main`.

```bash
git switch develop && git pull --ff-only
Expand All @@ -429,6 +433,8 @@ git switch -c feat/my-change
gh pr create --base develop --label enhancement # one category label — the labels are the changelog
```

- [`docs/adding-a-broadcaster.md`](docs/adding-a-broadcaster.md) — the walkthrough for a new
Mediathek: HTTP client → `IBroadcasterCrawler` adapter → agent host, with the traps called out
- [`docs/branching-and-release.md`](docs/branching-and-release.md) — the branch model, where a fix
belongs, protection and merge methods
- [`docs/ci.md`](docs/ci.md) — what CI runs and why there is no hand-written YAML here
Expand Down
Loading
Loading