# 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](CODE_OF_CONDUCT.md). 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 properdocs - 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 `make` commands 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](https://pre-commit.com/) to ensure your code is formatted correctly and is of good quality - [UV](https://docs.astral.sh/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](https://github.com/Yelp/detect-secrets) is configured as a pre-commit hook): - Use `.pre-commit-config.yaml` for **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-secrets` args). - Use `# pragma: allowlist secret` as 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-files` just to silence one line โ€” use the inline pragma instead, so real leaks in those files are still caught. - Exceptions: - Always log the exceptions and errors (use loguru) and then raise them ```py except Exception as e: logger.error(e) # Log the original error with a personalized message or with e (only the message will be logged) raise e # All the stack trace will be logged ``` - 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) ```py if not os.path.exists(file_path): logger.error(f"File not found: {file_path}. The current directory is: {os.getcwd()}") raise FileNotFoundError(f"The file {file_path} does not exist.") ``` ## 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](README.md#-steps-for-installation-users) - Then you can either [develop in a container](#b-or-develop-in-a-container) or [develop locally](#a-local-development) #### 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-dev`` the 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.sh` for Linux and macOS or `./install_windows.ps1` for Windows. - run ``make pre-commit install`` to 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`` (or ``make docker-dev`` if you want the project to be mounted in the container). - Inside the container, run ``make test-installation`` and ``make 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 ``.env`` file *(take a look at the ``.env.example`` file)*: - Test the package with : - ``make test-installation`` Will print a hello message - ``make test`` will 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. 1. Create a branch from the **`dev`** branch and respect the naming convention: `feature/your-feature-name` or `bugfix/your-bug-name`. 2. Before committing your code: - Run ``make test`` to run the tests. - Run ``make pre-commit`` to check the code style & linting. - Run ``make deploy-doc-local`` to update the documentation locally and test the website. - Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) โ€” see the [Commit Convention](#commit-convention) section below. - Useful references for git emojis (optional): - [Gitmoji](https://gitmoji.dev/) โ€” emoji guide for commit messages - [GitHub Emoji Cheat Sheet](https://github.com/ikatyang/emoji-cheat-sheet/blob/master/README.md) โ€” full list of GitHub emojis - To stay up to date with `dev`, sync it into your feature branch: - If branch **already pushed**: `git merge dev` then `git push` โ€” safe, no force-push needed - If branch **not yet pushed**: `git rebase dev` โ€” cleaner history, no merge commit - After syncing, run ``make test`` and ``make pre-commit`` again to ensure tests still pass. - **Do NOT manually update the version in `pyproject.toml`** โ€” semantic-release handles this automatically on merge. 3. Run CI/CD Locally (optional): run `make install-act` then `make act` for GitHub Actions (Docker must be running). 4. 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. ### 4.5. Renovate: automatic dependency updates - Renovate configuration can be found [here](.github/renovate.json) - **How it works**: - Renovate runs on a schedule - Creates PRs for dependency updates with the `renovate` label - **7-day cooldown for all dependencies** โ€” prevents immediate adoption of potentially malicious releases - **PyPI dependencies are not pinned** โ€” we rely on `uv.lock` for reproducible builds. Renovate updates `pyproject.toml` version constraints, and `uv.lock` is 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](#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](https://github.com/orgs/community/discussions/39054)" - (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] [()][!]: ` The emoji prefix is **optional** โ€” both forms are valid: ``` feat: add token refresh โœจ feat: add token refresh ``` --- ### 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: ``` feat(github): ... fix(llm): ... perf(cache): ... ci(runner): ... ``` 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:** ``` feat!(auth): remove legacy token endpoint ``` **2. `BREAKING CHANGE:` footer:** ``` feat(auth): redesign token flow BREAKING CHANGE: /v1/token removed, use /v2/auth instead ``` --- ### 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.