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.
5.2 KiB
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 and docs/architecture/, 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 — rustup honours it automatically) and the Tauri Linux dependencies.
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/, 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:
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 | only goes down |
| Coverage thresholds | vitest.config.ts | only go up |
| Traceability coverage | .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
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:
/// TRACES: UR-005 | DR-001
Every ID must exist as a row in 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. 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. 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, 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.
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, not the tracker.