CONTRIBUTING.md
Rama main · réplica de lectura del repositorio original.
# Contributing to Podspine
Thanks for your interest in improving Podspine! This is a small, deliberately
narrow tool — files in, per-chapter podcast feed out — so the best contributions
keep that scope sharp. Bug fixes, format/edge-case handling, and docs are all
very welcome.
## Before you start
- For anything non-trivial, please **open an issue first** so we can agree on the
approach before you write code.
- Check existing [issues](https://github.com/schubydoo/podspine/issues) and the
[CHANGELOG](CHANGELOG.md) so you're not duplicating work.
- Out of scope by design: user accounts, a built-in player, ebooks, a full media
library, and **any form of DRM circumvention** — please don't propose these.
## Development setup
Podspine is a Rust workspace (one crate per pipeline stage). You need:
- A recent stable **Rust** toolchain (see `rust-toolchain.toml` / `Cargo.toml`
for the minimum version).
- **`ffmpeg` and `ffprobe` on your `PATH`** — the prober/splitter shell out to
them, and several tests synthesize fixtures with ffmpeg.
```bash
cargo build
cargo run -- --library ./sample-books # → http://localhost:8080
```
## Checks (all must pass — CI enforces them)
```bash
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
```
Please run these locally before opening a PR. Tests that need ffmpeg are gated on
its availability, so run them with ffmpeg installed to actually exercise them.
## Branching & commits
- Branch off `main`; PRs target `main`.
- Use [Conventional Commits](https://www.conventionalcommits.org/) for messages,
e.g. `feat(scanner): …`, `fix(feed): …`, `docs: …`.
- Keep changes focused and the diff minimal — small, reviewable PRs merge faster.
## Pull requests
- Fill in the PR template and link the issue it addresses.
- Include tests for new behavior or bug fixes where practical.
- Update the docs (`README.md`, `docs/`) when your change is user-visible.
- **Add a changeset** for any user-visible change — create `.changeset/<slug>.md`
(or run `knope document-change`) with YAML front matter and a **single-line** body:
```markdown
---
default: minor
---
Ingest Opus files with embedded chapters
```
- `default:` is one of `major` (breaking), `minor` (feature → Features), `patch`
(fix → Fixes), `security`, or `perf`.
- The body must be **exactly one line** — knope renders any second line/paragraph
as a `#### heading` mid-list instead of a bullet, which corrupts the release
notes. Fold all detail into that one sentence. (`scripts/lint_changesets.sh`,
run by pre-commit, enforces this.)
- **Never put a `README.md` or other doc in `.changeset/`** — knope parses every
`.md` there and a non-fragment fails the release with "missing front matter".
Releases and `CHANGELOG.md` are generated from these fragments — don't hand-edit
the changelog. Internal-only PRs (CI, refactor, tests) skip it with the
`no-changelog` label.
## A few project-specific rules worth knowing
These are the invariants that keep feeds correct and the server safe — reviewers
will look for them:
- **Feed ordering is sacred.** `pubDate`s are sequential with oldest = chapter 1;
always emit `itunes:episode`, `itunes:duration`, and an `enclosure length` read
from the real output file (never prorated from bitrate).
- **ffmpeg is invoked with an argv vector, never a shell string** — chapter
titles and filenames are untrusted.
- **Book/episode ids are opaque keys.** Never build a path from user input;
canonicalize and assert it stays under the data directory.
- Keep `ffmpeg`/`ffprobe` out-of-process (the GPL boundary) and DRM-free-only.
## License
Podspine is licensed under **AGPL-3.0-only**. By contributing, you agree that
your contributions are licensed under the same terms.