DriveStage/CONTRIBUTING.md

8.8 KiB
Executable File

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 info@dcos.net — dcos.net

Architecture context: read 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 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. Do not open public issues for security vulnerabilities.


How to submit patches

  1. Fork & branch. Fork the repo, create a feature branch off main:
    git clone https://github.com/<your-user>/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):
    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.


Development setup

Rust (GUI)

  • Rust 1.75+ via rustup:
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    rustup default stable
    
  • Linux GUI build deps (Ubuntu/Debian):
    sudo apt-get install -y \
        libgtk-3-dev libssl-dev pkg-config libdbus-1-dev libudev-dev
    
  • Build the GUI from the repo root:
    make gui          # cargo build --release in gui/
    # or directly:
    cargo build --release
    

Bash (engine)

Standard Linux utilities plus shellcheck for linting:

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:

make test           # runs both bash tests and cargo test
# or directly:
bash scripts/test_distro_detection.sh

Rust unit tests

cd gui && cargo test --locked
# or from repo root:
make test

Lint gate

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).
  • 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. Each commit message should look like:

<type>(<scope>): <subject>

<optional body>

<optional footer>

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?

Thanks for helping make drivestage better.