Contribution

Contributions are welcome!

The repository is hosted at gitlab.com/yaal/canaille.

Discuss

If you want to implement a feature or a bugfix, please start by discussing it with us on the bugtracker or the matrix room. If the bug is confirmed or the features fits in the roadmap, you can then open a merge request on GitLab. The test suite must pass with full coverage for your contribution to be accepted.

Development environment

You can either run the development server locally or with Docker.

After having launched the development server, you have access to several services:

The canaille server has some default users:

  • A regular user which login and password are user;

  • A moderator user which login and password are moderator;

  • An admin user which admin and password are admin;

  • A new user which login is james. This user has no password yet, and his first attempt to log-in would result in sending a password initialization email (if a smtp server is configured).

Backends

Canaille comes with several backends:

  • a lightweight test purpose memory backend

  • a sql backend, based on sqlalchemy

  • a production-ready LDAP backend

Local environment

The only tool required for local development is uv. Make sure to have uv installed on your computer to be able to hack Canaille.

Initialize your development environment with:

  • uv sync --all-groups --all-extras if you want to have everything at your fingertips. Note that it may compile some Python dependencies that would expect things to be installed on your system;

  • uv sync --extra front --extra oidc to have a minimal working development environment. This will allow you to run the tests with uv pytest --backend memory.

  • uv sync --extra front --extra oidc --extra sqlite to have a minimal working development environment with SQLite backend support. This will allow you to run the tests with uv pytest --backend sql.

  • uv sync --extra front --extra oidc --extra ldap to have a minimal working development environment with LDAP backend support. This will allow you to run the tests with uv pytest --backend ldap. Some dependencies of Canaille might need to be compiled, so you probably want to check that GCC and cargo are available on your computer.

SQL

With the SQL backend, the development server will load and save data in a local sqlite database.

Run the development server with the SQL backend
$ uv run devserver

Memory

With the memory backend, all data is lost when Canaille stops.

Run the development server with the memory backend
$ uv run devserver --backend memory

LDAP

With the LDAP backend, all data is lost when Canaille stops.

Run the development server with the LDAP backend
$ uv run devserver --backend ldap

Note

If you want to run the development server locally with the LDAP backend, you need to have OpenLDAP installed on your system. It is generally shipped under the slapd or openldap package name.

Warning

On Debian or Ubuntu systems, the OpenLDAP slapd binary usage might be restricted by apparmor, and thus makes the tests and the development server fail. This can be mitigated by removing apparmor restrictions on slapd.

$ sudo apt install --yes apparmor-utils
$ sudo aa-complain /usr/sbin/slapd

Docker environment

SQL

With the SQL backend, the development server will load and save data in a local sqlite database.

Run the development server with the SQL backend
$ cd dev
$ docker compose up

Memory

With the memory backend, all data is lost when Canaille stops.

Run the development server with the memory backend
$ cd dev
$ docker compose --file docker-compose-memory.yml up

LDAP

With the LDAP backend, all data is lost when Canaille stops.

Run the development server with the LDAP backend
$ cd dev
$ docker compose --file docker-compose-ldap.yml up

Populate the database

The development server database comes populated with some random users and groups. If you need more, you can generate users and groups with the populate command:

# If using docker:
$ docker compose exec canaille env CANAILLE__DATABASE=<backend> CANAILLE_CONFIG=conf/canaille.toml uv run canaille populate --nb 100 users  # or docker-compose

# If running in local environment
$ env CANAILLE__DATABASE=<backend> CANAILLE_CONFIG=conf/canaille.toml uv run canaille populate  --nb 100 users

Adapt to use either the ldap or the sql configuration file. Note that this will not work with the memory backend.

Unit tests

To run the tests, you just can run uv run pytest and/or uv run tox to test all the supported python environments. Everything must be green before patches get merged.

To test a specific backend you can pass --backend memory, --backend sql or --backend ldap to pytest and tox. SQL backend supports variants: --backend sql:postgresql or --backend sql:sqlite. --backend sql tests both.

The test coverage is 100%, patches won’t be accepted if not entirely covered. You can check the test coverage with uv run pytest --cov --cov-report=html or uv run tox -e coverage -- --cov-report=html. You can check the HTML coverage report in the newly created htmlcov directory.

Integration tests

Integration tests verify that Canaille works correctly when built and deployed as a wheel package, a PyInstaller binary, or a Docker image. To run all build modes:

$ uv run pytest integration/

To test a specific build mode, use --build=dev (source), --build=package (wheel), --build=pyinstaller (binary), or --build=docker (container). You can also test a pre-built artifact: --build=pyinstaller:/path/to/binary or --build=docker:image:tag.

Code style

We use ruff along with other tools to format our code. Please run uv run tox -e style on your patches before submitting them. In order to perform a style check and correction at each commit you can use our prek configuration with uv run prek install.

Coding agents

The use of coding agents in contributions is allowed as long as all the other points of this document are respected, and it does not impact the software quality. Contribution authors MUST understand and be able to explain ANY line of code that has been produced, like the code they write themselves. The use of an agent must be indicated in the PR.

Front

The interface is built upon the Fomantic UI CSS framework. The dynamical parts of the interface use htmx.

  • Using Javascript in the interface is tolerated, but the whole website MUST be accessible for browsers without Javascript support, and without any feature loss.

  • Because of Fomantic UI we have a dependency to jQuery, however new contributions should not depend on jQuery at all. See the related issue.

Documentation

The documentation is generated when the tests run:

$ tox -e doc

You can also run sphinx by hand, that should be faster since it avoids the tox environment initialization:

$ sphinx-build doc build/sphinx/html/en

The generated documentation is located at build/sphinx/html/en.

Note

The documentation generates dynamic screenshots of Canaille using sphinxcontrib-screenshot, that internally uses Playwright. Playwright needs to be initialized with the following command:

$ uv run playwright install firefox

Code translation

Translations are done with Weblate.

The following commands are there as documentation, only the message extraction is needed for contributors. All the other steps are automatically done with Weblate.

Message extraction

After you have edited translatable strings, you should extract the messages with:

$ pybabel extract --mapping-file pyproject.toml --copyright-holder="Yaal Coop" --output-file canaille/translations/messages.pot canaille

Language addition

You can add a new language manually with the following command, however this should not be needed as Weblate takes car of this:

$ pybabel init --input-file canaille/translations/messages.pot --output-dir canaille/translations --locale <LANG>

Catalog update

You can update the catalogs with the following command, however this should not be needed as Weblate automatically update language catalogs when it detects new strings or when someone translate some existing strings. Weblate pushes happen every 24h.

$ pybabel update --input-file canaille/translations/messages.pot --output-dir canaille/translations --ignore-obsolete --no-fuzzy-matching --update-header-comment

Catalog compilation

You can compile the catalogs with the following command, however this should not be needed as catalogs are automatically compiled before running the unit tests, before launching the demo and before compiling the Canaille python package:

$ pybabel compile --directory canaille/translations --statistics

Documentation translation

Translations are done with Weblate.

The following commands are there as documentation, only the message extraction and the language addition is needed for contributors.

Message extraction

After you have edited translatable strings, you should extract the messages with:

$ sphinx-build --builder gettext doc doc/locales

Language addition

You can add a new language manually with the following command, however this should not be needed as Weblate takes car of this:

$ sphinx-intl update --pot-dir doc/locales --locale-dir doc/locales -l fr

Build the documentation in another language

$ sphinx-build --builder html --define language=fr doc build/sphinx/html/fr

Build packages

The CI checks that the build processes are functional with each commit.

Python package

The Python packaging step is took care of by uv:

$ uv build

Binary file

To build a single binary of Canaille, you can use pyinstaller by installing the release dependency group:

$ uv sync --group release --all-extras --no-dev
$ uv run pyinstaller canaille.spec

Docker

$ docker build -t canaille .

Publish packages

Python package

$ uv publish --publish-url https://test.pypi.org/legacy/
$ uv publish

Docker

You need to authenticate once on DockerHub.

$ docker login --username <hub docker login>

Then you can push the images.

$ export CANAILLE_VERSION=$(uv run python -c "from importlib.metadata import version; print(version('canaille'))")
$ docker tag canaille:latest "yaalcoop/canaille:latest"
$ docker tag canaille:latest "yaalcoop/canaille:${CANAILLE_VERSION}"
$ docker push yaalcoop/canaille:latest
$ docker push yaalcoop/canaille:${CANAILLE_VERSION}

Publish a new release

The package building process is took care of by the CI when a git tag is pushed. It also publishes the different packages on the different stores.

  1. Check that dependencies are up to date with uv sync --all-extras --all-groups --upgrade and update dependencies accordingly in separated commits;

  2. Check that tests are still green for every supported python version, and that coverage is still at 100%, by running uv run tox. Push and check that the CI is green;

  3. Check that the Release notes section is correctly filled up;

  4. Increase the version number in pyproject.toml;

  5. Commit with git commit;

  6. Tag the commit with git tag --annotate XX.YY.ZZ --message "Release version XX.YY.ZZ";

  7. Push the release commit and the new tag on the repository with git push --tags.