|
| 1 | +# Contributing to Harmont |
| 2 | + |
| 3 | +Thank you for your interest in making Harmont better — we're glad you're here. |
| 4 | +Contributions of every size are valued, whether that's a typo fix, a bug |
| 5 | +report with a great reproduction, or a new feature. This document explains how |
| 6 | +we work together, how to get your environment running, and how to get a change |
| 7 | +merged. |
| 8 | + |
| 9 | +If you just want to chat or ask a question, join us on |
| 10 | +[Discord](https://discord.gg/hm-dev) or |
| 11 | +[Slack](https://join.slack.com/t/harmont-dev/shared_invite/zt-3yt0tiv7r-qHm1O0p0nVh2GU~KKhUk9A). |
| 12 | + |
| 13 | +## Finding something to work on |
| 14 | + |
| 15 | +- Issues labeled [`good first issue`](https://github.com/harmont-dev/harmont-cli/labels/good%20first%20issue) |
| 16 | + are scoped for newcomers. |
| 17 | +- Issues labeled [`help wanted`](https://github.com/harmont-dev/harmont-cli/labels/help%20wanted) |
| 18 | + are ready for anyone to pick up. |
| 19 | +- Found a bug? [File an issue](#filing-issues) — a good bug report is a |
| 20 | + first-class contribution on its own. |
| 21 | + |
| 22 | +## Before you write code: open an issue |
| 23 | + |
| 24 | +**Every pull request must reference an issue that a maintainer has agreed |
| 25 | +should be fixed or built.** This applies to small fixes too — an issue takes |
| 26 | +two minutes to file and it means nobody's work gets wasted on a change we |
| 27 | +can't merge or that someone else is already making. |
| 28 | + |
| 29 | +The flow is: |
| 30 | + |
| 31 | +1. Open an issue (or comment on an existing one) describing the bug or the |
| 32 | + feature and how you'd like to approach it. |
| 33 | +2. Wait for a maintainer to confirm the direction. For bug fixes this is |
| 34 | + usually quick; for features expect some design discussion first — a new |
| 35 | + feature is a long-term maintenance commitment, so we want consensus before |
| 36 | + anyone invests time in code. |
| 37 | +3. Comment that you're taking it, then go build it. |
| 38 | + |
| 39 | +Pull requests for features that were never discussed in an issue will |
| 40 | +usually be closed with a pointer back to this section. It isn't personal — |
| 41 | +it's how we protect both your time and ours. |
| 42 | + |
| 43 | +You can expect a first response on issues and PRs within a week. If you |
| 44 | +haven't heard anything by then, feel free to ping the thread. |
| 45 | + |
| 46 | +## Use of AI |
| 47 | + |
| 48 | +AI-assisted contributions are welcome — much of Harmont is built with AI in |
| 49 | +the loop. The requirement is that **you** understand and stand behind what |
| 50 | +you submit: you can explain the change, you've run the tests, and the PR |
| 51 | +description is written (or at minimum, curated) by you. Please keep |
| 52 | +descriptions concise and specific; a giant auto-generated summary suggests |
| 53 | +the author doesn't know what the change does, and PRs that show no human |
| 54 | +understanding will be closed. |
| 55 | + |
| 56 | +## Setting up your environment |
| 57 | + |
| 58 | +Prerequisites: |
| 59 | + |
| 60 | +- **Rust** — latest stable via [rustup](https://rustup.rs). The workspace |
| 61 | + uses edition 2024, so you need a recent stable (1.85+). Formatting is |
| 62 | + pinned to **rustfmt 1.96** in CI; if your local `cargo fmt` produces |
| 63 | + unrelated diffs, update your toolchain (`rustup update stable`). |
| 64 | +- **Docker** — a running daemon. The local execution backend runs every |
| 65 | + pipeline step in a container, and the integration tests do too. On Linux, |
| 66 | + the layer-caching snapshotter also needs FUSE's `user_allow_other` enabled |
| 67 | + in `/etc/fuse.conf`. |
| 68 | +- **Python 3.11+** and [uv](https://docs.astral.sh/uv/) — for the `harmont` |
| 69 | + pipeline DSL that lives in `crates/hm-dsl-engine/harmont-py/`. Linting is |
| 70 | + pinned to **ruff 0.15** in CI. |
| 71 | + |
| 72 | +Build everything from the workspace root: |
| 73 | + |
| 74 | +```sh |
| 75 | +cargo build |
| 76 | +``` |
| 77 | + |
| 78 | +Run your freshly built CLI against a real project — the `examples/` |
| 79 | +directory has fourteen runnable projects to try it on: |
| 80 | + |
| 81 | +```sh |
| 82 | +cargo run -p harmont-cli -- run ci --backend docker --dir examples/rust |
| 83 | +``` |
| 84 | + |
| 85 | +## Running the checks |
| 86 | + |
| 87 | +CI runs the same checks you can run locally. Before pushing, this block |
| 88 | +should pass from the workspace root: |
| 89 | + |
| 90 | +```sh |
| 91 | +cargo test --workspace --locked |
| 92 | +cargo fmt --all --check |
| 93 | +cargo clippy --workspace --all-targets --locked -- -D warnings |
| 94 | +``` |
| 95 | + |
| 96 | +And for the Python DSL, from `crates/hm-dsl-engine/harmont-py/`: |
| 97 | + |
| 98 | +```sh |
| 99 | +uv sync --all-extras |
| 100 | +uv run pytest -v |
| 101 | +uv run ruff format --check . |
| 102 | +uv run ruff check . |
| 103 | +uv run ty check harmont |
| 104 | +``` |
| 105 | + |
| 106 | +Two things that surprise people: |
| 107 | + |
| 108 | +- **Clippy is strict.** The workspace flags `unwrap()`, `expect()`, |
| 109 | + `panic!`, `todo!`, `dbg!`, and direct `print!`/`eprintln!` in library |
| 110 | + code, plus the full pedantic and nursery sets — and CI runs clippy with |
| 111 | + `-D warnings`, so every one of them fails the build. Write error |
| 112 | + handling with `?` and real error types. |
| 113 | +- **pytest treats warnings as errors.** A deprecation warning from a |
| 114 | + dependency is a test failure; don't suppress it without a comment. |
| 115 | + |
| 116 | +You can also run the repo's own CI pipeline exactly as CI does — Harmont |
| 117 | +dogfoods itself via `.hm/ci.py`: |
| 118 | + |
| 119 | +```sh |
| 120 | +hm run ci --backend docker # locally, in Docker |
| 121 | +hm cloud login && hm run ci --org <your-org> # or on Harmont Cloud |
| 122 | +``` |
| 123 | + |
| 124 | +These lines use an installed `hm`; if you only have the freshly built |
| 125 | +workspace, substitute `cargo run -p harmont-cli --` for `hm`. The repo's |
| 126 | +`.hm/config.toml` pins the maintainer's cloud org, so pass `--org` to |
| 127 | +submit cloud runs to your own. |
| 128 | + |
| 129 | +Signing up at [app.harmont.dev](https://app.harmont.dev) gets you cloud runs |
| 130 | +of this same pipeline, which is the fastest way to reproduce a CI result. |
| 131 | + |
| 132 | +### Running a single test |
| 133 | + |
| 134 | +```sh |
| 135 | +cargo test -p harmont-cli --test cmd_init # one Rust integration test file |
| 136 | +cd crates/hm-dsl-engine/harmont-py && uv run pytest tests/test_rust.py -k <name> |
| 137 | +``` |
| 138 | + |
| 139 | +The Docker-backed integration tests (for example `crates/hm/tests/keep_going.rs`) |
| 140 | +are marked `#[ignore]`; run them with a live Docker daemon via |
| 141 | +`cargo test -p harmont-cli --test keep_going -- --ignored`. |
| 142 | + |
| 143 | +### Snapshot tests |
| 144 | + |
| 145 | +`hm-pipeline-ir` uses [insta](https://insta.rs) JSON snapshots. If you |
| 146 | +change the pipeline IR schema, review and accept the new snapshots with: |
| 147 | + |
| 148 | +```sh |
| 149 | +cargo insta review |
| 150 | +``` |
| 151 | + |
| 152 | +## How the workspace fits together |
| 153 | + |
| 154 | +| Crate | What it is | |
| 155 | +|---|---| |
| 156 | +| `crates/hm` | The `hm` binary (package `harmont-cli`) — command-line client for the Harmont CI platform. | |
| 157 | +| `crates/hm-exec` | Pluggable CI execution backends: local and cloud. | |
| 158 | +| `crates/hm-vm` | Local Docker backends that run pipeline steps on your machine. | |
| 159 | +| `crates/hm-render` | Build-event renderers (human, with progress bars, and JSON). | |
| 160 | +| `crates/hm-dsl-engine` | Evaluates Python pipeline definitions; contains the `harmont` Python package (`harmont-py/`). | |
| 161 | +| `crates/hm-pipeline-ir` | The pipeline IR wire-format schema. | |
| 162 | +| `crates/hm-config` | Layered configuration and credential storage. | |
| 163 | +| `crates/hm-plugin-cloud` | Cloud client library. | |
| 164 | +| `crates/hm-plugin-protocol` | Wire types shared between `hm` and plugins. | |
| 165 | +| `crates/hm-util` | Shared OS and filesystem utilities. | |
| 166 | + |
| 167 | +A useful mental model: the DSL engine evaluates your `.hm/*.py` pipeline |
| 168 | +into IR, an execution backend (local Docker or Harmont Cloud) schedules it |
| 169 | +as a DAG, and the binary renders the resulting event stream in your |
| 170 | +terminal. |
| 171 | + |
| 172 | +One sync rule to know about: the Python SDK in |
| 173 | +`crates/hm-dsl-engine/harmont-py/`, the `hm init` templates in |
| 174 | +`crates/hm/src/commands/init_templates/`, and the generated reference docs |
| 175 | +on docs.harmont.dev must stay in agreement. If you change a toolchain |
| 176 | +helper, update the matching template (roundtrip-tested by |
| 177 | +`crates/hm/tests/cmd_init.rs`) and refresh the docstring it is generated |
| 178 | +from; a maintainer will regenerate the published docs during review. |
| 179 | + |
| 180 | +## Debugging |
| 181 | + |
| 182 | +Every `hm` command accepts `-v`/`--verbose` for debug-level logging. For |
| 183 | +finer control, set `RUST_LOG` with standard tracing filter directives |
| 184 | +(for example `RUST_LOG=hm_exec=trace`); when set, it takes precedence over |
| 185 | +the flag. To see the raw `BuildEvent` stream that the renderers consume, |
| 186 | +run with `hm run --format json`. |
| 187 | + |
| 188 | +When chasing a local-execution bug, the fastest loop is usually a minimal |
| 189 | +pipeline in one of the `examples/` projects plus `--verbose`. |
| 190 | +For cloud-side behavior, `hm cloud login` followed by |
| 191 | +`hm run ci --org <your-org>` against your own |
| 192 | +[app.harmont.dev](https://app.harmont.dev) org reproduces what CI sees; |
| 193 | +the `--org` flag overrides the org pinned in `.hm/config.toml`. |
| 194 | + |
| 195 | +## Submitting a pull request |
| 196 | + |
| 197 | +- Branch from `main` in your fork. |
| 198 | +- Keep the PR scoped to its issue — resist drive-by refactors; file another |
| 199 | + issue instead. |
| 200 | +- PRs are **squash-merged**, so your PR title becomes the commit that lands. |
| 201 | + Write it as a [Conventional Commit](https://www.conventionalcommits.org): |
| 202 | + `fix(dsl): reject empty pipeline names`, `feat(render): add JSON event |
| 203 | + output`. Use `!` for breaking changes. Individual commits within the PR |
| 204 | + can be messy; the title cannot. |
| 205 | +- Fill in the PR description with two sections: **Summary** (what and why, |
| 206 | + linking the issue) and **Test plan** (what you ran and what you saw). |
| 207 | +- Mark the PR as draft while you're still iterating; when review comments |
| 208 | + are addressed, re-request review rather than waiting silently. |
| 209 | + |
| 210 | +## Filing issues |
| 211 | + |
| 212 | +For bugs, include: |
| 213 | + |
| 214 | +- `hm --version` output and your OS, |
| 215 | +- what you ran, what you expected, and what happened instead, |
| 216 | +- the smallest pipeline or command that reproduces it (the `examples/` |
| 217 | + projects make good starting points). |
| 218 | + |
| 219 | +For feature requests, describe the problem you're trying to solve before |
| 220 | +the solution you have in mind — the discussion usually starts there. |
| 221 | + |
| 222 | +Security issues: please email [marko@harmont.dev](mailto:marko@harmont.dev) |
| 223 | +instead of opening a public issue. |
| 224 | + |
| 225 | +## License |
| 226 | + |
| 227 | +Harmont is dual-licensed under [MIT](LICENSE-MIT) and |
| 228 | +[Apache 2.0](LICENSE-APACHE). Unless you explicitly state otherwise, any |
| 229 | +contribution you intentionally submit for inclusion in the work, as defined |
| 230 | +in the Apache-2.0 license, shall be dual-licensed as above, without any |
| 231 | +additional terms or conditions. (The `harmont` Python package carries its |
| 232 | +own MIT license; contributions to it are MIT-licensed.) |
| 233 | + |
| 234 | +## Code of Conduct |
| 235 | + |
| 236 | +This project follows the |
| 237 | +[Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By |
| 238 | +participating, you agree to abide by its terms. Conduct concerns go to |
| 239 | +[marko@harmont.dev](mailto:marko@harmont.dev). |
0 commit comments