715 lines
16 KiB
Markdown
715 lines
16 KiB
Markdown
# Contributing to YTPTube
|
|
|
|
Thank you for your interest in contributing to **YTPTube**! We welcome contributions from the community to help improve this project.
|
|
|
|
This document provides guidelines and instructions for contributing to the project.
|
|
|
|
## Table of Contents
|
|
|
|
- [Contributing to YTPTube](#contributing-to-ytptube)
|
|
- [Table of Contents](#table-of-contents)
|
|
- [Code of Conduct](#code-of-conduct)
|
|
- [Getting Started](#getting-started)
|
|
- [Prerequisites](#prerequisites)
|
|
- [Backend Requirements](#backend-requirements)
|
|
- [Frontend Requirements](#frontend-requirements)
|
|
- [Optional Tools](#optional-tools)
|
|
- [Installing Required Package Managers](#installing-required-package-managers)
|
|
- [Installing uv (Python Package Manager)](#installing-uv-python-package-manager)
|
|
- [Installing pnpm (Node.js Package Manager)](#installing-pnpm-nodejs-package-manager)
|
|
- [Cloning the Repository](#cloning-the-repository)
|
|
- [Development Setup](#development-setup)
|
|
- [Backend Setup (Python)](#backend-setup-python)
|
|
- [Frontend Setup (Node.js)](#frontend-setup-nodejs)
|
|
- [Development Workflow](#development-workflow)
|
|
- [Working on a Feature or Bug Fix](#working-on-a-feature-or-bug-fix)
|
|
- [Branching Strategy](#branching-strategy)
|
|
- [Important Rules:](#important-rules)
|
|
- [Branch Naming Conventions:](#branch-naming-conventions)
|
|
- [Testing Requirements](#testing-requirements)
|
|
- [Backend Testing (Python)](#backend-testing-python)
|
|
- [Frontend Testing (TypeScript/Vue)](#frontend-testing-typescriptvue)
|
|
- [Test Coverage Expectations](#test-coverage-expectations)
|
|
- [Code Quality and Linting](#code-quality-and-linting)
|
|
- [Backend (Python)](#backend-python)
|
|
- [Frontend (TypeScript/Vue)](#frontend-typescriptvue)
|
|
- [Code Quality Checklist](#code-quality-checklist)
|
|
- [Pre-commit Hooks](#pre-commit-hooks)
|
|
- [Setting Up Pre-commit Hooks](#setting-up-pre-commit-hooks)
|
|
- [Before Submitting](#before-submitting)
|
|
- [Pull Request Guidelines](#pull-request-guidelines)
|
|
- [Creating a Pull Request](#creating-a-pull-request)
|
|
- [PR Review Process](#pr-review-process)
|
|
- [After Your PR is Merged](#after-your-pr-is-merged)
|
|
- [Code Style](#code-style)
|
|
- [Python Code Style](#python-code-style)
|
|
- [TypeScript/Vue Code Style](#typescriptvue-code-style)
|
|
- [Getting Help](#getting-help)
|
|
- [Resources](#resources)
|
|
- [Ask Questions](#ask-questions)
|
|
- [Reporting Bugs](#reporting-bugs)
|
|
- [License](#license)
|
|
|
|
---
|
|
|
|
## Code of Conduct
|
|
|
|
Please be respectful and constructive in all interactions. We aim to maintain a welcoming and inclusive community.
|
|
|
|
---
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
Before you begin, ensure you have the following tools installed on your system:
|
|
|
|
#### Backend Requirements
|
|
|
|
- **Python 3.13+**: YTPTube requires Python 3.13 or higher
|
|
- **uv**: Modern Python package manager and virtual environment tool
|
|
|
|
#### Frontend Requirements
|
|
|
|
- **Node.js 18+**: Required for the Nuxt/Vue frontend
|
|
- **pnpm**: Fast, disk space efficient package manager
|
|
|
|
#### Optional Tools
|
|
|
|
- **Git**: Version control
|
|
- **Docker**: For containerized development (optional)
|
|
- **ffmpeg**: Required for video player functionality
|
|
|
|
### Installing Required Package Managers
|
|
|
|
#### Installing uv (Python Package Manager)
|
|
|
|
**uv** is a fast Python package installer and resolver written in Rust. It's used for managing Python dependencies and virtual environments.
|
|
|
|
**On Linux/macOS:**
|
|
|
|
```bash
|
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
```
|
|
|
|
**On Windows (PowerShell):**
|
|
|
|
```powershell
|
|
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
```
|
|
|
|
**Using pip:**
|
|
|
|
```bash
|
|
pip install uv
|
|
```
|
|
|
|
**Verify installation:**
|
|
|
|
```bash
|
|
uv --version
|
|
```
|
|
|
|
For more information, visit: https://github.com/astral-sh/uv
|
|
|
|
#### Installing pnpm (Node.js Package Manager)
|
|
|
|
**pnpm** is a fast, disk space efficient package manager for Node.js.
|
|
|
|
**Using npm:**
|
|
|
|
```bash
|
|
npm install -g pnpm
|
|
```
|
|
|
|
**Using Homebrew (macOS):**
|
|
|
|
```bash
|
|
brew install pnpm
|
|
```
|
|
|
|
**Using Scoop (Windows):**
|
|
|
|
```bash
|
|
scoop install pnpm
|
|
```
|
|
|
|
**Using standalone script:**
|
|
|
|
```bash
|
|
curl -fsSL https://get.pnpm.io/install.sh | sh -
|
|
```
|
|
|
|
**Verify installation:**
|
|
|
|
```bash
|
|
pnpm --version
|
|
```
|
|
|
|
For more information, visit: https://pnpm.io/installation
|
|
|
|
### Cloning the Repository
|
|
|
|
Clone the repository to your local machine:
|
|
|
|
```bash
|
|
git clone https://github.com/arabcoders/ytptube.git
|
|
cd ytptube
|
|
```
|
|
|
|
**Important**: Always work on the `dev` branch, not `master`/`main`:
|
|
|
|
```bash
|
|
git checkout dev
|
|
```
|
|
|
|
---
|
|
|
|
## Development Setup
|
|
|
|
### Backend Setup (Python)
|
|
|
|
1. **Navigate to the project root:**
|
|
|
|
```bash
|
|
cd ytptube
|
|
```
|
|
|
|
2. **Install Python dependencies using uv:**
|
|
|
|
```bash
|
|
uv sync
|
|
```
|
|
|
|
This will:
|
|
- Create a virtual environment (`.venv/`)
|
|
- Install all dependencies from `pyproject.toml`
|
|
- Install development dependencies
|
|
|
|
3. **Install development dependencies:**
|
|
|
|
Development dependencies are defined in `pyproject.toml` under `[dependency-groups]`:
|
|
|
|
```bash
|
|
uv sync --group dev
|
|
```
|
|
|
|
4. **Verify installation:**
|
|
|
|
```bash
|
|
uv run python --version
|
|
uv run pytest --version
|
|
```
|
|
|
|
5. **Run the backend development server:**
|
|
|
|
```bash
|
|
uv run app/main.py
|
|
```
|
|
|
|
The backend API will be available at `http://localhost:8081`
|
|
|
|
### Frontend Setup (Node.js)
|
|
|
|
1. **Navigate to the UI directory:**
|
|
|
|
```bash
|
|
cd ui
|
|
```
|
|
|
|
2. **Install Node.js dependencies using pnpm:**
|
|
|
|
```bash
|
|
pnpm install
|
|
```
|
|
|
|
> [!NOTE]
|
|
> Create a `.env` file in the `ui/` directory based on `.env.example` to configure environment variables.
|
|
> to link the frontend to the backend API.
|
|
|
|
3. **Run the frontend development server:**
|
|
|
|
```bash
|
|
pnpm dev
|
|
```
|
|
|
|
The frontend will be available at `http://localhost:8082` (or the port shown in the terminal)
|
|
|
|
4. **Verify the setup:**
|
|
|
|
```bash
|
|
pnpm run typecheck
|
|
pnpm run lint
|
|
```
|
|
|
|
---
|
|
|
|
## Development Workflow
|
|
|
|
### Working on a Feature or Bug Fix
|
|
|
|
1. **Ensure you're on the `dev` branch:**
|
|
|
|
```bash
|
|
git checkout dev
|
|
git pull origin dev
|
|
```
|
|
|
|
2. **Create a feature branch:**
|
|
|
|
```bash
|
|
git checkout -b feature/your-feature-name
|
|
# or
|
|
git checkout -b fix/bug-description
|
|
```
|
|
|
|
3. **Make your changes**
|
|
|
|
4. **Test your changes** (see [Testing Requirements](#testing-requirements))
|
|
|
|
5. **Run code quality checks** (see [Code Quality and Linting](#code-quality-and-linting))
|
|
|
|
6. **Commit your changes:**
|
|
|
|
```bash
|
|
git add .
|
|
git commit -m "feat: add new feature description"
|
|
# or
|
|
git commit -m "fix: fix bug description"
|
|
```
|
|
|
|
7. **Push to your fork:**
|
|
|
|
```bash
|
|
git push origin feature/your-feature-name
|
|
```
|
|
|
|
8. **Create a Pull Request** targeting the `dev` branch
|
|
|
|
---
|
|
|
|
## Branching Strategy
|
|
|
|
**YTPTube uses a two-branch strategy:**
|
|
|
|
- **`master`**: Production-ready code. Only maintainers merge to this branch.
|
|
- **`dev`**: Development branch. **All contributions must target this branch.**
|
|
|
|
### Important Rules:
|
|
|
|
- ✅ **DO**: Create feature branches from `dev`
|
|
- ✅ **DO**: Submit pull requests to `dev`
|
|
- ❌ **DON'T**: Submit pull requests to `master`/`main`
|
|
- ❌ **DON'T**: Commit directly to `dev` (use feature branches)
|
|
|
|
### Branch Naming Conventions:
|
|
|
|
- `feature/description` - For new features
|
|
- `fix/description` - For bug fixes
|
|
- `refactor/description` - For code refactoring
|
|
- `docs/description` - For documentation updates
|
|
- `test/description` - For test additions/improvements
|
|
|
|
---
|
|
|
|
## Testing Requirements
|
|
|
|
**All code changes must include appropriate tests.** This ensures code quality and prevents regressions.
|
|
|
|
### Backend Testing (Python)
|
|
|
|
Tests are located in `app/tests/` and use **pytest**.
|
|
|
|
**Running tests:**
|
|
|
|
```bash
|
|
# Run all tests
|
|
uv run pytest
|
|
|
|
# Run specific test file
|
|
uv run pytest app/tests/test_download.py
|
|
|
|
# Run with verbose output
|
|
uv run pytest -v
|
|
|
|
# Run with coverage
|
|
uv run pytest --cov=app --cov-report=html
|
|
```
|
|
|
|
**Creating new tests:**
|
|
|
|
1. Create a test file in `app/tests/` with the prefix `test_`
|
|
2. Use pytest conventions:
|
|
|
|
```python
|
|
import pytest
|
|
from app.library.YourModule import YourClass
|
|
|
|
class TestYourClass:
|
|
def test_feature_name(self):
|
|
"""Test description."""
|
|
# Arrange
|
|
instance = YourClass()
|
|
|
|
# Act
|
|
result = instance.method()
|
|
|
|
# Assert
|
|
assert result == expected_value
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_feature(self):
|
|
"""Test async functionality."""
|
|
result = await async_function()
|
|
assert result is not None
|
|
```
|
|
|
|
**Testing Singleton Classes:**
|
|
|
|
When testing singleton classes, always reset the singleton instance:
|
|
|
|
```python
|
|
from app.library.Presets import Presets
|
|
|
|
class TestPresets:
|
|
def setup_method(self):
|
|
"""Reset singleton before each test."""
|
|
Presets._reset_singleton()
|
|
|
|
def teardown_method(self):
|
|
"""Reset singleton after each test."""
|
|
Presets._reset_singleton()
|
|
```
|
|
|
|
### Frontend Testing (TypeScript/Vue)
|
|
|
|
Tests are located in `ui/tests/` and use **Vitest**.
|
|
|
|
**Running tests:**
|
|
|
|
```bash
|
|
cd ui
|
|
|
|
# Run all tests
|
|
pnpm test
|
|
|
|
# Run in watch mode
|
|
pnpm test:watch
|
|
|
|
# Run with coverage
|
|
pnpm run test --coverage
|
|
```
|
|
|
|
**Creating new tests:**
|
|
|
|
```typescript
|
|
import { describe, it, expect } from 'vitest'
|
|
import { mount } from '@vue/test-utils'
|
|
import YourComponent from '~/components/YourComponent.vue'
|
|
|
|
describe('YourComponent', () => {
|
|
it('renders correctly', () => {
|
|
const wrapper = mount(YourComponent)
|
|
expect(wrapper.exists()).toBe(true)
|
|
})
|
|
|
|
it('handles user interaction', async () => {
|
|
const wrapper = mount(YourComponent)
|
|
await wrapper.find('button').trigger('click')
|
|
expect(wrapper.emitted('event-name')).toBeTruthy()
|
|
})
|
|
})
|
|
```
|
|
|
|
### Test Coverage Expectations
|
|
|
|
- **New features**: Must include comprehensive tests
|
|
- **Bug fixes**: Must include a test that reproduces the bug
|
|
- **Refactoring**: Existing tests should still pass
|
|
- **Coverage goal**: Aim for >80% code coverage on new code
|
|
|
|
---
|
|
|
|
## Code Quality and Linting
|
|
|
|
### Backend (Python)
|
|
|
|
YTPTube uses **Ruff** for linting and formatting Python code.
|
|
|
|
**Running Ruff:**
|
|
|
|
```bash
|
|
# Check for linting issues
|
|
uv run ruff check app/
|
|
|
|
# Auto-fix linting issues
|
|
uv run ruff check --fix app/
|
|
|
|
# Format code
|
|
uv run ruff format app/
|
|
|
|
# Check specific file
|
|
uv run ruff check app/library/Download.py
|
|
uv run ruff check --fix app/library/Download.py
|
|
```
|
|
|
|
**Configuration:**
|
|
|
|
Ruff configuration is in `pyproject.toml` under `[tool.ruff]`.
|
|
|
|
### Frontend (TypeScript/Vue)
|
|
|
|
The frontend uses **ESLint** for linting and **TypeScript** for type checking.
|
|
|
|
**Running checks:**
|
|
|
|
```bash
|
|
cd ui
|
|
|
|
# Type checking
|
|
pnpm run typecheck
|
|
|
|
# Linting
|
|
pnpm run lint
|
|
|
|
# Auto-fix linting issues
|
|
pnpm run lint:fix
|
|
```
|
|
|
|
### Code Quality Checklist
|
|
|
|
Before committing, ensure:
|
|
|
|
- ✅ All tests pass (`uv run pytest` and `pnpm test`)
|
|
- ✅ No linting errors (`ruff check` and `pnpm run lint`)
|
|
- ✅ TypeScript compilation succeeds (`pnpm run typecheck`)
|
|
- ✅ Code is formatted properly
|
|
- ✅ No unused imports or variables
|
|
- ✅ Type hints are present (Python 3.13+)
|
|
|
|
---
|
|
|
|
## Pre-commit Hooks
|
|
|
|
YTPTube uses a custom shell script-based pre-commit hook that runs all checks in parallel for faster execution.
|
|
|
|
### Setting Up Pre-commit Hooks
|
|
|
|
1. **Create the pre-commit hook script:**
|
|
|
|
Create a file at `.git/hooks/pre-commit` with the following content:
|
|
|
|
```bash
|
|
#!/bin/sh
|
|
set -eu
|
|
|
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
cd "$REPO_ROOT" || exit 1
|
|
|
|
PIDS_FILE=$(mktemp)
|
|
trap 'rm -f "$PIDS_FILE"' EXIT
|
|
|
|
export CI=1
|
|
run_step() {
|
|
local label="$1"
|
|
shift
|
|
echo "🔹 Starting $label..." >&2
|
|
(
|
|
if ! "$@"; then
|
|
echo "❌ [$label] failed. Commit aborted." >&2
|
|
exit 1
|
|
else
|
|
echo "✅ [$label] passed." >&2
|
|
fi
|
|
) &
|
|
echo $! >>"$PIDS_FILE"
|
|
}
|
|
|
|
run_step "Ruff (Python linter)" uv run ruff check app/
|
|
run_step "PyTest" uv run pytest app/ -q
|
|
run_step "ESLint" pnpm --dir ./ui run lint
|
|
run_step "Vitest" pnpm --dir ./ui run test
|
|
|
|
fail=0
|
|
while read -r pid; do
|
|
if ! wait "$pid"; then
|
|
fail=1
|
|
fi
|
|
done <"$PIDS_FILE"
|
|
|
|
if [ "$fail" -eq 1 ]; then
|
|
echo "❌ One or more checks failed. Commit aborted." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ All checks passed. Commit allowed."
|
|
exit 0
|
|
```
|
|
|
|
2. **Make the hook executable:**
|
|
|
|
```bash
|
|
chmod +x .git/hooks/pre-commit
|
|
```
|
|
|
|
3. **Test the hook:**
|
|
|
|
```bash
|
|
# Test manually
|
|
.git/hooks/pre-commit
|
|
|
|
# Or make a test commit
|
|
git commit --allow-empty -m "test: verify pre-commit hook"
|
|
```
|
|
|
|
---
|
|
|
|
### Before Submitting
|
|
|
|
✅ **Checklist:**
|
|
|
|
- [ ] Code is on a feature branch created from `dev`
|
|
- [ ] All tests pass locally
|
|
- [ ] Code passes linting and formatting checks
|
|
- [ ] TypeScript types are properly defined (no `any`) without justification
|
|
- [ ] New features include tests
|
|
- [ ] Documentation is updated (if applicable)
|
|
- [ ] Commit messages are clear and descriptive
|
|
- [ ] Pre-commit hooks pass
|
|
|
|
---
|
|
|
|
## Pull Request Guidelines
|
|
|
|
### Creating a Pull Request
|
|
|
|
1. **Push your branch to GitHub:**
|
|
|
|
```bash
|
|
git push origin feature/your-feature-name
|
|
```
|
|
|
|
2. **Open a Pull Request on GitHub**
|
|
|
|
3. **Target the `dev` branch** (not `master`)
|
|
|
|
4. **Fill out the PR template** with:
|
|
- Description of changes
|
|
- Related issue numbers (if applicable)
|
|
- Testing performed
|
|
- Screenshots (for UI changes)
|
|
- Breaking changes (if any)
|
|
|
|
### PR Review Process
|
|
|
|
1. **Automated Checks**: CI pipeline runs tests and linting
|
|
2. **Code Review**: Maintainers review your code
|
|
3. **Feedback**: Address any requested changes
|
|
4. **Approval**: Once approved, maintainers will merge
|
|
|
|
### After Your PR is Merged
|
|
|
|
1. **Delete your feature branch** (optional but recommended)
|
|
2. **Pull the latest `dev` branch:**
|
|
|
|
```bash
|
|
git checkout dev
|
|
git pull origin dev
|
|
```
|
|
|
|
3. **Celebrate!** Thank you for contributing!
|
|
|
|
---
|
|
|
|
## Code Style
|
|
|
|
### Python Code Style
|
|
|
|
- **Line length**: 120 characters
|
|
- **Indentation**: 4 spaces
|
|
- **Quotes**: Double quotes for strings
|
|
- **Type hints**: Use Python 3.13+ type hints
|
|
- **Imports**: Absolute imports from `app.library`
|
|
- **Comparison**: Use Yoda comparisons for literals (`"value" == variable`)
|
|
|
|
**Example:**
|
|
|
|
```python
|
|
from app.library.Download import Download
|
|
from app.library.ItemDTO import ItemDTO
|
|
|
|
async def process_download(item: ItemDTO) -> bool:
|
|
"""Process a download item."""
|
|
try:
|
|
if "pending" == item.status: # Yoda comparison
|
|
result = await Download.start(item)
|
|
return result.success
|
|
except Exception as e:
|
|
LOG.error(f"Download failed: {e}")
|
|
return False
|
|
```
|
|
|
|
### TypeScript/Vue Code Style
|
|
|
|
- **Type safety**: No `any` types without justification
|
|
- **Explicit imports**: Import all Vue functions explicitly
|
|
- **Component structure**: Use `<script setup lang="ts">`
|
|
- **Props/Emits**: Always type-define
|
|
|
|
**Example:**
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import type { DownloadItem } from '~/types/download'
|
|
|
|
const props = defineProps<{
|
|
items: Array<DownloadItem>
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'itemSelected', item: DownloadItem): void
|
|
}>()
|
|
|
|
const selectedItem = ref<DownloadItem | null>(null)
|
|
</script>
|
|
```
|
|
|
|
---
|
|
|
|
## Getting Help
|
|
|
|
### Resources
|
|
|
|
- **API Docs**: See [API.md](API.md) for API reference
|
|
- **FAQ**: Check [FAQ.md](FAQ.md) for common questions
|
|
- **Issues**: Browse existing [GitHub Issues](https://github.com/arabcoders/ytptube/issues)
|
|
|
|
### Ask Questions
|
|
|
|
- **GitHub Discussions**: For general questions and discussions
|
|
- **GitHub Issues**: For bug reports and feature requests
|
|
- **Pull Requests**: For code review and implementation questions
|
|
- **Discord**: Join our community on Discord (link in README)
|
|
|
|
### Reporting Bugs
|
|
|
|
When reporting bugs, please include:
|
|
|
|
1. **Description**: Clear description of the issue
|
|
2. **Steps to Reproduce**: Detailed steps to reproduce the bug
|
|
3. **Expected Behavior**: What you expected to happen
|
|
4. **Actual Behavior**: What actually happened
|
|
5. **Environment**: OS, Python version, Node version, etc.
|
|
6. **Logs**: Relevant error messages or logs
|
|
7. **Screenshots**: If applicable
|
|
|
|
---
|
|
|
|
## License
|
|
|
|
By contributing to YTPTube, you agree that your contributions will be licensed under the [MIT License](LICENSE).
|
|
|
|
---
|
|
|
|
Thank you for contributing to YTPTube! Your efforts help make this project better for everyone.
|