PR workflow
atl.chat uses a fork-and-PR model against themain branch. Every merge into main triggers semantic-release which reads commit messages to determine the next version, generate changelogs, and publish a GitHub release.
Branch naming
Use a descriptive prefix that matches the work type:| Prefix | Use case | Example |
|---|---|---|
feat/ | New feature or enhancement | feat/xmpp-muc-bridging |
fix/ | Bug fix | fix/cloak-key-fallback |
docs/ | Documentation only | docs/add-atheme-ops-guide |
chore/ | Tooling, CI, dependency updates | chore/bump-unrealircd-image |
refactor/ | Code restructuring (no behaviour change) | refactor/bridge-event-bus |
test/ | Test additions or fixes | test/irc-integration-suite |
Step-by-step
- Fork the repository on GitHub and clone your fork locally.
-
Create a branch from
main: -
Make your changes. Run tests and linting before committing:
- Commit using Conventional Commits format.
-
Push your branch and open a pull request against
main. - Address review feedback. Push additional commits — the PR will be squash-merged.
Code review expectations
- Every PR requires at least one approving review before merge.
- Reviewers check for correctness, test coverage, style compliance, and documentation updates.
- Keep PRs focused. If a change touches multiple services, consider splitting into separate PRs.
- CI must pass (lint, unit tests, type checks) before merge is allowed.
- PRs are squash-merged into
mainto keep a linear history.
Commit style
atl.chat enforces Conventional Commits via commitlint (run as a pre-commit hook on thecommit-msg stage). Semantic-release parses these messages to determine version bumps and generate release notes.
Format
Types
| Type | Version bump | Purpose |
|---|---|---|
feat | minor | New feature or user-facing enhancement |
fix | patch | Bug fix |
docs | — | Documentation only |
chore | — | Tooling, CI, dependencies |
refactor | — | Code restructuring without behaviour change |
test | — | Adding or updating tests |
ci | — | CI/CD pipeline changes |
style | — | Formatting, whitespace (no logic change) |
perf | patch | Performance improvement |
BREAKING CHANGE: in the footer (or ! after the type) triggers a major version bump.
Scopes
Use the app or area name as scope:irc, atheme, xmpp, bridge, web, lounge, webpanel, docs, infra, ops, ci.
Examples
Interactive commits
The repo includes Commitizen for guided commit message creation. Run it interactively:Pre-commit hooks
atl.chat uses pre-commit to enforce code quality on every commit. Thejust lint command runs all hooks against the entire repo.
Setup
-
Install pre-commit (included in the Python dev dependencies):
-
If your environment sets
core.hooksPath(some editors and CI systems do this), unset it first — otherwisepre-commit installwill silently fail to register hooks: -
Install the git hooks:
-
Verify hooks are active:
This is equivalent to
just lint.
Hook reference
The full hook set defined in.pre-commit-config.yaml:
| Hook | Source | What it does |
|---|---|---|
check-json | pre-commit-hooks | Validates JSON syntax |
check-toml | pre-commit-hooks | Validates TOML syntax |
end-of-file-fixer | pre-commit-hooks | Ensures files end with a newline |
trailing-whitespace | pre-commit-hooks | Removes trailing whitespace (excludes .md) |
validate-pyproject | validate-pyproject | Validates pyproject.toml schema |
yamlfix | yamlfix | Auto-formats YAML files |
yamllint | yamllint | Lints YAML files |
actionlint | actionlint | Lints GitHub Actions workflows |
markdownlint-fix | markdownlint-cli | Auto-fixes Markdown style issues |
ruff-check | ruff-pre-commit | Python linting with auto-fix (--fix) |
ruff-format | ruff-pre-commit | Python formatting |
shellcheck | shellcheck-py | Shell script static analysis |
shfmt | pre-commit-shfmt | Shell script formatting |
gitleaks | gitleaks | Scans for accidentally committed secrets |
commitlint | commitlint-pre-commit-hook | Validates commit message format (commit-msg stage) |
luacheck | local (Docker) | Lua static analysis (runs via Docker image) |
lint-web | local | Runs Biome/ultracite on apps/web/ TypeScript/JSX files |
Running hooks manually
Code style
Each language in the monorepo has its own linter and formatter. The pre-commit hooks enforce these automatically, but you can also run them standalone during development.Python — ruff
All Python code (bridge, tests, scripts) is linted and formatted by ruff. Configuration lives in the rootpyproject.toml.
| Setting | Value |
|---|---|
| Target version | Python 3.11 |
| Line length | 120 |
| Quote style | Double quotes |
| Indent style | Spaces |
| Line ending | LF |
E (pycodestyle), F (pyflakes), I (isort), N (pep8-naming), UP (pyupgrade), B (flake8-bugbear), SIM (flake8-simplify), PL (pylint), RUF (ruff-specific).
[tool.ruff.lint.per-file-ignores] in pyproject.toml).
TypeScript / JSX — Biome (ultracite)
The Next.js web app (apps/web/) uses ultracite, a wrapper around Biome, for linting and formatting. Ultracite ships a pre-configured Biome profile for Next.js projects.
Note: Theultracite checkcommand currently has a known issue where it expects a.gitignoreinapps/web/. This does not affect builds (pnpm run buildworks fine).
Lua — luacheck
Prosody configuration and any Lua scripts are checked by luacheck. The pre-commit hook runs luacheck via a Docker image, so no local Lua installation is needed. Configuration lives in.luacheckrc at the repo root:
| Setting | Value |
|---|---|
| Standard | min (minimal standard globals) |
| Max line length | 300 |
| Prosody globals | Declared in the globals table (VirtualHost, Component, modules_enabled, etc.) |
apps/prosody/config/prosody.cfg.lua may emit unused-variable warnings because Prosody config files declare variables consumed by the Prosody runtime. The .luacheckrc sets unused = false for this file.
Shell — shellcheck and shfmt
Shell scripts (scripts/, infra/) are analysed by shellcheck for correctness and formatted by shfmt for consistency.
shfmt settings (from .pre-commit-config.yaml):
| Setting | Value |
|---|---|
| Language | bash |
| Indent | 2 spaces |
| Binary ops | Newline after |
| Switch cases | Indented |
| Keep padding | Yes |
| Simplify | Yes |
infra/nginx/docker-entrypoint.sh triggers shellcheck SC2016 (unexpanded variable in single quotes) — this is intentional and can be ignored.
Related pages
- Testing — test directory layout, running tests, fixtures
- Adding a Service — checklist for adding a new service to the monorepo
- Security — secret management and Gitleaks configuration