12 KiB
Contributing to Cleanuparr
Thanks for your interest in contributing to Cleanuparr! This guide will help you get started with development.
Before You Start
AI usage
In this ever-evolving field of work, AI is now the shiny new tool to help programmers work faster and there's nothing wrong with that. But it is very wrong to rely solely on AI tools to write, review and test your code.
If you do not have a background in programming and you do not intend to test your code properly, please do not submit AI-generated code. If you still want to help in other ways such as testing features, that would also help a lot!
Announce Your Intent
Before starting any work, please let us know what you want to contribute:
- For existing issues: Comment on the issue stating you'd like to work on it
- For new features/changes: Create a new issue first and mention that you want to work on it
This helps us avoid redundant work, git conflicts, and contributions that may not align with the project's direction.
Wait for approval from the maintainers before proceeding with your contribution.
Development Setup
Prerequisites
- .NET 10.0 SDK
- Node.js 26+
- Git
- (Optional) Make for database migrations
- (Optional) IDE: JetBrains Rider or Visual Studio
Repository Setup
- Fork the repository on GitHub
- Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/Cleanuparr.git cd Cleanuparr - Add the upstream repository:
git remote add upstream https://github.com/Cleanuparr/Cleanuparr.git
Backend Development
Initial Setup
1. Create a GitHub Personal Access Token (PAT)
Cleanuparr uses GitHub Packages for NuGet dependencies. You'll need a PAT with read:packages permission:
- Go to GitHub Settings > Developer Settings > Personal Access Tokens > Tokens (classic)
- Click "Generate new token" → "Generate new token (classic)"
- Give it a descriptive name (e.g., "Cleanuparr NuGet Access")
- Set an expiration (recommend 90 days or longer for development)
- Select only the
read:packagesscope - Click "Generate token" and copy it
2. Configure NuGet Source
Add the Cleanuparr NuGet repository:
dotnet nuget add source \
--username YOUR_GITHUB_USERNAME \
--password YOUR_GITHUB_PAT \
--store-password-in-clear-text \
--name Cleanuparr \
https://nuget.pkg.github.com/Cleanuparr/index.json
Replace YOUR_GITHUB_USERNAME and YOUR_GITHUB_PAT with your GitHub username and the PAT you created.
Running the Backend
Option 1: Using .NET CLI
Navigate to the backend directory:
cd code/backend
Build the application:
dotnet build Cleanuparr.Api/Cleanuparr.Api.csproj
Run the application:
dotnet run --project Cleanuparr.Api/Cleanuparr.Api.csproj
Run tests:
dotnet test
The API will be available at http://localhost:5000
Option 2: Using an IDE
For JetBrains Rider or Visual Studio:
- Open the solution file:
code/backend/cleanuparr.sln - Set
Cleanuparr.Apias the startup project - Press
F5to start the application
Database Migrations
Cleanuparr uses two separate database contexts: DataContext and EventsContext.
Prerequisites
Install Make if not already installed:
- Windows: Install via Chocolatey (
choco install make) or use WSL - macOS: Install via Homebrew (
brew install make) - Linux: Usually pre-installed, or install via package manager (
apt install make,yum install make, etc.)
Creating Migrations
From the code directory:
For data migrations (DataContext):
make migrate-data name=YourMigrationName
For events migrations (EventsContext):
make migrate-events name=YourMigrationName
Example:
make migrate-data name=AddUserPreferences
make migrate-events name=AddAuditLogEvents
Frontend Development
Setup
-
Navigate to the frontend directory:
cd code/frontend -
Install dependencies:
npm install -
Start the development server:
npm start
The UI will be available at http://localhost:4200
Testing
Unit tests run on Vitest through the Angular @angular/build:unit-test builder, in a jsdom environment. No browser download is required.
cd code/frontend
# Watch mode, re-runs on save
npm test
# Single run with a coverage report, written to coverage/ui/. This is what CI runs
npm run test:ci
# Single run without coverage
npm test -- --watch=false
# Run a single spec
npm test -- --include src/app/ui/chip-input/chip-input.component.spec.ts
# Lint, also a CI gate
npm run lint
npm test is Angular's ng test, which watches by default in an interactive terminal and runs once when stdout is not a TTY. CI therefore needs no extra flag, but do not rely on that locally: in your terminal npm test stays open until you quit it.
Writing specs
Specs live next to the code they cover, named {feature}.component.spec.ts. The Angular schematics in angular.json are configured with skipTests: true, so ng generate will not create a spec for you. Write it by hand.
House style, follow the existing specs as reference:
- Vitest globals (
describe,it,expect,vi) are enabled viatsconfig.spec.json. Do not import them. - Use
TestBed.createComponentand drive the rendered DOM. Assert on output, not on private internals. - For a component with inputs and outputs, declare a small standalone host component in the spec file and test through it. See
src/app/ui/chip-input/chip-input.component.spec.ts. - Stub API classes with a plain object of methods returning
of(...)from RxJS. Do not mockHttpClientand do not useprovideHttpClientTesting. Seesrc/app/features/settings/seeker/seeker.component.spec.ts. - Keep stub observables synchronous. An
rxResourcebacked byof(...)resolves within a singlefixture.detectChanges(), while an async source would needawait fixture.whenStable(). - To flush a bare
effect()outside a component, call it insideTestBed.runInInjectionContext(...)and flush withTestBed.tick(). Seesrc/app/core/services/overlay-stack.service.spec.ts. - Put shared setup in a local
function setup()inside thedescriberather than inbeforeEach, so each test reads top to bottom. - Components are zoneless and
OnPush, so callfixture.detectChanges()after every interaction that should update the view.
Two consequences of the zoneless, non-isolated test environment are worth knowing before you debug something strange:
fakeAsync,tick()andflush()from@angular/core/testingrequire Zone.js and cannot be used. Usevi.useFakeTimers()andvi.advanceTimersByTime()instead, and restore withvi.useRealTimers().- The Angular
@angular/buildunit-test runner sets Vitest'sisolateoption tofalseto match the old Karma behaviour. Vitest's own default isisolate: true, so its documentation will tell you the opposite. In practice module-level state,localStorage, fake timers and anything written todocument.documentElementleak into other spec files. Always undo them inafterEach.
Documentation Development
Setup
-
Navigate to the docs directory:
cd docs -
Install dependencies:
npm install -
Start the development server:
npm start
The documentation site will be available at http://localhost:3000
Building with Docker
Building a Local Docker Image
To build the Docker image locally for testing:
-
Navigate to the
codedirectory:cd code -
Build the image:
export PACKAGES_PAT=YOUR_GITHUB_PAT docker build \ --build-arg PACKAGES_USERNAME=YOUR_GITHUB_USERNAME \ --secret id=packages_pat,env=PACKAGES_PAT \ -t cleanuparr:local \ -f Dockerfile .Replace
YOUR_GITHUB_USERNAMEandYOUR_GITHUB_PATwith your credentials. -
Run the container:
docker run -d \ --name cleanuparr-dev \ -p 11011:11011 \ -v /path/to/config:/config \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Etc/UTC \ cleanuparr:local -
Access the application at http://localhost:11011
Building for Multiple Architectures
Use Docker Buildx for multi-platform builds:
export PACKAGES_PAT=YOUR_GITHUB_PAT
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg PACKAGES_USERNAME=YOUR_GITHUB_USERNAME \
--secret id=packages_pat,env=PACKAGES_PAT \
-t cleanuparr:local \
-f Dockerfile .
Code Standards
Backend (.NET/C#)
- Follow existing conventions and Microsoft C# Coding Conventions
- Use meaningful variable and method names
- Add XML documentation comments for public APIs
- Write unit tests whenever possible
Frontend (Angular/TypeScript)
- Follow existing conventions and the Angular Style Guide
- Use TypeScript strict mode
- Write unit tests whenever possible, see Frontend Development > Testing
Documentation
- Use clear, concise language
- Include code examples where appropriate
- Update relevant documentation when adding/changing features
- Check for spelling and grammar
Submitting Your Contribution
1. Create a Feature Branch
git checkout -b feature/your-feature-name
# or
git checkout -b fix/your-bug-fix-name
2. Make Your Changes
- Write clean, well-documented code
- Follow the code standards outlined above
- Test your changes thoroughly!
3. Commit Your Changes
Write clear, descriptive commit messages:
git add .
git commit -m "Add feature: brief description of your changes"
4. Keep Your Branch Updated
git fetch upstream
git rebase upstream/main
5. Push to Your Fork
git push origin feature/your-feature-name
6. Create a Pull Request
- Go to the Cleanuparr repository
- Click "New Pull Request"
- Select your fork and branch
- Fill out the PR template with:
- A descriptive title (e.g., "Add support for Prowlarr integration" or "Fix memory leak in download client polling")
- Description of changes
- Related issue number
- Testing performed
- Screenshots (if applicable)
7. Code Review Process
- Maintainers will review your PR
- Address any feedback or requested changes
- Once approved, your PR will be merged
Other Ways to Contribute
Help Test New Features
We're always looking for testers to help validate new features before they are released. If you'd like to help test upcoming changes:
- Join our Discord community
- Let us know you're interested in testing
- We'll provide you with pre-release builds and testing instructions
Your feedback helps us catch issues early and deliver better releases.
Getting Help
- Discord: Join our Discord community for real-time help
- Issues: Check existing GitHub issues or create a new one
- Documentation: Review the complete documentation
License
By contributing to Cleanuparr, you agree that your contributions will be licensed under the same license as the project.
Thanks for contributing to Cleanuparr!