Files
Cleanuparr/CONTRIBUTING.md
T
2026-08-02 23:51:17 +03:00

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

Repository Setup

  1. Fork the repository on GitHub
  2. Clone your fork locally:
    git clone https://github.com/YOUR_USERNAME/Cleanuparr.git
    cd Cleanuparr
    
  3. 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:

  1. Go to GitHub Settings > Developer Settings > Personal Access Tokens > Tokens (classic)
  2. Click "Generate new token" → "Generate new token (classic)"
  3. Give it a descriptive name (e.g., "Cleanuparr NuGet Access")
  4. Set an expiration (recommend 90 days or longer for development)
  5. Select only the read:packages scope
  6. 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:

  1. Open the solution file: code/backend/cleanuparr.sln
  2. Set Cleanuparr.Api as the startup project
  3. Press F5 to 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

  1. Navigate to the frontend directory:

    cd code/frontend
    
  2. Install dependencies:

    npm install
    
  3. 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 via tsconfig.spec.json. Do not import them.
  • Use TestBed.createComponent and 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 mock HttpClient and do not use provideHttpClientTesting. See src/app/features/settings/seeker/seeker.component.spec.ts.
  • Keep stub observables synchronous. An rxResource backed by of(...) resolves within a single fixture.detectChanges(), while an async source would need await fixture.whenStable().
  • To flush a bare effect() outside a component, call it inside TestBed.runInInjectionContext(...) and flush with TestBed.tick(). See src/app/core/services/overlay-stack.service.spec.ts.
  • Put shared setup in a local function setup() inside the describe rather than in beforeEach, so each test reads top to bottom.
  • Components are zoneless and OnPush, so call fixture.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() and flush() from @angular/core/testing require Zone.js and cannot be used. Use vi.useFakeTimers() and vi.advanceTimersByTime() instead, and restore with vi.useRealTimers().
  • The Angular @angular/build unit-test runner sets Vitest's isolate option to false to match the old Karma behaviour. Vitest's own default is isolate: true, so its documentation will tell you the opposite. In practice module-level state, localStorage, fake timers and anything written to document.documentElement leak into other spec files. Always undo them in afterEach.

Documentation Development

Setup

  1. Navigate to the docs directory:

    cd docs
    
  2. Install dependencies:

    npm install
    
  3. 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:

  1. Navigate to the code directory:

    cd code
    
  2. Build the image:

    docker build \
      --build-arg PACKAGES_USERNAME=YOUR_GITHUB_USERNAME \
      --build-arg PACKAGES_PAT=YOUR_GITHUB_PAT \
      -t cleanuparr:local \
      -f Dockerfile .
    

    Replace YOUR_GITHUB_USERNAME and YOUR_GITHUB_PAT with your credentials.

  3. 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
    
  4. Access the application at http://localhost:11011

Building for Multiple Architectures

Use Docker Buildx for multi-platform builds:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --build-arg PACKAGES_USERNAME=YOUR_GITHUB_USERNAME \
  --build-arg PACKAGES_PAT=YOUR_GITHUB_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)

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

  1. Go to the Cleanuparr repository
  2. Click "New Pull Request"
  3. Select your fork and branch
  4. 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:

  1. Join our Discord community
  2. Let us know you're interested in testing
  3. We'll provide you with pre-release builds and testing instructions

Your feedback helps us catch issues early and deliver better releases.

Getting Help

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!