# Contributing to drivestage Thanks for your interest in contributing to **drivestage**. This document covers everything you need to get a local development environment running and to send changes back upstream. **Maintainer:** Jeremy Anderson — [dcos.net](https://dcos.net) > **Architecture context:** read [`BLOG.md`](./BLOG.md) first. It explains the > ext4 + GRUB2 design, the boot-time auto-discovery model, and the rationale > behind every non-obvious decision in the codebase. PRs that contradict the > documented architecture without justification will be asked to motivate the > change. --- ## How to report bugs Open a [GitHub Issue](https://git.dcos.net/dcosnet/DriveStage/issues) and include: 1. **What you did** — exact command line (the full `blkstage.sh ...` invocation), the GUI button/tab, or the GRUB menu sequence. 2. **What you expected** — the observable outcome you were aiming for. 3. **What happened instead** — the actual output, error message, or panic backtrace. 4. **Environment** — distro + version, kernel, Rust version (if GUI-related), target USB device model/capacity, and ISO filenames involved. 5. **Logs** — capture with `sudo blkstage.sh --help`-equivalent verbose output or `RUST_LOG=debug` for the GUI. Redact sudo passwords — the script and GUI never log them, but please double-check anything you paste. **Security issues** are handled separately — see [`SECURITY.md`](./SECURITY.md). Do **not** open public issues for security vulnerabilities. --- ## How to submit patches 1. **Fork & branch.** Fork the repo, create a feature branch off `main`: ```bash git clone https://github.com//drivestage.git cd drivestage git checkout -b feat/my-feature ``` 2. **Make your changes.** Keep commits atomic and focused (see *Commit message conventions* below). 3. **Run the full local check suite** (see *Running tests*): ```bash make check test fmt clippy ``` CI runs the same gates — a green `make check test` will almost always mean a green CI run. 4. **Open a Pull Request** against `main`. Reference any related issue (`Closes #123`) in the PR body. Explain **why** the change is needed, not just **what** it does. 5. **Respond to review.** Maintainers may request changes; force-push to the same branch to update the PR. All contributions are merged under the project's [MIT license](./LICENSE). --- ## Development setup ### Rust (GUI) - Rust **1.75+** via [rustup](https://rustup.rs): ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup default stable ``` - Linux GUI build deps (Ubuntu/Debian): ```bash sudo apt-get install -y \ libgtk-3-dev libssl-dev pkg-config libdbus-1-dev libudev-dev ``` - Build the GUI from the repo root: ```bash make gui # cargo build --release in gui/ # or directly: cargo build --release ``` ### Bash (engine) Standard Linux utilities plus `shellcheck` for linting: ```bash sudo apt-get install -y \ bash parted sfdisk dosfstools e2fsprogs util-linux \ grub-pc-bin grub-efi-amd64-bin tar rsync wipefs shellcheck ``` Optional for non-GRUB bootloaders: `bootctl`, `limine`, `refind-install`. ### Working inside the source tree The repo is small enough that no special workspace tooling is required: ``` drivestage/ ├── scripts/blkstage.sh ← the engine (single bash file) ├── scripts/test_distro_detection.sh ├── gui/ ← Rust + Iced companion app └── (delivery files: Makefile, CHANGELOG.md, .github/, ...) ``` The bundled copy at `gui/assets/blkstage.sh` must stay byte-identical to `scripts/blkstage.sh` — run `make sync-assets` (or copy manually) before committing changes to the script. --- ## Running tests ### Bash distro-detection suite 30 filename → distro-profile assertions, no USB device required: ```bash make test # runs both bash tests and cargo test # or directly: bash scripts/test_distro_detection.sh ``` ### Rust unit tests ```bash cd gui && cargo test --locked # or from repo root: make test ``` ### Lint gate ```bash make check # cargo check + shellcheck make fmt # cargo fmt (writes) make clippy # cargo clippy -- -D warnings ``` `make fmt` writes; CI runs `cargo fmt --check` (read-only). Run `make fmt` locally before pushing. --- ## Code style ### Rust (`gui/`) - **rustfmt** is authoritative. `edition = "2021"`, `max_width = 100` (see [`rustfmt.toml`](./rustfmt.toml)). - **clippy** must pass with `-D warnings`. Don't `#[allow(...)]` without a comment explaining why. - Core logic lives under `gui/src/core/`; UI pages under `gui/src/ui/`. Don't put business logic in UI modules or `iced` calls in core modules. - Public functions get doc comments (`///`). Internal helpers may use `//`. - Errors flow through `anyhow::Result` at the boundary; `thiserror` types for typed errors in `core/error.rs`. ### Bash (`scripts/`) - **shellcheck** must pass. Use `shellcheck -x scripts/blkstage.sh` locally (`-x` allows following `source` across files). - `set -euo pipefail` is the script's baseline — every function inherits it. - Keep the engine as a single file. Don't split into a library tree. - Prefer `printf` over `echo` for any output that may contain backslashes or variable interpolation. - New distro support = one new `case` clause in `detect_distro_profile` **and** one new test case in `scripts/test_distro_detection.sh`. --- ## Commit message conventions This project uses [**Conventional Commits**](https://www.conventionalcommits.org/en/v1.0.0/). Each commit message should look like: ``` (): ``` ### Types | Type | When to use | |------------|------------------------------------------------------------------------| | `feat` | New user-facing capability (new subcommand, new GUI tab, new distro) | | `fix` | Bug fix | | `docs` | Documentation only (README, BLOG, CHANGELOG) | | `style` | Formatting, whitespace, rustfmt — no semantic change | | `refactor` | Code restructure with no behavior change | | `perf` | Performance improvement | | `test` | Adding or fixing tests | | `build` | Build system, Cargo.toml, Makefile, CI workflow | | `ci` | Changes to `.github/` | | `chore` | Tooling, repo hygiene, things not user-visible | ### Scopes Common scopes: `engine`, `gui`, `grub`, `distro`, `ci`, `deps`, `docs`. ### Examples ``` feat(engine): detect LVM/LUKS/mdadm-backed root in guard_root_disk Walks the lsblk PKNAME chain so the script refuses to operate when the host root filesystem sits on a layered block device, not just when /dev/sdX matches the root device directly. Closes #42. ``` ``` fix(grub): use --checkpoint=100 --checkpoint-action=dot GNU tar's `--checkpoint=.100` was never valid syntax. Corrected to the documented form so progress dots emit every 100 records during deploy_payload. ``` ``` docs: add CONTRIBUTING.md and SECURITY.md for v0.2.0 ``` ### Tips - Keep the **subject** to 50 characters where possible; wrap the body at 72. - Use the **imperative mood** ("add", "fix", "refactor") — the subject should complete the sentence *"If applied, this commit will ___."* - Reference issues in the footer: `Closes #42`, `Refs #17`. - Breaking changes: append `!` after the type/scope (`feat(engine)!:`) and add a `BREAKING CHANGE:` footer. --- ## Branch strategy - **`main`** — always shippable. Tags cut from `main`. Never push directly; everything goes through PR. - **Feature branches** — `feat/...`, `fix/...`, `docs/...`, `chore/...`. Short-lived; delete after merge. - **Release tags** — `v0.2.0`, `v0.2.1`, ...; follow SemVer. The CI workflow runs on tags too — a green tag build is the release artifact. - **Hotfix branches** — `hotfix/...` off `main`, merged straight back; bump the patch version. ### Merge policy - Squash-and-merge for single-logical-change PRs (preserves the conventional commit subject as the merge commit). - Rebase-and-merge for multi-commit PRs where each commit is meaningful on `main`. - Merge commits are reserved for release PRs that pull together many contributions. --- ## Need help? - Architecture & design intent: [`BLOG.md`](./BLOG.md) - Getting started: [`QUICKSTART.md`](./QUICKSTART.md) - Issues: [git.dcos.net/dcosnet/DriveStage/issues](https://git.dcos.net/dcosnet/DriveStage/issues) - Author: Jeremy Anderson — [dcos.net](https://dcos.net) Thanks for helping make drivestage better.