diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml new file mode 100644 index 0000000..2a94497 --- /dev/null +++ b/.forgejo/workflows/ci.yml @@ -0,0 +1,80 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +jobs: + build: + runs-on: rust-ci + steps: + - uses: actions/checkout@v4 + + - name: Cache cargo registry and build artifacts + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + cargo-${{ runner.os }}- + + - name: Cache sccache compilation objects + uses: actions/cache@v4 + with: + path: /root/.cache/sccache + # Not keyed to Cargo.lock: sccache caches individual compiler + # invocations by content hash, so it should accumulate across + # dependency bumps rather than reset like the target/ cache above. + key: sccache-${{ runner.os }}-${{ github.run_id }} + restore-keys: | + sccache-${{ runner.os }}- + + - name: Format check + run: cargo fmt --check + + - name: Clippy + run: cargo clippy --all-targets -- -D warnings + + - name: Check release profile compiles + run: cargo check --release + + test: + needs: build + runs-on: rust-ci + steps: + - uses: actions/checkout@v4 + + - name: Cache cargo registry and build artifacts + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + cargo-${{ runner.os }}- + + - name: Cache sccache compilation objects + uses: actions/cache@v4 + with: + path: /root/.cache/sccache + key: sccache-${{ runner.os }}-${{ github.run_id }} + restore-keys: | + sccache-${{ runner.os }}- + + - name: Test + run: cargo test + + audit: + needs: test + runs-on: rust-ci + steps: + - uses: actions/checkout@v4 + + - name: cargo audit + run: cargo audit diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..bd0d383 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,15 @@ +#!/bin/sh +# Auto-formats the project before every commit, using rustfmt's default +# style (no rustfmt.toml override — matches every sibling project in +# ~/dev). Tracked here rather than living only in .git/hooks/ so it's +# shareable across clones; activate once per clone with +# `cargo make install-hooks` (or `git config core.hooksPath .githooks`). +# +# Reformats the whole tree, not just staged files — simplest option for a +# single-person repo. Re-stages whatever rustfmt touched so the formatted +# version is what actually gets committed. +set -e + +cargo fmt + +git diff --name-only --diff-filter=M -- '*.rs' | xargs -r git add diff --git a/Cargo.toml b/Cargo.toml index feadc64..99c7867 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,12 @@ name = "pkgwatch" version = "0.1.0" edition = "2024" +# Not part of a Cargo workspace — this crate is the whole repo. Declared +# explicitly (rather than just omitting it) so that checking this repo out +# as a nested git worktree can't accidentally pick up an ancestor +# directory's workspace manifest. +[workspace] + [dependencies] anyhow = "1.0.104" hex = "0.4.3" diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 0000000..e215145 --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,37 @@ +[config] +default_to_workspace = false + +[tasks.format] +command = "cargo" +args = ["fmt"] + +[tasks.format-check] +command = "cargo" +args = ["fmt", "--check"] + +[tasks.lint] +command = "cargo" +args = ["clippy", "--all-targets", "--", "-D", "warnings"] + +[tasks.test] +command = "cargo" +args = ["test"] + +[tasks.audit] +command = "cargo" +args = ["audit"] + +[tasks.build] +command = "cargo" +args = ["build", "--release"] + +[tasks.install-hooks] +description = "One-time setup: point git at the tracked hooks in .githooks/" +command = "git" +args = ["config", "core.hooksPath", ".githooks"] + +[tasks.ci] +dependencies = ["format-check", "lint", "test", "audit"] + +[tasks.default] +alias = "ci" diff --git a/src/checker.rs b/src/checker.rs index 34225b9..5e4a925 100644 --- a/src/checker.rs +++ b/src/checker.rs @@ -11,8 +11,7 @@ pub fn latest_github_release(client: &reqwest::blocking::Client, repo: &str) -> let url = format!("https://github.com/{repo}/releases.atom"); let body = client.get(&url).send()?.error_for_status()?.text()?; - let pattern = format!(r#"releases/tag/([^"]+)""#); - let re = Regex::new(&pattern)?; + let re = Regex::new(r#"releases/tag/([^"]+)""#)?; match re.captures(&body) { Some(caps) => Ok(caps[1].to_string()), None => bail!("no release tag found in {url}"), diff --git a/src/config.rs b/src/config.rs index 71e609a..902d9d4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -46,8 +46,8 @@ pub fn load_packages_dir(dir: &Path) -> Result> { } let text = std::fs::read_to_string(&path) .with_context(|| format!("reading {}", path.display()))?; - let file: PackageFile = toml::from_str(&text) - .with_context(|| format!("parsing {}", path.display()))?; + let file: PackageFile = + toml::from_str(&text).with_context(|| format!("parsing {}", path.display()))?; out.extend(file.package); } Ok(out) diff --git a/src/main.rs b/src/main.rs index 9868f12..3a52395 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,7 +65,9 @@ fn main() -> Result<()> { } (_, true) => { println!(" tier 4-6 pass: flagging for human review, not auto-publishing"); - println!(" (review-queue persistence not yet implemented — this is where it plugs in)"); + println!( + " (review-queue persistence not yet implemented — this is where it plugs in)" + ); } (_, false) => { println!(" verification failed — not publishing, not updating state"); diff --git a/src/verifier.rs b/src/verifier.rs index dc7a723..c66aea6 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -26,13 +26,8 @@ pub fn verify( Verification::SameOriginSha256 { checksum_asset_pattern, } => { - let checksum_path = fetcher::download_asset( - client, - repo, - tag, - checksum_asset_pattern, - dest_dir, - )?; + let checksum_path = + fetcher::download_asset(client, repo, tag, checksum_asset_pattern, dest_dir)?; let checksum_text = std::fs::read_to_string(&checksum_path)?; let expected = checksum_text .split_whitespace() @@ -66,7 +61,9 @@ pub fn verify( repo, ]) .output() - .context("running `gh attestation verify` (is `gh` installed and authenticated?)")?; + .context( + "running `gh attestation verify` (is `gh` installed and authenticated?)", + )?; let passed = output.status.success(); Ok(VerificationResult {