CONTRIBUTING.md
Rama main · réplica de lectura del repositorio original.
# Contributing to Traefik Manager
Thanks for your interest in contributing. This guide covers everything you need to get started.
---
## Table of Contents
- [Reporting bugs](#reporting-bugs)
- [Suggesting features](#suggesting-features)
- [Submitting a pull request](#submitting-a-pull-request)
- [Running locally](#running-locally)
- [Tests](#tests)
- [Translations](#translations)
- [Project structure](#project-structure)
- [Code style](#code-style)
- [Branch guide](#branch-guide)
---
## Reporting bugs
Please use the [Bug Report](.github/ISSUE_TEMPLATE/bug_report.yml) issue template. Include:
- Traefik Manager version
- How you're running it (Docker, Podman, native Linux)
- Traefik version
- Steps to reproduce
- What you expected vs what happened
For **security vulnerabilities**, do not open a public issue - see [SECURITY.md](.github/SECURITY.md).
---
## Suggesting features
Start an [Idea discussion](https://github.com/chr0nzz/traefik-manager/discussions/new?category=ideas) before writing any code. This lets us discuss it first and avoids wasted effort if it doesn't fit the project's direction. Ideas become an issue once they are planned.
---
## Submitting a pull request
> [!IMPORTANT]
> **All pull requests target `dev`.** Never open a PR against `main` - it only moves when a release is cut. PRs against `main` will be asked to retarget.
1. Fork the repo and create your branch from `dev`.
2. Keep PRs focused - one fix or feature per PR.
3. For anything beyond a small bug fix, open an issue first so we can align on approach.
4. Test your changes with a real Traefik instance if possible.
5. Update the relevant docs page in `docs/` if your change affects user-facing behaviour.
6. If you add or reword text a user can see, mark it for translation and commit the regenerated catalogues in the same PR (see [Translations](#translations)).
7. Open the PR against `dev` (see [Branch guide](#branch-guide)). GitHub preselects `main` - change the base branch dropdown when creating the PR.
---
## Running locally
### Requirements
- Python 3.11+
- A running Traefik instance (or the Traefik API accessible at some URL)
- Docker (optional, for building the image)
### Setup
```bash
git clone https://github.com/chr0nzz/traefik-manager.git
cd traefik-manager
git checkout dev
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
### Configuration
Create a config directory and a minimal dynamic config file:
```bash
mkdir -p config backups
touch config/dynamic.yml
```
### Environment variables
| Variable | Description | Default |
|----------|-------------|---------|
| `TRAEFIK_API_URL` | URL of the Traefik API | `http://traefik:8080` |
| `CONFIG_PATH` | Path to your Traefik dynamic config | `config/dynamic.yml` |
| `COOKIE_SECURE` | Set `true` when running behind HTTPS | `false` |
| `RESTART_METHOD` | How TM restarts Traefik (`proxy`, `poison-pill`, `socket`) | `proxy` |
| `STATIC_CONFIG_PATH` | Path to `traefik.yml` to enable the Static Config Editor | - |
These are the ones you need to run it locally. The full list is in the [Environment Variables](https://traefik-manager.xyzlab.dev/env-vars.html) reference.
### Run
```bash
TRAEFIK_API_URL=http://your-traefik:8080 \
CONFIG_PATH=config/dynamic.yml \
python3 app.py
```
The UI is available at `http://localhost:5000`. There is no username - login is password-only. On first run a random password is generated and printed to the console with an `AUTO-GENERATED PASSWORD` banner; use that, then the setup wizard prompts you to set a permanent one. Set `ADMIN_PASSWORD` to choose it yourself instead.
### Docker build
```bash
docker build -t traefik-manager:local .
docker run -p 5000:5000 \
-e TRAEFIK_API_URL=http://your-traefik:8080 \
-v $(pwd)/config:/app/config \
traefik-manager:local
```
### Tests
```bash
pip install -r requirements-dev.txt
pytest # Python suite
make lint # ruff - undefined names are a hard failure
cd agent && go test ./... # agent suite
```
The suite runs against a temporary config directory - it never touches a real Traefik or your own config. Please run it before opening a pull request, and add a test when you change the config write path (`/save`, middleware saves, backups). Tests assert the YAML that actually lands on disk, not just the HTTP status, because that is where config-corrupting bugs show up.
---
## Translations
Any text a user can see goes through the translation helpers, including error messages returned by the API. CI fails a PR that shows unmarked English in a template, or that changes a string without updating `locale/messages.pot`.
| Where | Plain text | Text placed into HTML |
|-------|------------|-----------------------|
| Python and templates | `_()`, `ngettext()`, `pgettext()` | - |
| JavaScript | `t()`, `tn()`, `tc()` | `th()`, `thn()`, `thc()` |
Pass plain string literals with named placeholders (`%(name)s` in Python and templates, `{name}` in JavaScript) and keep HTML outside the translated text.
If your PR adds or rewords a string, regenerate the catalogues and commit the result with your change:
```bash
pip install -r requirements-dev.txt
make i18n-tools # once: installs the JS parser, needs Node
make i18n-extract # updates locale/messages.pot and every catalogue
make i18n-check # the same checks CI runs, needs gettext for msgfmt
```
Commit everything `make i18n-extract` changes under `locale/`, even though the PR then touches every language. Leave the new strings untranslated: translators fill them in on Weblate, and rewording a string sends it back to them in every language.
The [Development guide](https://traefik-manager.xyzlab.dev/development.html#translations) has the full rules for each helper.
---
## Project structure
```
app.py # Flask app, routes, CLI
core/ # Shared logic - settings, config I/O, auth, git, Traefik, agents
tests/ # pytest suite - run before opening a PR
requirements.txt # Python dependencies
Dockerfile
docker-compose.yml
agent/ # TMA - the Go agent for remote servers
templates/
index.html # Main SPA shell
sections/ # Navbar and stats bar
tabs/ # One file per tab (routes, middlewares, dashboard, etc.)
modals/ # Route, middleware, settings and other modals
static/
css/app.css # All custom styles
js/ # Application JS, one file per area
vendor/ # Third-party JS/CSS (Monaco, dagre, fonts)
docs/ # VitePress documentation site
.vitepress/
config.ts # Nav, sidebar, theme config
theme/ # Custom theme components and styles
*.md # One page per doc
.github/
workflows/
docker.yml # Builds and pushes Docker images on tag/branch push; on a tag, audits first and publishes the release last
audit.yml # pip-audit + govulncheck, daily, per push, and before every tag build
tests.yml # pytest + coverage + ruff, and the Go agent build/vet/test
pr-base-check.yml # Fails a PR opened against main instead of dev
release-binaries.yml # Builds agent binaries and publishes the GitHub release, called by docker.yml on a tag
wrangler.toml # Cloudflare Pages builds and deploys the docs site
```
> [!NOTE]
> **`app.py` still holds the route handlers.** Shared logic lives in `core/`, and the JavaScript is fully split into `static/js/`, but the Flask routes are still in `app.py`. Moving them into blueprints needs an app-factory restructure and has not been done. See the [Development guide](https://traefik-manager.xyzlab.dev/development.html) for the `core/` module graph and its conventions.
---
## Code style
- **Python:** no strict formatter enforced, but follow the existing style - 4-space indent, single quotes, type hints on new functions.
- **HTML/JS:** keep changes within the existing Tailwind + vanilla JS patterns. No new frameworks.
- **CSS:** add rules to `static/css/app.css`. No inline styles in HTML unless they're dynamic values.
- **No comments:** don't add explanatory comments to code - use clear names instead. The existing codebase follows this convention.
- **No dead code:** don't leave commented-out blocks or unused variables.
- **Commit messages:** one imperative subject line with a conventional prefix (`fix:`, `feat:`, `docs:`, `build:`, `ci:`, `chore:`, `test:`), with the issue or discussion in brackets when there is one. No body - the reasoning belongs in the release notes.
---
## Branch guide
| Branch | Purpose | Accepts PRs? |
|--------|---------|--------------|
| `dev` | Active development - all features, bug fixes, and docs changes for the next release | **Yes - every PR targets `dev`** |
| `main` | Release branch - only moves when a version is tagged | **No - never open PRs here** |
Everything lands on `dev` first - features, bug fixes, and docs alike. When `dev` is ready for release it is merged to `main` and tagged. The tag runs the dependency audit, then publishes the Docker images, then attaches the agent binaries and publishes the GitHub release; a failed audit stops all three. Prepare the release notes as a draft release for the tag rather than publishing it by hand. Nothing else touches `main`: a PR merged there would sit unreleased, drift out of sync with `dev`, and risk being clobbered by the next release merge.