9.1 KiB
Contributing
Thank you for your interest in contributing to Tunarr! This guide will help you get started with development.
Prerequisites
Before you begin, ensure you have the following installed:
- Node.js 22 or later
- pnpm 9.12.3 or later (do not use npm or yarn)
- Git
- FFmpeg (for testing streaming features)
Getting Started
1. Fork and Clone
- Fork the repository on GitHub: chrisbenincasa/tunarr
- Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/tunarr.git
cd tunarr
2. Install Dependencies
pnpm i
3. Start Development Servers
pnpm turbo dev
This starts:
- Backend server on
http://localhost:8000 - Frontend dev server on
http://localhost:5173/web
Repository Structure
Tunarr is a monorepo with four main packages:
| Package | Path | Description |
|---|---|---|
@tunarr/server |
server/ |
Node.js backend (Fastify, SQLite) |
@tunarr/web |
web/ |
React frontend (Vite, Material-UI) |
@tunarr/types |
types/ |
Shared TypeScript types and Zod schemas |
@tunarr/shared |
shared/ |
Utility functions shared between packages |
Common Commands
Root-Level Commands
# Install all dependencies
pnpm i
# Start all dev servers
pnpm turbo dev
# Build all packages
pnpm turbo build
# Run all tests
pnpm turbo test
# Run the typechecker
pnpm turbo typecheck
# Format code with Prettier
pnpm fmt
# Lint changed files only
pnpm lint-changed
Server Commands
cd server
pnpm dev # Start server with hot reload
pnpm debug # Start server with debugger attached
pnpm test # Run server tests
pnpm test path/to/test.ts # Run a single test file
pnpm generate-openapi # Regenerate OpenAPI spec
pnpm kysely # Run Kysely CLI for database operations
pnpm tunarr # Run CLI commands
Web Commands
cd web
pnpm dev # Start Vite dev server
pnpm bundle # Build production bundle
pnpm generate-client # Regenerate API client from OpenAPI spec
pnpm regen-routes # Regenerate TanStack Router routes
Development Workflow
Making Changes
-
Create a new branch from
dev:git checkout dev git pull origin dev git checkout -b feature/your-feature-name -
Make your changes
-
Ensure code passes all checks:
pnpm fmt pnpm lint-changed pnpm turbo typecheck pnpm turbo test -
Commit your changes using conventional commit format
-
Push your branch and open a Pull Request against
dev
Adding API Endpoints
When adding new API endpoints, follow these steps:
-
Define your route in
server/src/api/{domain}Api.ts -
Use Fastify with the Zod type provider for type-safe requests/responses
-
Register the route in
server/src/api/index.ts -
Regenerate the OpenAPI spec:
cd server && pnpm generate-openapi -
Regenerate the web client:
cd web && pnpm generate-client
Database Changes
The codebase uses both Kysely (legacy) and Drizzle ORM:
- New code should use Drizzle ORM
- Schema definitions are in
server/src/db/schema/ - Database migrations are in
server/src/migration/ - Access the database via
DBAccess, which provides bothdb(Kysely) anddrizzle(Drizzle) instances
Working with Dependencies
When adding new services or components that need dependency injection:
- Define your service key in
server/src/types/inject.ts - Register your service in the appropriate module:
DBModule.tsfor database servicesServicesModule.tsfor business logic servicesStreamModule.tsfor streaming servicesFFmpegModule.tsfor FFmpeg-related services
- Use
@injectable()decorator and@inject(KEYS.ServiceName)for constructor injection
Code Style
General Guidelines
- TypeScript: All code must be written in TypeScript
- No
as any: Never cast types usingas any - Formatting: Prettier handles formatting (run
pnpm fmt) - Linting: ESLint 9.x with flat config
- Pre-commit hooks: Husky + lint-staged run automatically
Import Aliases
- Server: Use
@/prefix (e.g.,import { foo } from '@/services/foo') - Web: Uses configured path aliases
Pre-Commit Hooks
Tunarr uses Husky and lint-staged to automatically run checks before each commit. These hooks are installed automatically when you run pnpm i.
What Runs on Commit
When you commit, the following checks run automatically on staged files:
- Prettier - Formats code and auto-fixes formatting issues
- ESLint - Lints code and reports errors
If any check fails, the commit will be blocked. Fix the reported issues and try again.
Bypassing Hooks (Not Recommended)
In rare cases where you need to skip pre-commit hooks:
git commit --no-verify -m "your message"
!!! warning Only bypass hooks when absolutely necessary. All checks will still run in CI, so skipping them locally just delays catching issues.
Troubleshooting Hooks
If hooks aren't running after cloning:
pnpm i # Reinstall dependencies to set up hooks
If hooks are misconfigured or you need to reinstall them:
pnpm exec husky install
Commit Messages
Tunarr uses Conventional Commits for commit messages. This standardized format enables automatic changelog generation and makes the git history easier to read.
Format
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
Types
| Type | Description |
|---|---|
feat |
A new feature |
fix |
A bug fix |
docs |
Documentation changes only |
style |
Code style changes (formatting, semicolons, etc.) |
refactor |
Code changes that neither fix bugs nor add features |
perf |
Performance improvements |
test |
Adding or updating tests |
chore |
Maintenance tasks (deps, build config, etc.) |
ci |
CI/CD configuration changes |
Scope (Optional)
The scope indicates which part of the codebase is affected:
server- Backend changesweb- Frontend changestypes- Shared types packageshared- Shared utilities packagedocs- Documentationdeps- Dependency updates
Examples
# Feature
git commit -m "feat(web): add dark mode toggle to settings"
# Bug fix
git commit -m "fix(server): resolve race condition in stream cleanup"
# Documentation
git commit -m "docs: update contributing guide with commit conventions"
# Refactor with scope
git commit -m "refactor(server): migrate channel queries to Drizzle"
# Chore without scope
git commit -m "chore: update dependencies"
# Breaking change (add ! after type)
git commit -m "feat(api)!: change channel endpoint response format"
Commit Message Body
For complex changes, add a body to explain what and why:
git commit -m "fix(server): handle null media duration gracefully
Previously, media items with null duration would cause the scheduler
to crash. This change treats null duration as 0 and logs a warning.
Fixes #1234"
Testing
- Framework: Vitest
- Test files: Use
.test.tsextension - Test data: Use
@faker-js/fakerfor generating test data
# Run all tests
pnpm turbo test
# Run tests in watch mode
pnpm test:watch
# Run a single test file
cd server && pnpm test path/to/file.test.ts
Architecture Overview
Server
- Fastify for HTTP server with Zod type provider
- Inversify for dependency injection
- Drizzle ORM (preferred) and Kysely (legacy) for database access
- better-sqlite3 for SQLite database
- Meilisearch for search functionality
Key directories:
api/- Route handlers organized by domaindb/- Database layer and schemaservices/- Business logicstream/- Video streaming pipelineffmpeg/- FFmpeg wrapper and pipeline builderexternal/- API clients for Plex, Jellyfin, Emby
Web
- React 18 with TypeScript
- Vite for bundling
- Material-UI v7 for components
- TanStack Router for file-based routing
- TanStack Query for data fetching
- Zustand for state management
- React Hook Form + Zod for forms
Key directories:
routes/- File-based routingcomponents/- Reusable componentshooks/- Custom React hooksstore/- Zustand storesgenerated/- Auto-generated API client
Getting Help
- GitHub Issues: Report bugs or request features
- Discord: Join the community for discussion and support
Pull Request Guidelines
- Target the
devbranch for all PRs - Keep PRs focused - one feature or fix per PR
- Use conventional commits - follow the commit message format
- Ensure all checks pass before requesting review
- Update documentation if your change affects user-facing behavior
- Add tests for new functionality when applicable