|
c588255…
|
ragelink
|
1 |
# Contributing to Fossilrepo |
|
c588255…
|
ragelink
|
2 |
|
|
c588255…
|
ragelink
|
3 |
Thanks for your interest in contributing. This document covers how to get set up, our coding standards, and the PR process. |
|
c588255…
|
ragelink
|
4 |
|
|
c588255…
|
ragelink
|
5 |
## Development Setup |
|
c588255…
|
ragelink
|
6 |
|
|
c588255…
|
ragelink
|
7 |
### Prerequisites |
|
c588255…
|
ragelink
|
8 |
|
|
c588255…
|
ragelink
|
9 |
- Python 3.12+ |
|
c588255…
|
ragelink
|
10 |
- Docker and Docker Compose |
|
c588255…
|
ragelink
|
11 |
- [uv](https://docs.astral.sh/uv/) (Python package manager) |
|
c588255…
|
ragelink
|
12 |
- [Ruff](https://docs.astral.sh/ruff/) (linter/formatter) |
|
c588255…
|
ragelink
|
13 |
|
|
c588255…
|
ragelink
|
14 |
### Running Locally |
|
c588255…
|
ragelink
|
15 |
|
|
c588255…
|
ragelink
|
16 |
```bash |
|
c588255…
|
ragelink
|
17 |
git clone https://github.com/ConflictHQ/fossilrepo.git |
|
c588255…
|
ragelink
|
18 |
cd fossilrepo |
|
c588255…
|
ragelink
|
19 |
|
|
c588255…
|
ragelink
|
20 |
# Start infrastructure |
|
c588255…
|
ragelink
|
21 |
docker compose up -d postgres redis mailpit |
|
c588255…
|
ragelink
|
22 |
|
|
c588255…
|
ragelink
|
23 |
# Install dependencies |
|
c588255…
|
ragelink
|
24 |
uv sync --all-extras |
|
c588255…
|
ragelink
|
25 |
|
|
c588255…
|
ragelink
|
26 |
# Run migrations and seed data |
|
c588255…
|
ragelink
|
27 |
DJANGO_DEBUG=true uv run python manage.py migrate |
|
c588255…
|
ragelink
|
28 |
DJANGO_DEBUG=true uv run python manage.py seed |
|
c588255…
|
ragelink
|
29 |
|
|
c588255…
|
ragelink
|
30 |
# Start the dev server |
|
c588255…
|
ragelink
|
31 |
DJANGO_DEBUG=true POSTGRES_HOST=localhost uv run python manage.py runserver |
|
c588255…
|
ragelink
|
32 |
``` |
|
c588255…
|
ragelink
|
33 |
|
|
c588255…
|
ragelink
|
34 |
Or use Docker for everything: |
|
c588255…
|
ragelink
|
35 |
|
|
c588255…
|
ragelink
|
36 |
```bash |
|
c588255…
|
ragelink
|
37 |
docker compose up -d --build |
|
c588255…
|
ragelink
|
38 |
docker compose exec backend python manage.py migrate |
|
c588255…
|
ragelink
|
39 |
docker compose exec backend python manage.py seed |
|
c588255…
|
ragelink
|
40 |
``` |
|
c588255…
|
ragelink
|
41 |
|
|
c588255…
|
ragelink
|
42 |
### Default Users |
|
c588255…
|
ragelink
|
43 |
|
|
c588255…
|
ragelink
|
44 |
- `admin` / `admin` — superuser, full access |
|
c588255…
|
ragelink
|
45 |
- `viewer` / `viewer` — read-only permissions |
|
4ce269c…
|
ragelink
|
46 |
|
|
4ce269c…
|
ragelink
|
47 |
## Code Style |
|
4ce269c…
|
ragelink
|
48 |
|
|
c588255…
|
ragelink
|
49 |
We use **Ruff** for linting and formatting. No debates, no custom configs. |
|
c588255…
|
ragelink
|
50 |
|
|
c588255…
|
ragelink
|
51 |
```bash |
|
c588255…
|
ragelink
|
52 |
# Check |
|
c588255…
|
ragelink
|
53 |
ruff check . |
|
c588255…
|
ragelink
|
54 |
ruff format --check . |
|
c588255…
|
ragelink
|
55 |
|
|
c588255…
|
ragelink
|
56 |
# Fix |
|
c588255…
|
ragelink
|
57 |
ruff check --fix . |
|
c588255…
|
ragelink
|
58 |
ruff format . |
|
c588255…
|
ragelink
|
59 |
``` |
|
c588255…
|
ragelink
|
60 |
|
|
c588255…
|
ragelink
|
61 |
Key conventions: |
|
c588255…
|
ragelink
|
62 |
|
|
c588255…
|
ragelink
|
63 |
- **Max line length:** 140 characters |
|
c588255…
|
ragelink
|
64 |
- **Imports:** sorted by Ruff (isort rules) |
|
c588255…
|
ragelink
|
65 |
- **Quote style:** double quotes |
|
c588255…
|
ragelink
|
66 |
- **Target:** Python 3.12+ |
|
c588255…
|
ragelink
|
67 |
|
|
c588255…
|
ragelink
|
68 |
## Codebase Conventions |
|
c588255…
|
ragelink
|
69 |
|
|
c588255…
|
ragelink
|
70 |
Read [`bootstrap.md`](bootstrap.md) before writing code. It covers: |
|
c588255…
|
ragelink
|
71 |
|
|
c588255…
|
ragelink
|
72 |
- Model base classes (`Tracking`, `BaseCoreModel`) |
|
c588255…
|
ragelink
|
73 |
- Soft deletes (never call `.delete()`) |
|
c588255…
|
ragelink
|
74 |
- Permission system (`P` enum + project-level RBAC) |
|
c588255…
|
ragelink
|
75 |
- View patterns (HTMX partials, auth checks) |
|
c588255…
|
ragelink
|
76 |
- Template conventions (dark theme, Tailwind classes) |
|
4ce269c…
|
ragelink
|
77 |
|
|
4ce269c…
|
ragelink
|
78 |
## Testing |
|
4ce269c…
|
ragelink
|
79 |
|
|
c588255…
|
ragelink
|
80 |
Tests run against a real PostgreSQL database. No mocked databases. |
|
c588255…
|
ragelink
|
81 |
|
|
c588255…
|
ragelink
|
82 |
```bash |
|
c588255…
|
ragelink
|
83 |
# Run all tests |
|
c588255…
|
ragelink
|
84 |
DJANGO_DEBUG=true uv run pytest |
|
c588255…
|
ragelink
|
85 |
|
|
c588255…
|
ragelink
|
86 |
# Run specific test file |
|
c588255…
|
ragelink
|
87 |
DJANGO_DEBUG=true uv run pytest tests/test_releases.py |
|
c588255…
|
ragelink
|
88 |
|
|
c588255…
|
ragelink
|
89 |
# Run with coverage |
|
c588255…
|
ragelink
|
90 |
DJANGO_DEBUG=true uv run pytest --cov |
|
c588255…
|
ragelink
|
91 |
``` |
|
c588255…
|
ragelink
|
92 |
|
|
c588255…
|
ragelink
|
93 |
Every PR should: |
|
c588255…
|
ragelink
|
94 |
|
|
c588255…
|
ragelink
|
95 |
- Include tests for new features (happy path + permission denied cases) |
|
c588255…
|
ragelink
|
96 |
- Not decrease test coverage |
|
c588255…
|
ragelink
|
97 |
- Pass all existing tests |
|
c588255…
|
ragelink
|
98 |
|
|
c588255…
|
ragelink
|
99 |
## Pull Request Process |
|
c588255…
|
ragelink
|
100 |
|
|
c588255…
|
ragelink
|
101 |
1. **Fork and branch** from `main`. Branch naming: `feature/short-description` or `fix/short-description`. |
|
c588255…
|
ragelink
|
102 |
|
|
c588255…
|
ragelink
|
103 |
2. **Write code** following the conventions in `bootstrap.md`. |
|
c588255…
|
ragelink
|
104 |
|
|
c588255…
|
ragelink
|
105 |
3. **Write tests.** Both allowed and denied permission cases. Assert against database state, not just status codes. |
|
c588255…
|
ragelink
|
106 |
|
|
c588255…
|
ragelink
|
107 |
4. **Lint and test locally.** CI will catch it anyway, but save yourself a round trip. |
|
c588255…
|
ragelink
|
108 |
|
|
c588255…
|
ragelink
|
109 |
5. **Open a PR** with a clear description: |
|
c588255…
|
ragelink
|
110 |
- What changed and why |
|
c588255…
|
ragelink
|
111 |
- How to test it |
|
c588255…
|
ragelink
|
112 |
- Link to any related issues |
|
c588255…
|
ragelink
|
113 |
|
|
c588255…
|
ragelink
|
114 |
6. **Address review feedback** in new commits (don't amend/squash during review). |
|
c588255…
|
ragelink
|
115 |
|
|
c588255…
|
ragelink
|
116 |
7. **Merge** when CI is green and review is approved. |
|
c588255…
|
ragelink
|
117 |
|
|
c588255…
|
ragelink
|
118 |
## Reporting Issues |
|
c588255…
|
ragelink
|
119 |
|
|
c588255…
|
ragelink
|
120 |
Use [GitHub Issues](https://github.com/ConflictHQ/fossilrepo/issues). Include: |
|
c588255…
|
ragelink
|
121 |
|
|
c588255…
|
ragelink
|
122 |
- What you expected to happen |
|
c588255…
|
ragelink
|
123 |
- What actually happened |
|
c588255…
|
ragelink
|
124 |
- Steps to reproduce |
|
c588255…
|
ragelink
|
125 |
- Browser/OS/version if relevant |
|
c588255…
|
ragelink
|
126 |
|
|
c588255…
|
ragelink
|
127 |
## Architecture Decisions |
|
c588255…
|
ragelink
|
128 |
|
|
c588255…
|
ragelink
|
129 |
Fossilrepo has some non-obvious design choices worth understanding: |
|
c588255…
|
ragelink
|
130 |
|
|
c588255…
|
ragelink
|
131 |
- **No Fossil HTTP server.** We read `.fossil` files directly via SQLite (`FossilReader`) and use `fossil http` in CGI mode for sync. No persistent Fossil process, stateless containers. |
|
c588255…
|
ragelink
|
132 |
- **Django-backed forum posts** supplement Fossil's native forum because Fossil forum posts don't sync via clone/pull. |
|
c588255…
|
ragelink
|
133 |
- **Encrypted fields** use Fernet (AES-128-CBC + HMAC) keyed from `SECRET_KEY` for SSH keys and OAuth tokens at rest. |
|
c588255…
|
ragelink
|
134 |
- **Single org model.** Multi-org is possible but not implemented — fossilrepo targets self-hosted single-team deployments. |
|
4ce269c…
|
ragelink
|
135 |
|
|
c588255…
|
ragelink
|
136 |
## License |
|
4ce269c…
|
ragelink
|
137 |
|
|
c588255…
|
ragelink
|
138 |
By contributing, you agree that your contributions will be licensed under the MIT License. |