The repo had no SECURITY.md, CONTRIBUTING.md, code of conduct, or issue and PR templates. For a client that handles Jellyfin credentials and ships signed binaries, the missing one that actually matters is SECURITY.md: there was no stated way to report a vulnerability privately, so the only available channel was the public tracker. CONTRIBUTING.md documents the gates as they now stand, including the three ratchets and which direction each is allowed to move, and the two rules that surprise people: bug fixes start with a failing test, and Jellyfin's taxonomy stays in Rust. The bug template asks the three playback questions -- streaming or downloaded, transcoding or direct, music or video -- because those answers decide which of several very different code paths a report is about, and reconstructing them over several round trips is most of the cost of a playback bug report. docs/build/ci-operations.md is the missing operations manual: how to change the builder image and in what order (image pushed before the workflow that names it, or CI breaks), why tags are dated rather than :latest or per-SHA, what each secret is for, and what losing the updater private key would mean -- installed desktop clients only accept payloads signed by the key matching the public key they shipped with, so losing it means everyone reinstalls by hand. Disk exhaustion on the runner is documented as a manual check rather than a scheduled job. A daily job would occupy the only slot on a single-slot runner and pull the whole builder image to run `df` -- and `df` inside a container does not reliably describe the host's disk, so it would spend real build capacity reporting a number that might be wrong. What the doc records instead is the part that is actually hard to rediscover: the symptoms (cargo dying mid-link, docker refusing to pull, actions/cache quietly not saving) and that `docker volume prune` needs `-a` to touch named volumes, which is how it filled up unnoticed. Two things in these docs are stated plainly because they are true and were not written down anywhere: without branch protection every gate in the pipeline is advisory, and the Gitea instance -- canonical remote, signing secrets, registry, runner -- is not backed up by anything in this repository.
124 lines
5.2 KiB
Markdown
124 lines
5.2 KiB
Markdown
# Contributing to JellyTau
|
|
|
|
Thanks for looking. This file is the short version of how the project is built
|
|
and what has to be true before a change lands. The long version lives in
|
|
[CLAUDE.md](CLAUDE.md) and [docs/architecture/](docs/architecture/README.md),
|
|
which are maintained rather than decorative — read them before a structural
|
|
change.
|
|
|
|
## Getting set up
|
|
|
|
Package manager is **bun**. You will also need a Rust toolchain (the exact
|
|
version is pinned in [src-tauri/rust-toolchain.toml](src-tauri/rust-toolchain.toml)
|
|
— rustup honours it automatically) and the Tauri Linux dependencies.
|
|
|
|
```bash
|
|
bun install
|
|
bun run hooks:install # do this once: it enables the pre-commit gates
|
|
bun run tauri dev
|
|
```
|
|
|
|
`hooks:install` points `core.hooksPath` at [scripts/hooks/](scripts/hooks/), so
|
|
hook updates arrive with a `git pull` instead of needing a re-install.
|
|
|
|
## What has to pass
|
|
|
|
Everything below runs in CI, and the fast half runs in the pre-commit hook. None
|
|
of it is advisory:
|
|
|
|
```bash
|
|
bun run check # svelte-check — 0 errors
|
|
bun run test # vitest
|
|
bun run format:check # prettier
|
|
bun run lint # eslint — 0 errors; the warning count is a ratchet
|
|
bun run check:boundary # no Jellyfin taxonomy in the frontend
|
|
bun run test:rust # cargo test
|
|
cd src-tauri && cargo fmt --all && cargo clippy --all-targets -- -D warnings
|
|
cd src-tauri && cargo deny check # advisories, licences, bans, sources
|
|
```
|
|
|
|
`bun run test:all` runs the whole set.
|
|
|
|
Several of these are **ratchets** — a number that only ever moves in the
|
|
improving direction:
|
|
|
|
| Ratchet | Where | Rule |
|
|
|---|---|---|
|
|
| eslint `--max-warnings` | [.gitea/workflows/build-and-test.yml](.gitea/workflows/build-and-test.yml) | only goes down |
|
|
| Coverage thresholds | [vitest.config.ts](vitest.config.ts) | only go up |
|
|
| Traceability coverage | [.gitea/workflows/traceability-check.yml](.gitea/workflows/traceability-check.yml) | only goes up |
|
|
|
|
Never relax one to make a build pass. Fix the thing it caught.
|
|
|
|
## The two rules that surprise people
|
|
|
|
**1. Bug fixes start with a failing test.** Write a test that reproduces the bug
|
|
and *watch it fail* before you touch the fix. A test written against
|
|
already-fixed code can pass for the wrong reason and guards nothing. If the logic
|
|
is trapped in a component, extract the pure part into a plain `.ts` module and
|
|
test that — see `episodeStrip.ts` or `TrackList.logic.ts` for the pattern.
|
|
|
|
**2. Domain vocabulary lives in Rust.** The frontend is presentation-only. It
|
|
must not encode Jellyfin's *taxonomy* — for example, the set of item types that
|
|
makes up a category like "Music". Send an opaque scope across the IPC boundary
|
|
and let the backend expand it. `bun run check:boundary` is a tripwire, not a
|
|
proof: it only flags item-type array literals, so a green run does not mean you
|
|
are clear. [docs/specs/scoped-search-boundary.md](docs/specs/scoped-search-boundary.md)
|
|
describes the leak that made this a rule.
|
|
|
|
## Traceability
|
|
|
|
Code that implements a requirement carries a `TRACES:` comment naming the
|
|
requirement IDs, and a tool builds the matrix from those comments:
|
|
|
|
```rust
|
|
/// TRACES: UR-005 | DR-001
|
|
```
|
|
|
|
Every ID must exist as a row in [docs/requirements.md](docs/requirements.md) —
|
|
`bun run traces:validate` fails on a typo or a stale rename. Internal helpers and
|
|
requirement-less code stay untraced; do not sprinkle IDs to raise the number.
|
|
|
|
If you add a requirement, add its row. If you implement one, tag the code.
|
|
|
|
## Commits and pull requests
|
|
|
|
- Conventional-commit subjects: `fix(player): …`, `feat(updater): …`, `ci: …`.
|
|
- Explain **why** in the body, not what the diff already shows. The commit log
|
|
is the main record of why things are the way they are here, and it is used to
|
|
draft release notes.
|
|
- One concern per commit. A formatting sweep and a behaviour change in the same
|
|
commit is unreviewable.
|
|
- Rebase rather than merge-commit onto `master`.
|
|
|
|
## Specs
|
|
|
|
New features start from [docs/specs/SPEC-TEMPLATE.md](docs/specs/SPEC-TEMPLATE.md).
|
|
Its "Layer assignment" section is the point: each piece of logic gets placed in
|
|
Rust or the frontend *with a reason*. Review against
|
|
[docs/specs/SPEC-REVIEW-CHECKLIST.md](docs/specs/SPEC-REVIEW-CHECKLIST.md). Do
|
|
not frame a spec around "no Rust changes required" — correct placement is the
|
|
goal, not minimal backend churn.
|
|
|
|
## CI
|
|
|
|
CI is **Gitea Actions** (`.gitea/workflows/`), not GitHub.
|
|
|
|
🔴 **CI installs no system tools.** Every build, test and packaging tool must
|
|
already be in the Docker builder image. If a job needs a tool the image lacks,
|
|
add it to [Dockerfile.builder](Dockerfile.builder), rebuild and push the image,
|
|
and pin the new tag — do not `apt-get` it at job time. Details in
|
|
[docs/build/ci-operations.md](docs/build/ci-operations.md).
|
|
|
|
Fetching the project's own declared dependencies (`bun install`, cargo crates,
|
|
an advisory database) is not a toolchain install and is fine.
|
|
|
|
## Reporting bugs
|
|
|
|
Use the issue templates. For anything involving playback, include what the
|
|
platform was, whether the media was streaming or downloaded, and whether it was
|
|
transcoding — those three answers determine which of several code paths you were
|
|
actually on.
|
|
|
|
Security issues go to [SECURITY.md](SECURITY.md), not the tracker.
|