Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Rust Development Guidelines

You are an expert Rust developer working on the `HaH` project. Follow these project-specific patterns and Rust best practices.

## Project Architecture
- `hah-core`: The base logic, models, and rule engine.
- `hah-checks`: Implementations of specific system checks (Apt, Boot, Network, etc.).
- `hah`: The CLI entry point.

## Coding Patterns
- **Error Handling**: Use `anyhow::Result` for application-level logic and `anyhow!` for formatting errors.
- **Rule Values**: Use the `RuleValue` enum from `hah-core` for data passing between checks and the engine.
- **Mocking**: Use `mockall` for traits used in external interactions (like `CommandRunner`).

## Tool Usage
- Use `cargo fmt` for formatting.
- Use `cargo clippy` with `-D warnings` for linting.
- Use `cargo llvm-cov` for coverage analysis.

## Memory & Performance
- Prefer passing references (`&str`, `&[T]`) over cloning where possible.
- Use `Iterator` methods (`map`, `filter`, `flat_map`, `fold`) instead of explicit `for` loops or mutable state when processing collections.
- Avoid `unwrap()` and `expect()` in production code; use proper error propagation. `clippy::unwrap_used` and `clippy::expect_used` are denied in this project except in tests.

## Functional Programming & Testability
- **Pure Functions**: Favor small, pure functions that take inputs and return outputs without side effects. Logic that doesn't require I/O should be extracted from "impure" functions.
- **Immutability**: Prefer `let` bindings over `let mut` where possible. Transformations should return new data structures rather than mutating in-place.
- **Dependency Injection**: Pass dependencies (like `CommandRunner`) as arguments or generic traits to make side-effecting code testable with mocks.
- **Composition**: Use closure-based composition and combinators (like `Option::map`, `Result::and_then`) to handle control flow.

## Minimalism & Code Health
- **KISS (Keep It Simple, Stupid)**: Favor the simplest solution that satisfies the requirements. Avoid over-engineering or premature abstraction.
- **DRY (Don't Repeat Yourself)**: Extract common logic into reusable functions or traits. If a logic pattern appears three times, it must be abstracted.
- **Minimal Complexity**: Keep functions small and focused on a single responsibility. If a function has deep nesting or many branches, refactor it into smaller, composed pure functions.
- **Dead Code**: Ensure no unused imports, variables, or functions remain. Trust the compiler and Clippy to identify unnecessary complexity.

## Testing
- Add unit tests in a `mod tests` block at the bottom of the file.
- Add integration tests in `crates/hah/tests/`.
- Ensure new public functions are covered by tests per `AGENTS.md`.
42 changes: 42 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: PR Check

on:
pull_request:
branches: [main]
push:
branches: [main]

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

jobs:
quality:
name: Format / Lint / Test / Audit
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt

- name: Cache Cargo registry & build artifacts
uses: Swatinem/rust-cache@v2

- name: Check formatting
run: cargo fmt --all -- --check

- name: Clippy (deny warnings)
run: cargo clippy --all-targets -- -D warnings

- name: Run tests
run: cargo test --all

- name: Security audit
uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
82 changes: 82 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Release

on:
push:
tags:
- 'v[0-9]*'

env:
CARGO_TERM_COLOR: always

jobs:
build:
name: Build — ${{ matrix.target }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
runner: ubuntu-latest
- target: aarch64-unknown-linux-gnu
runner: ubuntu-latest
cross: true

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Cache Cargo registry & build artifacts
uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}

- name: Install cross (for cross-compilation)
if: matrix.cross
run: cargo install cross --locked

- name: Build release binary
run: |
if [ "${{ matrix.cross }}" = "true" ]; then
cross build --release --target ${{ matrix.target }}
else
cargo build --release --target ${{ matrix.target }}
fi

- name: Rename binary
run: |
src="target/${{ matrix.target }}/release/hah"
dst="hah-${{ matrix.target }}"
cp "$src" "$dst"

- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: hah-${{ matrix.target }}
path: hah-${{ matrix.target }}
if-no-files-found: error

release:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/

- name: Create release
uses: softprops/action-gh-release@v2
with:
name: ${{ github.ref_name }}
generate_release_notes: true
files: artifacts/**/*
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
41 changes: 41 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Agent Guidelines

## Validation Gate

Every change **must** pass the full quality gate before being considered done:

```bash
make check
```

This runs, in order:

| Step | Command | Requirement |
| -------------- | ---------------------------------------------------------------- | ----------------------- |
| Format check | `cargo fmt --all -- --check` | No formatting diffs |
| Lint | `cargo clippy --all-targets -- -D warnings` | Zero warnings |
| Tests | `cargo test --all` | All tests green |
| Security audit | `cargo audit` | No unpatched advisories |
| Coverage | `cargo llvm-cov --all-targets --workspace --fail-under-lines 95` | ≥ 95 % line coverage |

### One-time setup (if tools are missing)

```bash
make setup
```

## Workflow

1. Make your changes.
2. Run `make fmt` to auto-format before committing.
3. Run `make check` and fix every reported issue.
4. Do **not** submit or push until `make check` exits with code 0.

## Non-negotiable rules

- Never suppress a Clippy warning with `#[allow(...)]` unless it is inside a `#[cfg(test)]` block and the suppression is genuinely test-only (e.g. `clippy::unwrap_used`, `clippy::panic`).
- Never lower or remove the `--fail-under-lines 95` threshold.
- New public functions must have tests that exercise their main code paths.

## Relevant Instructions
- **[.copilot-instructions.md](.copilot-instructions.md)**: Contains detailed Rust coding patterns, project architecture overview, and error handling policies. Always refer to this for implementation details.
38 changes: 38 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[workspace]
members = [
"crates/hah",
"crates/hah-core",
"crates/hah-dsl",
"crates/hah-checks",
"crates/hah-utils",
]
resolver = "2"

[workspace.dependencies]
mockall = "0.13"

[workspace.lints.clippy]
# ── Functional iterator / combinator style ──────────────────────────────────
cloned_instead_of_copied = "warn"
filter_map_next = "warn"
flat_map_option = "warn"
map_flatten = "warn"
map_unwrap_or = "warn"
needless_collect = "warn"
option_if_let_else = "warn"
or_fun_call = "warn"
redundant_closure_for_method_calls = "warn"

# ── Error handling (no silent panics in production code) ─────────────────────
unwrap_used = "warn"
expect_used = "warn"
panic = "warn"

# ── Immutability / purity ────────────────────────────────────────────────────
mut_mut = "warn"
needless_pass_by_ref_mut = "warn"

[workspace.lints.rust]
# `coverage` cfg is set by cargo-llvm-cov during coverage measurement.
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] }
warnings = "deny"
28 changes: 28 additions & 0 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Dependencies

Direct dependencies of the HaH workspace crates.
_Generated by `make doc-dependencies` — do not edit by hand._

## Runtime Dependencies

| Crate | Version | License | Purpose |
| ----- | ------- | ------- | ------- |
| [anyhow](https://crates.io/crates/anyhow) | 1 | MIT OR Apache-2.0 | Flexible concrete Error type built on std::error::Error |
| [bytesize](https://crates.io/crates/bytesize) | 1 | Apache-2.0 | an utility for human-readable bytes representations |
| [clap](https://crates.io/crates/clap) | 4 | MIT OR Apache-2.0 | A simple to use, efficient, and full-featured Command Line Argument Parser |
| [colored](https://crates.io/crates/colored) | 2 | MPL-2.0 | The most simple way to add colors in your terminal |
| [dirs](https://crates.io/crates/dirs) | 5 | MIT OR Apache-2.0 | A tiny low-level library that provides platform-specific standard locations of directories for config, cache and other data on Linux, Windows, macOS and Redox by leveraging the mechanisms defined by the XDG base/user directory specifications on Linux, the Known Folder API on Windows, and the Standard Directory guidelines on macOS |
| [regex](https://crates.io/crates/regex) | 1 | MIT OR Apache-2.0 | An implementation of regular expressions for Rust. This implementation uses
finite automata and guarantees linear time matching on all inputs |
| [serde](https://crates.io/crates/serde) | 1 | MIT OR Apache-2.0 | A generic serialization/deserialization framework |
| [serde_json](https://crates.io/crates/serde_json) | 1 | MIT OR Apache-2.0 | A JSON serialization file format |
| [serde_yaml_ng](https://crates.io/crates/serde_yaml_ng) | 0.9 | MIT OR Apache-2.0 | YAML data format for Serde |
| [walkdir](https://crates.io/crates/walkdir) | 2 | Unlicense/MIT | Recursively walk a directory |

## Development-only Dependencies

| Crate | Version | License | Purpose |
| ----- | ------- | ------- | ------- |
| [filetime](https://crates.io/crates/filetime) | 0.2 | MIT/Apache-2.0 | Platform-agnostic accessors of timestamps in File metadata |
| [mockall](https://crates.io/crates/mockall) | 0.13 | MIT OR Apache-2.0 | A powerful mock object library for Rust |
| [tempfile](https://crates.io/crates/tempfile) | 3 | MIT OR Apache-2.0 | A library for managing temporary files and directories |
44 changes: 44 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.PHONY: all setup fmt fmt-check lint test audit check doc-dependencies

all: check

## Install required Cargo tools (run once)
setup:
cargo install cargo-audit
cargo install cargo-llvm-cov
rustup component add llvm-tools-preview

## Auto-format all code
fmt:
cargo fmt --all

## Check formatting without modifying files (used in CI)
fmt-check:
cargo fmt --all -- --check

## Run Clippy; treat all warnings as errors
lint:
cargo clippy --all-targets -- -D warnings

## Run all tests
test:
cargo test --all

## Run security audit against RustSec advisory database
audit:
cargo audit

## Generate HTML coverage report (opens in target/llvm-cov/html/)
coverage:
cargo llvm-cov --all-targets --workspace --html

## Fail the build if line coverage drops below 95 %
coverage-ci:
cargo llvm-cov --all-targets --workspace --fail-under-lines 95

## Full quality gate: format-check + lint + test + audit + coverage
check: fmt-check lint test audit coverage-ci

## Regenerate DEPENDENCIES.md from live cargo metadata
doc-dependencies:
python3 tools/gen_deps_doc.py > DEPENDENCIES.md
Loading
Loading