This guide takes you from a fresh clone to a tested contribution. For an overview of Stove, start with the README; for using Stove in an application, see the user documentation.
Run commands from the repository root unless a working directory is shown. The
examples use mise exec -- so they work without changing your shell configuration.
If you plan to submit a pull request without write access, fork trendyol/stove first and clone your fork instead. Otherwise:
git clone https://github.com/trendyol/stove.git
cd stoveYou need Git, internet access for tool and dependency downloads, and mise 2026.9.1 or newer. The commands below target macOS and Linux. On Windows, use WSL2 with the repository and tools inside Linux, and enable your Docker runtime's WSL integration.
Mise manages language runtimes and development tools. A C compiler, native
libraries, and a Docker-compatible daemon must be installed separately for the
full repository build. Install and start your Docker runtime before setup; check
that docker info succeeds from the same terminal you will use for development.
On macOS, with Homebrew:
brew install mise libpq openssl@3 pkgconf
# Only if Xcode Command Line Tools are not already installed:
xcode-select --installOn Debian/Ubuntu, install mise using its installation instructions, then:
sudo apt-get update
sudo apt-get install build-essential libpq-dev libssl-dev pkg-configOther Linux distributions need the equivalent compiler, PostgreSQL client library headers, OpenSSL headers, and pkg-config packages. You do not need to start a system PostgreSQL service for the test containers. On macOS, the project makes Homebrew's libpq and OpenSSL paths available inside the mise environment.
From your clone:
mise trust
mise run setupmise trust allows this repository's configuration and setup scripts to run.
Cloning alone does not install or enable Git hooks: each newcomer must run
setup once in their checkout.
Setup performs the following steps:
- Installs the pinned tools from mise.toml: Java 25/21/17, Node.js, Go, Rust with rustfmt and Clippy, just, Lefthook, protoc, and Python.
- Installs the pre-commit hook and migrates the former Gradle hook's default-path setting when present.
- Installs dashboard dependencies from the npm lockfile.
- Checks the C compiler, protoc, PostgreSQL/OpenSSL libraries, and Docker daemon.
Successful setup ends with all prerequisite checks reporting OK. The first run
can take a while because it downloads several toolchains. It is safe to rerun
mise run setup after pulling updates. Running mise install directly also
installs the Git hook, but does not install dashboard dependencies or run doctor.
The setup only adjusts repository-local hook configuration. Git identity and
personal preferences stay unchanged. Custom hook paths are preserved; Lefthook
reports conflicts for manual resolution. If doctor fails, completed installation
steps remain usable. Fix the reported prerequisites and run mise run doctor.
mise exec -- just --list
mise run doctor
# Example: check the Kotlin/JVM projects
mise exec -- just lint-jvmChoose the check for your area from the table below. A first JVM or Rust check also downloads dependencies and can take longer than later runs. Lint checks are separate from the test suites.
| Tool | Purpose | Configuration |
|---|---|---|
| mise | Installs pinned tools and supplies the project environment | mise.toml |
| just | Provides named commands for checks, formatting, and setup | justfile |
| Lefthook | Runs the relevant checks before a commit | lefthook.yml |
| Gradle wrappers | Build and test the JVM projects using their checked-in Gradle version | gradlew, JVM recipes wrapper |
You can optionally activate mise in your shell
to type just lint-jvm instead of mise exec -- just lint-jvm. Git hooks invoke
mise exec themselves, so shell activation is optional; mise must still be on
PATH in the terminal or Git client that creates the commit.
Keep personal mise overrides in the ignored mise.local.toml. Use the shared
versions when reproducing a failure before attributing it to the repository.
| Area | Location | Starting check |
|---|---|---|
| Core library and system integrations | lib/ | mise exec -- just lint-jvm |
| Framework, process, and container runners | starters/ | mise exec -- just lint-jvm |
| JUnit/Kotest support and Gradle plugins | test-extensions/, plugins/ | mise exec -- just lint-jvm |
| JVM example applications | examples/ | mise exec -- just lint-jvm |
| Standalone JVM recipes | recipes/jvm/ | mise exec -- just lint-recipes |
| Go library and process examples | go/stove-kafka/, recipes/process/golang/go-showcase/ | mise exec -- just lint-go |
| Rust dashboard server | server/stove-server/ | mise exec -- just lint-rust |
| React dashboard | server/stove-server/spa/ | mise exec -- just lint-spa |
| User documentation | docs/, mkdocs.yml | See documentation development |
| Shared build and onboarding | build-logic/, gradle/, scripts/ | Run affected groups and the onboarding tests below |
The main JVM project, JVM recipes, and Go process recipe are separate Gradle
builds. Use each build's wrapper and working directory as shown in the test
commands below. Process-recipe Kotlin tests are covered by the process E2E build,
not lint-recipes or lint-go.
For a new system integration, read Writing custom systems. Existing modules and tests are useful examples of the repository's conventions.
For Kotlin/JVM work, open the root Gradle project in an IDE with Kotlin support.
Use the checked-in Gradle wrapper and JDK 25 as the Gradle JVM. Individual
modules also use JDK 17 and 21; mise exports JAVA_HOME, STOVE_JDK21, and
STOVE_JDK17, which the Gradle configuration uses to discover those installations.
mise which java
mise exec -- ./gradlew javaToolchainsThe first command identifies the Java executable in the managed JDK 25; its JDK
home is the directory above bin. Configure that home as the IDE's Gradle JVM.
An IDE launched from the desktop may not inherit mise's environment. Launch it
from a mise-enabled terminal or configure the additional JDK locations in its
Gradle environment, then reimport the project. Import standalone recipe builds
separately when working on them.
For Rust and dashboard work, point the editor at the corresponding server or SPA directory and use the mise-managed Rust and Node.js tools. Enable EditorConfig support for the shared .editorconfig settings; the command-line checks remain the reference for formatting and analysis.
Create a branch for your change, make focused edits, then format and check the area you touched. For example, for JVM work:
mise exec -- just format-jvm
mise exec -- just lint-jvm
mise exec -- ./gradlew :lib:stove:testReplace the test module with the one you changed. To discover Gradle projects,
run mise exec -- ./gradlew projects. Gradle test reports are written under each
module's build/reports/tests/ directory.
Useful root commands:
mise exec -- just lint # Check every group, even on a clean tree
mise exec -- just lint-jvm lint-spa # Check selected groups
mise exec -- just format # Apply all formatters
mise exec -- just format-recipes # Format only the JVM recipes
mise exec -- just api-dump # Deliberately update JVM API baselines
mise exec -- lefthook run pre-commit # Check groups selected by staged pathsEach group (jvm, recipes, rust, spa, go) has a lint- and a format-
command. Formatting applies the tools' formatting and automatic fixes without
running Detekt, Clippy, typechecks, Go vet, or API checks. Review the diff and rerun
lint after formatting. Update public API baselines only for intentional API
changes, and include the resulting .api diff for review.
The JVM command requests Spotless, Detekt, and API checks. The current Gradle
configuration disables Detekt on Java 25, so detekt SKIPPED is expected with
the default toolchain; a successful run does not mean Detekt analyzed the code.
When you commit, pre-commit checks run in parallel. Staged paths select whole project groups, and those checks read the working tree, including unstaged edits in a selected project. Hooks do not format or stage files. Review and stage any fixes before retrying a commit.
JVM recipe changes do not select the main JVM build; Go recipe changes do not select JVM recipe lint. The shared version catalog selects both JVM builds. Changes to the root justfile, Lefthook configuration, or mise configuration and environment script select all groups. A documentation-only commit may have no matching lint jobs. Passing the hook does not replace running relevant tests.
After pulling changes to tool versions or setup configuration, rerun
mise run setup. For only an npm lockfile change, use
mise exec -- just setup-spa. To reinstall hooks independently, run
mise exec -- just hooks.
CI workflows in .github/workflows/ are the source of truth for checks and tool setup. The commands below run their main checks locally with the pinned mise environment. Container-backed tests require Docker and download images on first use. Start with your affected module before running broader suites.
# Main JVM build, tests, and aggregate coverage report
mise exec -- ./gradlew build :koverXmlReport
# Standalone JVM recipes
mise exec -- ./recipes/jvm/gradlew -p recipes/jvm build e2eTest --parallel
# Go process recipes and the sarama container test
mise exec -- sh -c '
export GO_EXECUTABLE="$(command -v go)"
./recipes/process/golang/go-showcase/gradlew \
-p recipes/process/golang/go-showcase e2eTest e2eTest-container
'The Go executable is resolved inside mise's environment so Gradle uses the managed version. The container test removes its image in a Gradle finalizer, including after a test failure. Process tests run sequentially to bound container usage.
Build the SPA before testing embedded assets. These commands all run from the repository root:
mise exec -- just setup-spa
mise exec -- npm --prefix server/stove-server/spa run check:api
mise exec -- just lint-spa
mise exec -- npm --prefix server/stove-server/spa test
mise exec -- npm --prefix server/stove-server/spa run build
mise exec -- just --justfile server/stove-server/justfile verifyThe server's verify command includes Rust formatting, Clippy, unit/API/MCP tests,
acceptance tests, and the PostgreSQL load test. Cargo skips rebuilding the SPA
because it was built above. If you change the REST API, regenerate the dashboard
types with mise exec -- npm --prefix server/stove-server/spa run generate:api
and review the generated diff.
To try the dashboard locally with its PostgreSQL-backed server:
mise exec -- just --justfile server/stove-server/justfile postgres-up
# Stop the stack while keeping PostgreSQL data:
mise exec -- just --justfile server/stove-server/justfile postgres-downThe stack builds from source and serves the dashboard at http://localhost:4040
by default. For frontend iteration, leave the stack running and, in another
terminal, run mise exec -- npm --prefix server/stove-server/spa run dev. Open
the URL printed by Vite; its /api requests proxy to the server on port 4040.
See the dashboard guide for usage.
The integration tests use temporary repositories and stubbed language tools to verify onboarding, command execution, and hook routing:
mise exec -- python3 -m unittest discover -s scripts/tests -vInstall the documentation dependencies in a local virtual environment:
mise exec -- python3 -m venv .venv
.venv/bin/python -m pip install -r requirements-docs.txt
.venv/bin/mkdocs serveOpen the address printed by MkDocs. Before submitting documentation changes, run
.venv/bin/mkdocs build --strict, matching the documentation workflow. The virtual
environment and generated site/ directory are ignored by Git. The documentation
hooks also regenerate the tracked docs/assets/data/setup.json; review any changes
to it when editing the data behind the setup wizard.
| Symptom | What to check |
|---|---|
mise is missing during setup or a commit |
Install mise and make it available on PATH to your terminal or Git client. Restart the client after changing its environment. Shell activation alone is not required by the hook. |
| Mise says the repository is untrusted | Review the checkout and run mise trust from its root. |
| Doctor reports a missing prerequisite | Install the system packages listed above or start Docker, then run mise run doctor. Full setup need not be repeated for this check. |
| Docker is installed but tests cannot connect | Run docker info in the same environment as the tests; verify the active Docker context and any runtime-specific Testcontainers configuration. See troubleshooting. |
| Gradle uses the wrong Java version or cannot find a toolchain | Run through mise exec --, inspect ./gradlew javaToolchains in that environment, and check the IDE's Gradle JVM/environment settings. |
| Rust cannot find libpq, OpenSSL, or protoc | Install the native packages and rerun doctor. On macOS, use the mise environment so the Homebrew library paths are applied. |
| SPA tools or dependencies are missing | Run mise exec -- just setup-spa, especially after a lockfile update. |
| A formatting check fails | Run the relevant just format-GROUP through mise, inspect the diff, and rerun lint-GROUP. Stage the fixes if preparing a commit. |
apiCheck fails |
Decide whether the public API change is intended. For an intentional change, run mise exec -- just api-dump and review the baseline diff; otherwise fix the API regression. |
| Hook installation reports a custom hooks path | Inspect git config --show-origin --get core.hooksPath. Integrate Lefthook with your existing hook setup or remove the override only if you no longer need it, then rerun mise exec -- just hooks. |
| A commit checks more files than expected | Staged paths select whole groups, and shared configuration can select all groups. Unstaged edits in those groups are checked too. |
When reporting a build or setup issue, include your OS, the command that failed,
the relevant output, and whether mise run doctor succeeds. Include Docker runtime
information for container-related failures.
- Keep the change focused and explain the problem and resulting behavior. Link a related issue when there is one; discuss large design changes in an issue first.
- Follow nearby code conventions and add or update tests for changed behavior.
- Run the relevant formatting, lint, and test commands. Record what passed and any checks you could not run in the pull request description.
- Update user documentation and examples when public behavior changes. Review intentional public API baseline and generated-file changes alongside the code.
- Inspect your diff before committing. Keep local environment overrides, editor settings, and build outputs out of the contribution.
- Open the pull request against
mainand address applicable CI failures and review feedback.
Gradle reuses its daemon, configuration cache, and task outputs locally. Avoid
clean or --no-daemon during normal iteration unless diagnosing a specific issue.
GitHub's Gradle setup action owns the Gradle cache; a second cache of the same
directories would duplicate downloads and uploads. CI uses the runner's Docker
daemon directly and does not archive its live /var/lib/docker directory.
Rust dependency artifacts are cached using the toolchain and Cargo inputs. CI turns
off incremental compilation and debug symbols to reduce cache size; local Cargo
defaults stay unchanged. Gradle and Rust cache writes are limited to main, with
pull requests restoring those caches. Workflow concurrency cancels obsolete runs
for the same branch.