2025-12-30 10:57:44 +01:00
# Contributing to Bambuddy
Thank you for your interest in contributing to Bambuddy! This document provides guidelines and instructions for contributing.
## Table of Contents
- [Code of Conduct ](#code-of-conduct )
2026-03-15 16:02:43 +01:00
- [Before You Start ](#before-you-start )
2026-04-10 15:20:02 +02:00
- [Documentation Requirements ](#documentation-requirements )
2025-12-30 10:57:44 +01:00
- [Getting Started ](#getting-started )
- [Development Setup ](#development-setup )
- [Making Changes ](#making-changes )
- [Code Style ](#code-style )
2026-02-08 09:01:55 +01:00
- [Internationalization (i18n) ](#internationalization-i18n )
- [Authentication & Permissions ](#authentication--permissions )
2025-12-30 10:57:44 +01:00
- [Testing ](#testing )
2026-02-06 14:55:46 +01:00
- [CI Pipeline ](#ci-pipeline )
2025-12-30 10:57:44 +01:00
- [Submitting Changes ](#submitting-changes )
- [Reporting Bugs ](#reporting-bugs )
- [Requesting Features ](#requesting-features )
## Code of Conduct
Please read and follow our [Code of Conduct ](CODE_OF_CONDUCT.md ) to keep our community welcoming and respectful.
2026-03-15 16:02:43 +01:00
## Before You Start
**Every contribution starts with an issue.** Before writing any code or opening a pull request:
1. **Open a new issue** or **comment on an existing one** describing what you'd like to work on
2. **Wait for agreement** — discuss the approach with a maintainer so we're aligned on scope and direction
3. **Get assigned** — once we agree, a maintainer will assign the issue to you
4. **Then start coding** — only open a PR for an issue that is assigned to you
**No assigned issue = no PR.** Pull requests without a corresponding assigned issue will be closed.
This keeps everyone on the same page, avoids wasted effort on changes that may not fit the project's direction, and prevents multiple contributors from working on the same thing.
2026-04-10 15:20:02 +02:00
## Documentation Requirements
Features and user-visible behavior changes **must** include matching documentation updates in the docs repos:
- **[bambuddy-wiki ](https://github.com/maziggy/bambuddy-wiki )** — end-user guide (installation, configuration, feature walkthroughs, reference)
- **[bambuddy-website ](https://github.com/maziggy/bambuddy-website )** — marketing site (updated only when the change affects public claims or feature lists)
### When docs updates are required
| Change | Needs wiki? | Needs website? |
|---|---|---|
| New feature | ✅ | Maybe (if in the feature list) |
| New config key / setting | ✅ | ❌ |
| New port, URL, API endpoint | ✅ | ❌ |
| Installation or upgrade steps change | ✅ | ✅ |
| UI change that affects screenshots | ✅ | ❌ |
| Bug fix with no observable behavior change | ❌ | ❌ |
| Internal refactor | ❌ | ❌ |
| Test-only change | ❌ | ❌ |
### Workflow
1. Open your code PR here in `bambuddy`
2. Open companion PR(s) in `bambuddy-wiki` and/or `bambuddy-website`
3. **Link the companion PR(s) in the code PR description** (the PR template has a dedicated section)
2026-04-24 09:47:45 +02:00
4. Merge the PRs together — usually code first, then docs, unless the docs reference new things that don't exist yet
2026-04-10 15:20:02 +02:00
If your change truly doesn't need docs (internal refactor, silent bug fix), say so in the PR description and give a one-line reason.
2026-04-10 16:32:33 +02:00
### Previews before you merge
2026-04-24 09:47:45 +02:00
Clone the docs repo and run it locally to see your changes rendered with the real theme before opening the PR:
2026-04-10 16:32:33 +02:00
2026-04-24 09:47:45 +02:00
- **Wiki** (`bambuddy-wiki` ) — `pip install -r requirements.txt && mkdocs serve` — live-reload on `http://localhost:8000`
- **Website** (`bambuddy-website` ) — static HTML/CSS, open the changed file directly or serve with `python -m http.server`
2026-04-10 16:32:33 +02:00
2026-04-24 09:47:45 +02:00
Review like you would the production site. Catch broken links, layout regressions, typos, missing images. If it looks right, open the PR.
2026-04-10 16:32:33 +02:00
2026-04-10 15:20:02 +02:00
### Editing docs without a local clone
Both docs repos can be edited directly in the browser, no `git clone` required:
- **GitHub web editor** — click the pencil icon on any file in the repo
- **github.dev** — press `.` (period) on any repo page to open VS Code in your browser, with multi-file editing and syntax highlighting
2025-12-30 10:57:44 +01:00
## Getting Started
1. **Fork the repository** on GitHub
2. **Clone your fork** locally:
```bash
git clone https://github.com/YOUR_USERNAME/bambuddy.git
cd bambuddy
```
3. **Add the upstream remote** :
```bash
git remote add upstream https://github.com/maziggy/bambuddy.git
```
## Development Setup
### Prerequisites
2026-02-06 14:55:46 +01:00
- Python 3.11+
- Node.js 20+
- npm
2025-12-30 10:57:44 +01:00
### Backend Setup
```bash
# Create virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
2026-02-06 14:55:46 +01:00
pip install -r requirements-dev.txt # Dev/test dependencies (pytest, ruff, bandit, etc.)
2025-12-30 10:57:44 +01:00
# Install pre-commit hooks
pip install pre-commit
pre-commit install
fix(vp): stop uvloop from silently truncating VP FTP uploads (#1896)
Native (non-Docker) installs launched uvicorn without --loop asyncio, so
uvicorn[standard] auto-selected uvloop. uvloop's SSL layer drops
already-received but still-buffered data when the client closes the data
connection without a TLS close_notify while the reader is flow-control
paused on slow storage. cmd_STOR writes each chunk to disk inside the read
loop, so a slow consumer falls behind, the tail is lost, read() returns a
clean EOF, and the loop exits with no exception -- the server acked 226 for
a file it truncated itself, then archived, queued, and forwarded the corrupt
3MF to the real printer.
Fix in two independent layers:
1. Remove the trigger: add --loop asyncio to every native launch path,
matching the Dockerfile -- deploy/bambuddy.service, install/install.sh
(systemd + launchd), spoolbuddy/install/install.sh, the Windows NSSM
service, README, CONTRIBUTING dev command.
2. Defense in depth (loop-independent): cmd_STOR now validates that a
received .3mf opens as a ZIP (reads the central directory, no
decompression) before replying 226. A truncated/corrupt file is dropped
and answered with 426, and on_file_received never runs -- so a broken
upload surfaces as an immediate slicer-side send error instead of being
archived and pushed to the printer. Scoped to .3mf; other filetypes pass
through unchanged.
2026-07-05 10:32:13 +02:00
# Run backend (--loop asyncio matches production; avoids a uvloop TLS bug
# that can truncate Virtual Printer FTP uploads on slow storage — see #1896)
DEBUG=true uvicorn backend.app.main:app --reload --host 0.0.0.0 --port 8000 --loop asyncio
2025-12-30 10:57:44 +01:00
```
### Frontend Setup
```bash
cd frontend
# Install dependencies
npm install
# Run development server
npm run dev
```
The frontend will be available at `http://localhost:5173` and will proxy API requests to the backend.
### Running with Docker
```bash
2026-02-06 14:55:46 +01:00
# Run the full application
2025-12-30 10:57:44 +01:00
docker compose up -d --build
2026-02-06 14:55:46 +01:00
# Run tests in Docker (mirrors CI)
docker compose -f docker-compose.test.yml run --rm backend-test
docker compose -f docker-compose.test.yml run --rm frontend-test
2025-12-30 10:57:44 +01:00
```
## Making Changes
2026-03-18 07:32:31 +01:00
1. **Create a branch** from `dev` for your changes:
2025-12-30 10:57:44 +01:00
```bash
2026-03-18 07:32:31 +01:00
git checkout dev
git pull upstream dev
2025-12-30 10:57:44 +01:00
git checkout -b feature/your-feature-name
# or
git checkout -b fix/your-bug-fix
```
2. **Make your changes** following our code style guidelines
3. **Test your changes** thoroughly
4. **Commit your changes** with clear, descriptive messages:
```bash
git commit -m "Add feature: description of what you added"
```
### Branch Naming
- `feature/` - New features
- `fix/` - Bug fixes
- `docs/` - Documentation changes
- `refactor/` - Code refactoring
- `test/` - Test additions or fixes
## Code Style
### Backend (Python)
2026-02-06 14:55:46 +01:00
We use [Ruff ](https://github.com/astral-sh/ruff ) for linting and formatting. Configuration is in `pyproject.toml` .
2025-12-30 10:57:44 +01:00
```bash
# Check linting
2026-02-06 14:55:46 +01:00
ruff check backend/
2025-12-30 10:57:44 +01:00
# Auto-fix issues
2026-02-06 14:55:46 +01:00
ruff check --fix backend/
2025-12-30 10:57:44 +01:00
# Format code
2026-02-06 14:55:46 +01:00
ruff format backend/
# Check formatting without changes
ruff format --check backend/
2025-12-30 10:57:44 +01:00
```
### Frontend (TypeScript/React)
2026-02-06 14:55:46 +01:00
We use ESLint for linting and TypeScript for type checking:
2025-12-30 10:57:44 +01:00
```bash
cd frontend
# Lint
npm run lint
# Type check
2026-02-06 14:55:46 +01:00
npx tsc --noEmit
2025-12-30 10:57:44 +01:00
```
### Pre-commit Hooks
2026-02-06 14:55:46 +01:00
Pre-commit hooks run automatically on `git commit` and include Ruff linting/formatting, trailing whitespace fixes, YAML/JSON validation, and import shadowing checks. To run manually:
2025-12-30 10:57:44 +01:00
```bash
pre-commit run --all-files
```
2026-02-08 09:01:55 +01:00
## Internationalization (i18n)
The frontend uses [react-i18next ](https://react.i18next.com/ ) for all user-facing text. **Never hardcode user-visible strings** — always use translation keys.
### Locale Files
2026-08-01 08:28:06 +02:00
Translations live in `frontend/src/i18n/locales/` . `en.ts` is the reference locale; every other `*.ts` file in that directory is checked against it. The parity check discovers the directory at runtime, so a new locale is picked up automatically — this file never needs updating when one is added.
To see the current set of locales and check your work:
```bash
cd frontend
npm run check:i18n
```
2026-02-08 09:01:55 +01:00
### Adding New Strings
2026-08-01 08:28:06 +02:00
1. Add the key to the appropriate section in **every** locale file
2026-02-08 09:01:55 +01:00
2. Use the `useTranslation` hook in your component:
```tsx
import { useTranslation } from 'react-i18next';
function MyComponent() {
const { t } = useTranslation();
return < span > {t('section.myNewKey')}< / span > ;
}
```
3. Keys are organized by feature (e.g., `spoolman.` , `nav.` , `common.` )
### Important Notes
2026-08-01 08:28:06 +02:00
- Every locale file must use the **same key structure** — same nesting, same key paths
- Always add keys to **every** locale to maintain parity, with real translations rather than English placeholders — the check flags leaves that are identical to `en`
- Run `npm run test:run` before pushing — it chains the parity check, which CI runs too. Plain `npm test` is vitest in watch mode and skips it
2026-02-08 09:01:55 +01:00
- If you find structural inconsistencies between locales, fix them — different key paths cause silent fallback to English
## Authentication & Permissions
Bambuddy has an optional authentication system. When auth is enabled, API endpoints are protected by granular permissions.
### How It Works
Authentication is **opt-in** — when disabled, all endpoints are open. The system uses `RequirePermissionIfAuthEnabled` which:
- Checks if auth is enabled in settings
- If disabled: allows the request through (no-op)
- If enabled: validates JWT token/API key and checks the user has the required permission
### Adding Auth to New Endpoints
Use the `RequirePermissionIfAuthEnabled` dependency in your route:
```python
from backend.app.core.auth import RequirePermissionIfAuthEnabled
from backend.app.core.permissions import Permission
@router .get("/my-resource")
async def get_my_resource(
_: User | None = RequirePermissionIfAuthEnabled(Permission.RESOURCE_READ),
):
...
```
### Permission Convention
Permissions follow the `resource:action` pattern (e.g., `filaments:read` , `printers:control` ). Standard actions:
| Action | Usage |
|--------|-------|
| `read` | View/list resources |
| `create` | Create new resources |
| `update` | Modify existing resources |
| `delete` | Remove resources |
2026-06-26 11:31:48 +01:00
Some resources have additional actions. Examples: `printers:control` for live printer controls
such as stop/pause/resume, `printers:files` for printer storage access, `queue:create` for
creating queue items that may dispatch immediately when scheduled ASAP, `library:upload` for
File Manager uploads/imports, and `archives:reprint_own` / `archives:reprint_all` for archive
reprint eligibility. Archive reprint still needs `queue:create` before it can enqueue a job.
2026-02-08 09:01:55 +01:00
### Adding New Permissions
1. Add the permission to the `Permission` enum in `backend/app/core/permissions.py`
2. Add it to the appropriate category in `PERMISSION_CATEGORIES`
3. Add it to the relevant default groups (`Administrators` gets all, `Operators` and `Viewers` as appropriate)
4. Use it in your route with `RequirePermissionIfAuthEnabled`
### Default Groups
| Group | Access Level |
|-------|-------------|
| **Administrators** | All permissions |
| **Operators** | Full control of printers, own items in archives/queue, read-only settings |
| **Viewers** | Read-only access to all resources |
2025-12-30 10:57:44 +01:00
## Testing
2026-02-06 14:55:46 +01:00
The easiest way to run tests is with the provided scripts in the project root:
2025-12-30 10:57:44 +01:00
```bash
2026-02-06 14:55:46 +01:00
./test_frontend.sh # TypeScript check + ESLint + Vitest
./test_backend.sh # Ruff lint/format + pytest (parallel)
./test_docker.sh # Full Docker build, unit tests, and integration tests
./test_all.sh # All of the above (frontend → backend → docker)
./test_security.sh # Security scans (bandit, pip-audit, npm-audit)
```
`test_docker.sh` supports flags like `--backend-only` , `--skip-integration` , `--fresh` — run with `--help` for details.
`test_security.sh` runs fast scans by default. Use `--full` for the complete suite (CodeQL, Trivy, etc.) or specify individual scans like `./test_security.sh bandit codeql` .
### Running Tests Individually
2025-12-30 10:57:44 +01:00
2026-02-06 14:55:46 +01:00
**Backend** — tests are in `backend/tests/` with `unit/` and `integration/` subdirectories:
2025-12-30 10:57:44 +01:00
2026-02-06 14:55:46 +01:00
```bash
pytest backend/tests/ -v # All tests
pytest backend/tests/unit/ # Unit tests only
pytest backend/tests/ --cov=backend # With coverage
2025-12-30 10:57:44 +01:00
```
2026-02-06 14:55:46 +01:00
**Frontend** — tests use [Vitest ](https://vitest.dev/ ) and are in `frontend/src/__tests__/` :
2025-12-30 10:57:44 +01:00
```bash
cd frontend
2026-02-06 14:55:46 +01:00
npm run test:run # Single run
npm test # Watch mode
npm run test:coverage # With coverage
```
2025-12-30 10:57:44 +01:00
2026-02-06 14:55:46 +01:00
## CI Pipeline
2025-12-30 10:57:44 +01:00
2026-02-06 14:55:46 +01:00
Pull requests trigger automated CI checks via GitHub Actions (`.github/workflows/ci.yml` ):
- **Backend**: Ruff lint + format check, unit/integration tests, pip-audit
- **Frontend**: ESLint, TypeScript type check, Vitest tests, production build
- **Docker**: Full image build, backend/frontend tests in Docker, integration health checks
- **Security**: CodeQL analysis, dependency audits
All checks must pass before merging. Run `./test_all.sh` locally before pushing to catch issues early.
2025-12-30 10:57:44 +01:00
## Submitting Changes
1. **Push your branch** to your fork:
```bash
git push origin feature/your-feature-name
```
2. **Create a Pull Request** on GitHub:
2026-03-18 07:32:31 +01:00
- **Always target the `dev` branch** as the base branch (not `main` )
2025-12-30 10:57:44 +01:00
- Use a clear, descriptive title
- Fill out the PR template completely
- Link any related issues
2026-04-05 12:15:12 +02:00
- Include before/after screenshots for any visual changes
2025-12-30 10:57:44 +01:00
3. **Wait for review** - maintainers will review your PR and may request changes
### PR Guidelines
- Keep PRs focused and reasonably sized
- One feature or fix per PR
- Update documentation if needed
- Add tests for new functionality
- Ensure all tests pass
- Follow the existing code style
2026-04-05 12:15:12 +02:00
- **Visual changes require screenshots** — if your PR changes any frontend UI, include before/after screenshots showing the old and new appearance
2025-12-30 10:57:44 +01:00
## Reporting Bugs
Use the [Bug Report template ](https://github.com/maziggy/bambuddy/issues/new?template=bug_report.yml ) and include:
- Clear description of the bug
- Steps to reproduce
- Expected vs actual behavior
- Your environment (OS, Python version, browser)
- Printer model and firmware version
- Relevant logs
## Requesting Features
Use the [Feature Request template ](https://github.com/maziggy/bambuddy/issues/new?template=feature_request.yml ) and include:
- Clear description of the feature
- Use case / problem it solves
- Proposed solution
- Alternatives considered
## Questions?
- Check the [Documentation ](http://wiki.bambuddy.cool )
- Open a [Discussion ](https://github.com/maziggy/bambuddy/discussions )
- Review existing [Issues ](https://github.com/maziggy/bambuddy/issues )
---
Thank you for contributing to Bambuddy!