Contributing
This page is for contributors working on Genja itself. If you only need to add Genja to an application, start with Installation and Quickstart.
Development Setup
Genja is a Rust workspace with Python bindings for the core runtime. A full development environment needs:
- Rust and Cargo
- Python 3.10 or newer
- PDM for the Python binding development environment
- maturin for building the Python extension module
- Zensical for building the documentation site
Tool Versions
The workspace pins Rust and Cargo to 1.88.0 in rust-toolchain.toml. Use that
toolchain locally so compiler diagnostics, formatting, and trybuild snapshots
match CI.
Individual crates do not currently declare a formal rust-version MSRV in
their package metadata.
Python binding development requires Python 3.10 or newer, as declared by the
genja-py package.
Clone the repository and check the Rust workspace first:
cargo check --workspace
For Python binding work, install the Python development dependencies from the binding package:
cd genja-core-python
pdm install
VS Code And Rust Analyzer
Optional Feature Analysis
Rust Analyzer normally analyses the default Cargo features. Code behind a
feature gate, such as genja-cli's tui module, can be treated as inactive
unless that feature is enabled for the editor. The CLI's commands,
discovery, and output modules are not feature-gated within genja-cli,
so they do not need the same configuration.
For TUI work, add this to your private VS Code workspace settings, preserving any other features you already enable:
{
"rust-analyzer.cargo.features": ["genja-cli/tui"]
}
Alternatively, use "rust-analyzer.cargo.features": "all" to analyse all
optional features. This can increase analysis time and compile additional
dependencies. These editor settings do not change the crate's default features
or enable features for separate Cargo commands you run in a terminal.
Reload the Rust Analyzer workspace after changing the configuration.
Inline Type Hints
Inlay type hints display inferred types alongside variables in the editor; they are not part of the source code. If hover and autocomplete work but inline hints are missing, enable inlay hints in VS Code's Settings UI and check this Rust Analyzer setting:
{
"rust-analyzer.inlayHints.typeHints.enable": true
}
Showing inline hints is an editor preference, separate from enabling feature analysis. Enabling hints alone does not activate optional Cargo modules. Explicitly typed declarations may not need an inferred type hint; hover can still show their type information. See the Rust Analyzer configuration reference for available hint settings.
Personal Editor Settings
Keep personal settings in VS Code's user settings or an untracked workspace
configuration. The examples above are optional contributor setup; they do not
require adding .vscode/settings.json to the repository. Agree on shared
editor settings with maintainers before committing them.
Rust Checks
Run the Rust workspace tests before sending changes that affect Rust behavior.
Exclude genja-core-python from the workspace test run; its PyO3 tests depend
on the Python environment and should be run with the PDM commands below.
cargo test --workspace --exclude genja-core-python --all-features
--all-features includes optional feature-gated tests, such as discovery through
genja::cli. Without it, Cargo enables only default features; selecting the
genja-cli workspace member does not enable the genja-cli feature on genja.
Also check that genja builds without optional features, as CI does:
cargo check -p genja --no-default-features
For focused changes, run the package that owns the behavior:
cargo test -p genja-core
cargo test -p genja
cargo test -p genja --features genja-cli
cargo test -p genja-cli
cargo test -p genja-core-derive
cargo test -p genja-plugin-manager
Use Cargo formatting before submitting Rust changes:
cargo fmt --all -- --check
This CI command is read-only. It fails when formatting changes are needed; fix those locally with:
cargo fmt --all
Run Clippy with warnings treated as errors:
cargo clippy --workspace --all-targets --all-features -- -D warnings
CI does not auto-fix formatting or Clippy warnings. If you use
cargo clippy --fix, review the generated changes and commit the ones you want
to keep.
If a change touches shared runtime behavior, task execution, plugin loading, or public APIs, prefer running both the focused package tests and the Rust workspace test command above.
Trybuild UI Fixtures
The derive macro compile-fail tests under genja-core/tests/ui_genja_task/
use trybuild .stderr snapshot files.
These snapshots are sensitive to Rust compiler diagnostic formatting. In
practice, drift is usually caused by rustc version changes rather than by the
feature change itself.
When a trybuild test fails only because the expected and actual diagnostics have drifted, refresh the fixtures with:
TRYBUILD=overwrite cargo test -p genja-core --test derive_compile
Do not hand-edit .stderr fixtures unless you are making a small targeted
correction. Prefer regenerating them from compiler output.
Refresh trybuild snapshots only when:
- macro diagnostics intentionally changed
- the Rust toolchain changed and the emitted diagnostics legitimately drifted
When adding new compile-fail fixtures, minimize unrelated warnings in the test case where possible so snapshot files are less brittle across toolchain updates.
Python Checks
The Python bindings live in genja-core-python. Run commands from that
directory.
Install the Python development dependencies before running checks:
pdm install -d
Build the extension into the active Python environment and run the Python tests:
pdm run test
Run the Rust tests that exercise the PyO3 crate:
pdm run test-rust
Use pdm run test-rust instead of invoking cargo test -p genja-core-python
directly. The wrapper runs Cargo through the project Python environment, sets
the correct PYO3_PYTHON interpreter, and ensures the PyO3-backed tests can
access the installed Python packages.
Avoid launching multiple genja-core-python Rust-backed test commands
concurrently from separate processes. Each run is already single-threaded
internally, and concurrent invocations may hang or fail to return output
reliably in this workspace.
Run type checks for the Python examples and tests:
pdm run typecheck
Check Python API stub and PyO3 documentation coverage:
pdm run check-stubs
Run this check when changing Rust/PyO3-exposed Python APIs, .pyi stubs,
Python API docstrings, or top-level Python re-exports. It verifies that the
top-level python/genja/*.pyi stubs are listed for docstring checks, public
stub APIs have docstrings, duplicated top-level stubs stay aligned with
genja.pyi, and documented PyO3 bindings do not regress.
When adding a top-level python/genja/*.pyi stub, document its public API and
add it to STUBS_REQUIRING_DOCSTRINGS in
genja-core-python/scripts/check_python_api_docs.py during the same change. When
adding Rust/PyO3 doc comments for another binding source file, extend the same
script so CI prevents future regressions for that file too.
For Rust/PyO3 classes re-exported from genja/__init__.py, keep the class shape
aligned between genja.pyi and __init__.pyi. If another duplicated top-level
class is brought under the documentation standard, add its name to
DUPLICATED_TOP_LEVEL_CLASSES in check_python_api_docs.py. The check compares
public methods and properties so the top-level import surface does not drift from
the extension-module stub.
Run Python lint and formatting checks:
pdm run lint
When debugging PyO3 test failures, the project also provides:
pdm run test-rust-debug
Changelog And Compatibility
This repository uses a single top-level CHANGELOG.md.
This repository follows Keep a Changelog style for CHANGELOG.md.
- Add user-visible changes under
Unreleased. - Use release-note headings such as:
AddedChangedFixedRemoved- Mark breaking entries inline with
**Breaking:**and include migration guidance in the same bullet. - Update the changelog during the branch, not only at merge time.
- For linked work items, use
Refs: #<issue>when an issue exists.
Documentation
Documentation pages live in docs/ and the navigation is configured in
zensical.toml.
After changing documentation, build the site:
zensical build
To preview the site locally:
zensical serve
When adding a new guide, update both the navigation in zensical.toml and the
link list in Home when the page should be discoverable from the
front page.
Examples
Rust examples live under genja/examples. Run them with Cargo:
cargo run -p genja --example basic_runtime
cargo run -p genja --example filter_hosts
cargo run -p genja --example run_task
cargo run -p genja --example run_task_tree
cargo run -p genja --example async_inventory_plugin
Python examples live under genja/examples/python. Build the local Python
extension before running them:
cd genja-core-python
maturin develop
cd ..
python genja/examples/python/basic_runtime.py
If you add or change an example, update Examples so users know what the example demonstrates.
Plugin Changes
Plugin manager changes often need dynamic loading coverage. The workspace
includes test plugin crates under genja-plugin-manager/tests/.
For plugin loading, registration, ABI, or manager behavior, run:
cargo test -p genja-plugin-manager
For runtime behavior that crosses from plugins into task execution, also run the
relevant genja-core or genja tests.
Contribution Flow
Keep changes focused. A good contribution usually includes:
- The implementation change
- Tests for changed behavior
- Documentation updates for user-facing behavior
- Example updates when the change affects common workflows
Before opening a pull request, run the smallest focused checks that prove the change and the broader checks for any shared runtime or public API changes.
GitHub Actions runs CI for pull requests and for pushes to main, develop,
feature/**, and issue-style *-genja-* branches. CI runs formatting checks,
Clippy with all features and -D warnings, Rust tests with all features excluding
genja-core-python, a separate genja build without optional features, and the
Python binding lint, typecheck, Python test, and PyO3 Rust test commands. A
separate cross-platform compatibility workflow runs on pull requests into
main and checks Linux, macOS, and Windows across all supported Python versions.
Release publishing is separate from CI. Rust crates are published only from
rs-vX.Y.Z tags on commits reachable from main, and the release workflow
publishes the publishable Rust crates in dependency order after validating crate
versions and internal dependency metadata. Genja currently uses a unified Rust
release train: when preparing rs-vX.Y.Z, bump genja, genja-core,
genja-core-derive, genja-plugin-manager, and genja-cli to X.Y.Z, even if
one crate only changed because the release train moved. Keep internal path dependency
version requirements aligned to the dependency crate's released version.
The publish order is genja-core-derive, genja-core, genja-plugin-manager,
genja-cli, then genja. This includes the optional CLI dependency: genja-cli
must be available on crates.io before publishing genja.
Validate release metadata locally with python scripts/validate-release.py.
During release preparation, pass --tag rs-vX.Y.Z to check the intended version.
Package verification against crates.io requires internal dependency versions to
be published first; the release workflow publishes and waits for each dependency
before packaging the next crate.
Python package releases use matching py-vX.Y.Z tags. The release workflow
checks the genja-py package version against the genja-core-python and
genja-cli crate versions, and checks that the cli extra requires the
matching minor release line. For v0.5.0, that requirement is
genja-cli>=0.5.0,<0.6.0. Bump these versions together on the release branch.
Update the examples in Versions And Compatibility
with the released version at the same time.
The workflow builds platform wheels for both packages, tests base and optional
wheel-only installs, and publishes the CLI wheels before the dependent
genja-py wheels and source distribution. Configure PyPI trusted publishing
for both distributions before tagging the release. The CLI package has no
source distribution; unsupported platforms cannot build it through pip.
Confirm that the genja-cli PyPI project name can be claimed before release;
if unavailable, rename the wheel distribution and the genja-py extra's
dependency consistently.
Documentation package versions
The documentation package-version check is scoped to Markdown guides under
docs/, the repository README.md, and the package-root READMEs for
genja, genja-core, genja-core-derive, genja-plugin-manager,
genja-cli, and genja-core-python.
After updating the release examples, run the checker from the repository root:
python scripts/check-doc-versions.py --version X.Y.Z
Use --tag rs-vX.Y.Z or --tag py-vX.Y.Z to validate a tag. The checker
reports the file, line, found version, and expected version for each stale
reference. CI runs it for release/ISSUE-vX.Y.Z branches, including SemVer
pre-release suffixes such as release/163-v0.5.0-alpha.1, and before either
publish workflow proceeds. A pre-release branch checks documentation against
its base X.Y.Z version. To test the checker itself, run pdm run test-doc-versions
from genja-core-python.
Installation commands, Cargo dependencies, compatibility tables, release
examples, and prose describing the current Genja release must
use the intended release version. A compatible minor-version range, such as a
genja-py[cli] requirement, keeps its lower bound at the first patch release
of that minor line and its upper bound at the next minor line; those bounds do
not change for later patch releases.
Task IDs and task registration versions belong to task authors, so examples
such as example.hello@1.0.0 are outside this check. IP addresses and other
numbers unrelated to Genja package versions are also outside it. Historical
statements, such as when a feature first appeared, may retain an older version
with an explanation immediately before the reference:
<!-- genja-version-check: allow-next 0.5.0 reason="CLI support first appeared in this release" -->
The CLI feature became available in v0.5.0.
The allow-next comment exempts only the next recognized occurrence of the
specified Genja package version within three lines, including one inside a
following code block.
It must include a reason. Repeat it for each intentional older occurrence;
do not exclude a whole file. The checker rejects unused or mismatched
comments so moved text cannot silently leave stale exceptions behind.
Issue Templates
Choose a release, feature, or bug template from the
new-issue chooser.
For a release, replace every X.Y.Z placeholder, remove steps that do not
apply, and use the checkboxes to track preparation, validation, publication,
and post-release verification. Issue templates become available in the chooser
after they are merged into main.
Release Flow
Use develop as the integration branch and main as the release branch. Release
preparation should happen on a branch named release/ISSUE-vX.Y.Z, where
ISSUE is the release issue number, cut from develop. A SemVer pre-release
suffix such as -alpha.1 or -rc.1 may follow X.Y.Z during preparation;
the Rust and Python publication workflows currently require stable release tags:
feature/* -> PR -> develop
develop -> release/ISSUE-vX.Y.Z
release/ISSUE-vX.Y.Z -> PR -> main
main -> fast-forward develop
While the release pull request is open, avoid merging new work into develop so
develop can be fast-forwarded to the released main commit after publication.
If develop has moved ahead, do not force it; either finish the release sync
first or use a normal merge from main into develop.
After the release pull request is merged, tag the exact resulting main commit.
Rust and Python release tags may point at the same commit:
git checkout main
git pull origin main
git tag rs-vX.Y.Z
git tag py-vX.Y.Z
git push origin rs-vX.Y.Z py-vX.Y.Z
Fast-forward develop to the released commit before resuming feature merges:
git checkout develop
git pull origin develop
git merge --ff-only main
git push origin develop
Release Notes
User-facing compatibility information belongs in Versions And Compatibility. Maintainer-only publish steps should stay out of the user guides unless they are needed by contributors building release artifacts.
Contributor FAQ
Why are type hints or autocomplete missing in optional Rust modules?
Rust Analyzer may be analysing only default features. Enable the feature you
are working on, such as genja-cli/tui. See
Optional Feature Analysis for configuration and
Inline Type Hints for hint display settings.
Should I commit my VS Code settings?
Keep personal settings private unless maintainers agree to share them. See Personal Editor Settings for where to keep them.