Contributing to this project¶
First off, thanks for taking the time to contribute! โค๏ธ
1. Code of Conduct¶
This project and everyone participating in it is governed by the Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior.
2. Team members:¶
- Amine DJEGHRI
3. Best practices ๐ก¶
- Docstring your functions and classes, it is even more important as it is used to generate the documentation with Mkdocs
- If you use an IDE (like PyCharm), define src the "source" folder and test the "test" folder so your IDE can help you auto import files
-
Use the
makecommands to run your code, it is easier and faster than writing the full command (and check the Makefile for all available commands ๐)- Run Use the pre-commit hooks to ensure your code is formatted correctly and is of good quality
- UV is powerful (multi-thread, package graph solving, rust backend, etc.) use it as much as you can.
- If you have a lot of data, use Polars for faster and more efficient dataframe processing.
- If you have CPU intensive tasks, use multiprocessing with python's pool map.
-
Secret scanning (detect-secrets is configured as a pre-commit hook):
- Use
.pre-commit-config.yamlfor structural/file-level exclusions โ e.g. excluding whole files, known autogenerated patterns, or recurring false-positive lines across the repo (--exclude-files,--exclude-lines,--exclude-secretsargs). - Use
# pragma: allowlist secretas an inline comment on any line that intentionally references a secret (e.g.${{ secrets.GITHUB_TOKEN }}). This suppresses the false positive for that specific line only, without loosening repo-wide rules. - Do not add entire workflow or config files to
--exclude-filesjust to silence one line โ use the inline pragma instead, so real leaks in those files are still caught.
- Use
-
Exceptions:
- Always log the exceptions and errors (use loguru) and then raise them
- Sometimes, you don't need to raise the exception (in a loop for example) to not interrupt the execution.
- Use if else instead of catching and raising the exception when possible (log and raise also)
3.1 Security¶
This project implements a multi-layer security approach:
Local Security (Pre-commit):
- actionlint โ validates GitHub Actions workflow syntax
- zizmor โ detects CI/CD security vulnerabilities in workflows
- detect-secrets โ prevents accidental secret commits
- pip-audit โ scans dependencies for known vulnerabilities
CI Security (GitHub Actions):
- Renovate โ manages dependencies with 7-day cooldown to prevent malicious releases
4. How to contribute¶
4.1 File structure (๐ณ Tree)¶
Check the readme file.
4.2 Steps for Installation (Contributors and maintainers)¶
- The first step is to install, read and test the project as a user
- Then you can either develop in a container or develop locally
a. Local development¶
- Requires Linux, Windows or MacOS.
- Python and UV will be installed if they are not already installed.
- git clone the repository
- Install the package in editable mode with one of the following commands :
make install-devthe recommended way. it uses uv and will automatically create a .venv folder inside the project and install the dependencies- or
uv pip install -e .will install the package in your selected environment (venv, or conda or ...)
- Run the scripts locally with
./install_unix.shfor Linux and macOS or./install_windows.ps1for Windows. - run
make pre-commit installto install pre-commit hooks - To install the GitHub actions locally, run
make install-act
b. or Develop in a container¶
- You can run a docker image containing the project with
make docker-prod(ormake docker-devif you want the project to be mounted in the container). - Inside the container, run
make test-installationandmake test - A venv is created inside the container and the dependencies are installed.
- (Optional) UV will handle this automatically. However, if you face a problem with the .venv folder, and you have a
.venv folder locally and also mounted in the container, you need the local one with (
rm -rf .venv); otherwise it will create a conflict since the project is mounted in the container.
4.3. Run the test to see if everything is working¶
- Create a
.envfile (take a look at the.env.examplefile): - Test the package with :
make test-installationWill print a hello messagemake testwill run all the tests (requires .env file)
4.4. Pushing your work¶
-
Before you start working on an issue, please comment on (or create) the issue and wait for it to be assigned to you. If someone has already been assigned but didn't have the time to work on it lately, please communicate with them and ask if they're still working on it. This is to avoid multiple people working on the same issue. Once you have been assigned an issue, you can start working on it. When you are ready to submit your changes, open a pull request. For a detailed pull request tutorial, see this guide.
-
Create a branch from the
devbranch and respect the naming convention:feature/your-feature-nameorbugfix/your-bug-name. -
Before committing your code:
- Run
make testto run the tests. - Run
make pre-committo check the code style & linting. - Run
make deploy-doc-localto update the documentation locally and test the website. - Commit messages follow Conventional Commits โ see the Commit Convention section below.
- Useful references for git emojis (optional):
- Gitmoji โ emoji guide for commit messages
- GitHub Emoji Cheat Sheet โ full list of GitHub emojis
- To stay up to date with
dev, sync it into your feature branch:- If branch already pushed:
git merge devthengit pushโ safe, no force-push needed - If branch not yet pushed:
git rebase devโ cleaner history, no merge commit
- If branch already pushed:
- After syncing, run
make testandmake pre-commitagain to ensure tests still pass. - Do NOT manually update the version in
pyproject.tomlโ semantic-release handles this automatically on merge. - Run CI/CD Locally (optional): run
make install-actthenmake actfor GitHub Actions (Docker must be running). - Open a pull request targeting
dev. Make sure the PR title follows the commit convention (e.g.feat: add new plugin) โ it becomes the squash commit message used by semantic-release to determine the version bump.
- Run
4.5. Renovate: automatic dependency updates¶
- Renovate configuration can be found here
- How it works:
- Renovate runs on a schedule
- Creates PRs for dependency updates with the
renovatelabel - 7-day cooldown for all dependencies โ prevents immediate adoption of potentially malicious releases
- PyPI dependencies are not pinned โ we rely on
uv.lockfor reproducible builds. Renovate updatespyproject.tomlversion constraints, anduv.lockis regenerated to lock specific versions - Security PRs are not grouped โ vulnerability alerts bypass grouping rules and open immediately as standalone PRs to ensure fast remediation
- What to do:
- Review all Renovate PRs manually โ they require human approval before merging
- A dependency dashboard issue is created automatically to track pending updates
- Check the PR details, then merge when confident
4.6. (For repository maintainers) Merging strategies & GitHub Actions guidelines¶
- All PRs use Squash merge โ enforced at repository level (no merge commits, no rebase merge via PR).
- The PR title becomes the squash commit message โ it must follow the commit convention so semantic-release can determine the version bump.
GitHub Actions workflows¶
| Workflow | Trigger | Purpose |
|---|---|---|
๐ PR โ Quality Checks (ci.yml) |
pull_request on any branch |
Pre-commit (actionlint, ruff, detect-secrets, pip-audit) + tests on every PR |
๐ถ Release โ Dev (dev-release.yml) |
push to dev (non [skip ci]) |
RC versioning (v1.1.0-rc.X), builds & publishes prerelease to GitHub |
๐ Release โ Main (main-release.yml) |
push/PR merged to main |
Stable versioning (v1.1.0), builds, publishes to GitHub Releases + deploys docs |
๐ค Renovate (renovate.json) |
Schedule (Renovate app) | Updates GitHub Actions and Python dependencies with 7-day cooldown + selective automerge |
Release & Deployment Flow¶
| Branch | Commit Type | Release? | Version Pattern | Result |
|---|---|---|---|---|
dev |
feat:, fix:, perf: |
โ RC | v1.1.0-rc.1 โ v1.1.0-rc.2 |
Prerelease on GitHub |
dev |
feat!: / BREAKING |
โ RC | v2.0.0-rc.1 |
Major prerelease on GitHub |
dev |
docs:, chore:, ci:, style:, refactor:, build:, test: |
โ No | โ | No release |
main |
feat:, fix:, perf: |
โ Stable | v1.1.0 |
Stable release + GitHub + docs deploy |
main |
feat!: / BREAKING |
โ Stable | v2.0.0 |
Major stable release + GitHub + docs deploy |
main |
docs:, chore:, ci:, style:, refactor:, build:, test: |
โ No | โ | No release, no docs deploy |
Key points:
- RC (release candidate) versions allow testing before stable release
- Each releasable commit on dev bumps the rc counter: 1.1.0-rc.1 โ 1.1.0-rc.2, etc.
- When dev is squash-merged into main, semantic-release on main evaluates the squashed commit and releases the stable version (e.g. 1.1.0)
- Release bump commits always include [skip ci] to prevent infinite loops
- Syncing main โ dev after a release is a regular merge commit โ [skip ci] guard in dev-release.yml prevents a spurious RC
Merging Strategy¶
| Merge | Strategy | Why |
|---|---|---|
feature/* โ dev |
Squash | Clean history, one conventional commit per feature |
dev โ main |
Squash | One commit for semantic-release to evaluate on main |
main โ dev (sync) |
Merge commit (direct push, no PR) | Direct local push โ squash enforcement only applies to PRs; [skip ci] in release bump commits skips dev pipeline |
Step-by-step: shipping a feature to production¶
1. Sync local dev with remote dev (always fast-forward, no conflicts):
git checkout dev
git pull โ always do this before branching
2. Branch off dev:
git checkout -b feature/my-feature
3. Commit with conventional commits:
feat: add new plugin
fix: handle auth error
4. (Optional) Sync feature branch with dev if dev moved forward:
git merge dev โ if branch already pushed to remote (safe)
(not recommended) git rebase dev โ if branch is only local (cleaner history)
Note: either way it gets squashed on PR merge, so both are fine
5. Open PR: feature/my-feature โ dev
โ PR title must follow commit convention (e.g. "feat: add new plugin")
โ ๐ PR โ Quality Checks runs (pre-commit + tests)
โ Squash merge into dev
โ ๐ถ Release โ Dev runs โ creates v1.1.0-rc.1 on GitHub (prerelease)
6. (Repeat steps 2-5 for more features)
Each releasable squash commit โ v1.1.0-rc.2, v1.1.0-rc.3, etc.
7. When ready for production, open PR: dev โ main
โ ๐ PR โ Quality Checks runs
โ Squash merge into main
โ ๐ Release โ Main runs โ creates v1.1.0 (stable) + GitHub Release + Docs
8. Sync main back into dev:
git checkout dev && git pull
git merge main โ regular merge commit (direct push, no PR)
(The [skip ci] in the release bump commit prevents a spurious RC)
โ ๏ธ Expect conflicts on CHANGELOG.md and pyproject.toml โ this is normal.
Both were modified by semantic-release on both branches (RC on dev, stable on main).
Always keep MAIN's version (stable wins over RC):
git merge --continue # git pre-fills "Merge branch 'main' into dev" โ keep as-is
git push
After this, dev has v1.0.0 as base โ next RC will correctly be v1.1.0-rc.1
GitHub pages automatic deployment :¶
- You need to give the right permissions to GithubAction: Check that "Read and write permissions" are enabled in your project: Settings -> Actions -> General -> Workflow permissions : Read and write permissions
- You need to have the branch 'gh-pages' in your repository after following this tutorial. You can create it with
make deploy-doc-gh, it will prompt you for your creds, go to https://github.com/settings/tokens and generate a classic key (your password), then push the documentation to the gh-pages branch. - In GitHub Pages settings, select 'Deploy from a branch' and select 'gh-pages' branch.
- From the next time, GitHub Actions will run automatically and deploy it from the main branch.
Issues:
- GitHub Actions will run automatically and deploy it from the main branch. If you face an error saying that your branch
is not allowed to deploy to github-pages, check this issue"
- (Optional) If you want to manually deploy the documentation, run make deploy-doc-gh it will push the documentation to the gh-pages branch
Creating tags for releases¶
Tags are created automatically by semantic-release on merge to main or dev. Do NOT create tags manually โ it will confuse semantic-release and cause incorrect version bumps.
Commit Convention¶
Format: [emoji] <type>[(<scope>)][!]: <description>
The emoji prefix is optional โ both forms are valid:
Types & Release Impact¶
| Emoji | Type | Aliases | Release | Example |
|---|---|---|---|---|
| ๐ | fix |
bugfix, hotfix |
๐ข patch | ๐ fix(llm): handle null response from API |
| โก๏ธ | perf |
โ | ๐ข patch | โก๏ธ perf(cache): reduce install time |
| โจ | feat |
feature |
๐ก minor | โจ feat(auth): add GitHub App token rotation |
| ๐ฅ | feat! |
BREAKING CHANGE footer |
๐ด major | ๐ฅ feat!: redesign agent card schema |
| โป๏ธ | refactor |
โ | โ | โป๏ธ refactor: clean up plugin factory |
| ๐ | docs |
โ | โ | ๐ docs: update README setup section |
| ๐จ | style |
โ | โ | ๐จ style: format with ruff |
| ๐งช | test |
โ | โ | ๐งช test: add unit tests for auth module |
| ๐ง | chore |
โ | โ | ๐ง chore: bump dependencies |
| ๐ท | ci |
โ | โ | ๐ท ci: optimize uv cache strategy |
| ๐ฆ | build |
โ | โ | ๐ฆ build: update hatchling config |
Scopes (optional)¶
Use a scope to narrow the context:
Common scopes: github, llm, auth, agent, cache, api, ci, deps
Breaking Changes¶
Two ways to declare a breaking change (triggers major bump):
1. ! after the type:
2. BREAKING CHANGE: footer:
Examples¶
โจ feat(github): add support for GitHub App token rotation
๐ fix(llm): handle empty response from LLM API
โก๏ธ perf(cache): persist .venv between CI runs
๐ฅ feat!(agent): redesign A2A message schema
โป๏ธ refactor: clean up plugin factory initialization
๐ docs: add deployment guide for Kubernetes
๐งช test: add integration tests for GitHub auth flow
๐ง chore: update pre-commit hooks
๐ท ci: add pull_request trigger to pipeline
5. GitHub Actions Philosophy: Local-First Testing¶
We follow a local-first approach for testing and quality checks. GitHub Actions runs the exact same tools and commands you run locally (uv, make test, make pre-commit), not custom GitHub Actions equivalents. This philosophy ensures:
- Environment parity: Your local machine matches CI exactly โ no surprises when pushing
- Faster feedback: Run tests locally in seconds before pushing, catch issues early
- Single source of truth: One set of tools and scripts (
Makefile,pyproject.toml) used everywhere - Reproducibility: Anyone can reproduce CI behavior locally without GitHub access
Example: Instead of using GitHub Actions-specific linters, we install uv locally (same as in CI), then run make pre-commit and make test โ the exact same commands GitHub Actions runs. This means your local environment is 100% reproducible in CI, and you catch issues before pushing.
Always run make test and make pre-commit locally before opening a PR โ it's faster and guarantees your code will pass CI since you're running identical tooling.