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
10 changes: 5 additions & 5 deletions .agents/rules/code-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
These are enforced by `rustfmt` and Clippy — do not fight them:

- **Formatting**: `cargo fmt` defaults (4 spaces, 100-column soft width).
Run `cargo fmt --check` before committing.
- **Lints**: `cargo clippy --all-targets -- -D warnings` must be clean. Fix
Run `cargo fmt --all --check` before committing.
- **Lints**: `cargo clippy --workspace --all-targets -- -D warnings` must be clean. Fix
lints — do not suppress them without a strong, documented reason.
- **File length**: keep source files cohesive and modular (target `<= 500
LOC` per file); extract helpers or modules when approaching the limit.
- **Module boundaries**: keep them tidy — the single crate splits into
`crates/` when `record` lands, so code that will move together should
already live together.
- **Crate boundaries**: keep them aligned with `plan.md` — CLI output in
`buildtrace-cli`, report model in `buildtrace-core`, manifest reading in
`buildtrace-cargo`. Code that will move together should already live together.
- **No dead code**: remove it or gate it behind the ticket that needs it.
No commented-out code blocks and no leftover debug output.

Expand Down
20 changes: 11 additions & 9 deletions .agents/rules/development-commands.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
## Development Commands

Run all commands from the **repo root** unless otherwise specified.
Run all commands from the **repo root** unless otherwise specified. The repo
is a Cargo workspace (`crates/`); `buildtrace-linux` and `buildtrace-policy`
arrive with `record`/`propose`.

```bash
# Check formatting (must pass)
cargo fmt --check
cargo fmt --all --check

# Lint with warnings denied (must pass)
cargo clippy --all-targets -- -D warnings
cargo clippy --workspace --all-targets -- -D warnings

# Run the full test suite (must pass)
cargo test
cargo test --workspace

# Run a single test by name
cargo test <name>
cargo test --workspace <name>

# Build the binary
cargo build
# Build all crates
cargo build --workspace

# Run inventory against the current directory
cargo run -- inventory
cargo run -p buildtrace-cli -- inventory

# Run inventory against another manifest
cargo run -- inventory --manifest-path <path/to/Cargo.toml>
cargo run -p buildtrace-cli -- inventory --manifest-path <path/to/Cargo.toml>
```

The installed binary is named `cargo-buildtrace`, so `cargo buildtrace
Expand Down
1 change: 1 addition & 0 deletions .agents/rules/technology-stack.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
| Layer | Technology |
|---|---|
| Language | Rust, edition 2024 |
| Layout | Cargo workspace (`crates/`): `buildtrace-cli` (commands, output, exit codes), `buildtrace-core` (report model, rendering), `buildtrace-cargo` (manifest reading, attribution); `buildtrace-linux` + `buildtrace-policy` arrive with `record`/`propose` |
| CLI | `clap` (derive) — binary name `cargo-buildtrace` so `cargo buildtrace` dispatch works |
| Serialization | `serde` + `serde_json` |
| Cargo metadata | `cargo_metadata` (metadata only — never execute dependency code) |
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Check formatting
run: cargo fmt --check
run: cargo fmt --all --check
- name: Clippy
run: cargo clippy -- -D warnings
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Build
run: cargo build --verbose
run: cargo build --workspace --verbose
- name: Run tests
run: cargo test --verbose
run: cargo test --workspace --verbose
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ This is a single-context repository using `CONTEXT.md` and `docs/adr/`. See `doc
BuildTrace is a Linux-first Cargo build capability recorder and policy-diff tool.

- **Facts over assumptions**: inspect source code and verify contracts directly. Do not rely on unverified assumptions.
- **Layout**: single `buildtrace` crate now; split into `crates/` when `record` lands. Keep module boundaries tidy so the split stays mechanical.
- **Gates**: `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test` must pass before a task counts as done.
- **Local-only files**: `plan.md`, `CONTEXT.md`, `docs/adr/`, `docs/agents/`, and `.scratch/` stay untracked. Never commit them.
- **Layout**: Cargo workspace with `crates/` from day one per `plan.md` (`buildtrace-cli`, `buildtrace-core`, `buildtrace-cargo` now; `buildtrace-linux`, `buildtrace-policy` arrive with `record`/`propose`). Keep crate boundaries tidy.
- **Gates**: `cargo fmt --all --check`, `cargo clippy --workspace --all-targets -- -D warnings`, `cargo test --workspace` must pass before a task counts as done.
- **Local-only files**: Never commit them gitignored files.
- **Clean git hygiene**: never commit secrets, API keys, `.env` files, or gitignored build output.

## Workspace Rules
Expand Down
17 changes: 16 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 7 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
[package]
name = "buildtrace"
version = "0.0.1"
edition = "2024"
license = "MIT OR Apache-2.0"

[[bin]]
name = "cargo-buildtrace"
path = "src/main.rs"

[dependencies]
clap = { version = "4.6.7", features = ["derive"] }
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
[workspace]
members = [
"crates/buildtrace-core",
"crates/buildtrace-cargo",
"crates/buildtrace-cli",
]
resolver = "2"
8 changes: 8 additions & 0 deletions crates/buildtrace-cargo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "buildtrace-cargo"
version = "0.0.1"
edition = "2024"
license = "MIT OR Apache-2.0"

[dependencies]
buildtrace-core = { path = "../buildtrace-core" }
50 changes: 50 additions & 0 deletions crates/buildtrace-cargo/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::path::Path;

use buildtrace_core::{Report, sort_report};

#[derive(Debug)]
pub struct InventoryError(String);

impl std::fmt::Display for InventoryError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}

/// The single test seam: everything (CLI and tests) goes through here.
///
/// v0.0.1 skeleton: validates the manifest is readable and returns an empty
/// report. Real package identities arrive with metadata support.
pub fn inventory(manifest_path: &Path) -> Result<Report, InventoryError> {
std::fs::read(manifest_path).map_err(|err| {
InventoryError(format!(
"cannot read manifest {}: {err}",
manifest_path.display()
))
})?;
let mut report = Report::default();
sort_report(&mut report);
Ok(report)
}

#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;

fn crate_manifest() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml")
}

#[test]
fn inventory_accepts_readable_manifest() {
let report = inventory(&crate_manifest()).expect("crate manifest reads");
assert!(report.packages.is_empty());
}

#[test]
fn inventory_rejects_missing_manifest() {
let err = inventory(Path::new("does-not-exist-Cargo.toml")).expect_err("must fail");
assert!(err.to_string().contains("cannot read manifest"));
}
}
14 changes: 14 additions & 0 deletions crates/buildtrace-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "buildtrace-cli"
version = "0.0.1"
edition = "2024"
license = "MIT OR Apache-2.0"

[[bin]]
name = "cargo-buildtrace"
path = "src/main.rs"

[dependencies]
buildtrace-cargo = { path = "../buildtrace-cargo" }
buildtrace-core = { path = "../buildtrace-core" }
clap = { version = "4.6.7", features = ["derive"] }
59 changes: 59 additions & 0 deletions crates/buildtrace-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::path::PathBuf;
use std::process::ExitCode;

use buildtrace_cargo::{InventoryError, inventory};
use buildtrace_core::{render_human, render_json};
use clap::{Parser, Subcommand, ValueEnum};

#[derive(Debug, Parser)]
#[command(
name = "cargo-buildtrace",
version,
about = "List dependency build scripts and procedural macros without executing them"
)]
struct Cli {
#[command(subcommand)]
command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
/// List build-time executables without executing them
Inventory {
/// Path to the manifest to inspect (defaults to ./Cargo.toml)
#[arg(long, default_value = "Cargo.toml")]
manifest_path: PathBuf,
/// Output format
#[arg(long, value_enum, default_value_t = Format::Human)]
format: Format,
},
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
enum Format {
Human,
Json,
}

fn run(cli: Cli) -> Result<(), InventoryError> {
let Command::Inventory {
manifest_path,
format,
} = cli.command;
let report = inventory(&manifest_path)?;
match format {
Format::Human => print!("{}", render_human(&report)),
Format::Json => println!("{}", render_json(&report)),
}
Ok(())
}

fn main() -> ExitCode {
match run(Cli::parse()) {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
eprintln!("error: {err}");
ExitCode::from(2)
}
}
}
9 changes: 9 additions & 0 deletions crates/buildtrace-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "buildtrace-core"
version = "0.0.1"
edition = "2024"
license = "MIT OR Apache-2.0"

[dependencies]
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
Loading
Loading