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.mdfirst. 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:
- What you did — exact command line (the full
blkstage.sh ...invocation), the GUI button/tab, or the GRUB menu sequence. - What you expected — the observable outcome you were aiming for.
- What happened instead — the actual output, error message, or panic backtrace.
- Environment — distro + version, kernel, Rust version (if GUI-related), target USB device model/capacity, and ISO filenames involved.
- Logs — capture with
sudo blkstage.sh --help-equivalent verbose output orRUST_LOG=debugfor 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
- 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 - Make your changes. Keep commits atomic and focused (see Commit message conventions below).
- Run the full local check suite (see Running tests):
CI runs the same gates — a greenmake check test fmt clippymake check testwill almost always mean a green CI run. - 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. - 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(seerustfmt.toml). - clippy must pass with
-D warnings. Don't#[allow(...)]without a comment explaining why. - Core logic lives under
gui/src/core/; UI pages undergui/src/ui/. Don't put business logic in UI modules oricedcalls in core modules. - Public functions get doc comments (
///). Internal helpers may use//. - Errors flow through
anyhow::Resultat the boundary;thiserrortypes for typed errors incore/error.rs.
Bash (scripts/)
- shellcheck must pass. Use
shellcheck -x scripts/blkstage.shlocally (-xallows followingsourceacross files). set -euo pipefailis the script's baseline — every function inherits it.- Keep the engine as a single file. Don't split into a library tree.
- Prefer
printfoverechofor any output that may contain backslashes or variable interpolation. - New distro support = one new
caseclause indetect_distro_profileand one new test case inscripts/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 aBREAKING CHANGE:footer.
Branch strategy
main— always shippable. Tags cut frommain. 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/...offmain, 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 - Getting started:
QUICKSTART.md - Issues: git.dcos.net/dcosnet/DriveStage/issues
- Author: Jeremy Anderson info@dcos.net — dcos.net
Thanks for helping make drivestage better.