Set up project tooling to match the rest of ~/dev's Rust fleet
Surveyed sporah/doubleo7/feedsignal/uy-immigration-watcher/notif-picker for conventions and replicated the current dominant pattern rather than inventing a new one: - Forgejo CI (.forgejo/workflows/ci.yml): build/test/audit jobs on the rust-ci runner label, cargo+sccache caching, cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo audit. Matches sporah/doubleo7/feedsignal/uy-immigration-watcher; notif-picker's docker-label/manual-toolchain-install variant looks like an earlier iteration superseded by this one. - Makefile.toml with format/format-check/lint/test/audit/build tasks and a `ci` task chaining them — copied from notif-picker's clean version, the only project that had this pattern. `cargo make ci` now runs the same checks locally that CI runs. - Explicit empty [workspace] in Cargo.toml (doubleo7's pattern) so a nested git-worktree checkout can't accidentally inherit an ancestor directory's workspace manifest. - rustfmt: no rustfmt.toml, matching every sibling project — default style is the established convention here, not an oversight. New for this fleet, since nothing else in ~/dev has it: a git-native pre-commit hook (.githooks/pre-commit, activated via `cargo make install-hooks` / `git config core.hooksPath .githooks`) that runs `cargo fmt` and re-stages whatever it reformats. Chose git's native hooksPath over the pre-commit(.com) framework or cargo-husky — no extra runtime dependency, hook is tracked and shareable, and nothing else here needs Python. Kept to formatting only; clippy/audit stay in CI, which already covers them and can run heavier checks than a commit hook should. Fixed one clippy finding (useless format! in checker.rs) and reformatted the existing code to match the now-enforced default rustfmt style. `cargo make ci` passes clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A2FEut5tVMNjeVjqhgVZbr
This commit is contained in:
parent
bf46cbf073
commit
388096264b
8 changed files with 149 additions and 13 deletions
80
.forgejo/workflows/ci.yml
Normal file
80
.forgejo/workflows/ci.yml
Normal file
|
|
@ -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
|
||||||
15
.githooks/pre-commit
Executable file
15
.githooks/pre-commit
Executable file
|
|
@ -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
|
||||||
|
|
@ -3,6 +3,12 @@ name = "pkgwatch"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
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]
|
[dependencies]
|
||||||
anyhow = "1.0.104"
|
anyhow = "1.0.104"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
|
|
||||||
37
Makefile.toml
Normal file
37
Makefile.toml
Normal file
|
|
@ -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"
|
||||||
|
|
@ -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 url = format!("https://github.com/{repo}/releases.atom");
|
||||||
let body = client.get(&url).send()?.error_for_status()?.text()?;
|
let body = client.get(&url).send()?.error_for_status()?.text()?;
|
||||||
|
|
||||||
let pattern = format!(r#"releases/tag/([^"]+)""#);
|
let re = Regex::new(r#"releases/tag/([^"]+)""#)?;
|
||||||
let re = Regex::new(&pattern)?;
|
|
||||||
match re.captures(&body) {
|
match re.captures(&body) {
|
||||||
Some(caps) => Ok(caps[1].to_string()),
|
Some(caps) => Ok(caps[1].to_string()),
|
||||||
None => bail!("no release tag found in {url}"),
|
None => bail!("no release tag found in {url}"),
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ pub fn load_packages_dir(dir: &Path) -> Result<Vec<(String, Package)>> {
|
||||||
}
|
}
|
||||||
let text = std::fs::read_to_string(&path)
|
let text = std::fs::read_to_string(&path)
|
||||||
.with_context(|| format!("reading {}", path.display()))?;
|
.with_context(|| format!("reading {}", path.display()))?;
|
||||||
let file: PackageFile = toml::from_str(&text)
|
let file: PackageFile =
|
||||||
.with_context(|| format!("parsing {}", path.display()))?;
|
toml::from_str(&text).with_context(|| format!("parsing {}", path.display()))?;
|
||||||
out.extend(file.package);
|
out.extend(file.package);
|
||||||
}
|
}
|
||||||
Ok(out)
|
Ok(out)
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,9 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
(_, true) => {
|
(_, true) => {
|
||||||
println!(" tier 4-6 pass: flagging for human review, not auto-publishing");
|
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) => {
|
(_, false) => {
|
||||||
println!(" verification failed — not publishing, not updating state");
|
println!(" verification failed — not publishing, not updating state");
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,8 @@ pub fn verify(
|
||||||
Verification::SameOriginSha256 {
|
Verification::SameOriginSha256 {
|
||||||
checksum_asset_pattern,
|
checksum_asset_pattern,
|
||||||
} => {
|
} => {
|
||||||
let checksum_path = fetcher::download_asset(
|
let checksum_path =
|
||||||
client,
|
fetcher::download_asset(client, repo, tag, checksum_asset_pattern, dest_dir)?;
|
||||||
repo,
|
|
||||||
tag,
|
|
||||||
checksum_asset_pattern,
|
|
||||||
dest_dir,
|
|
||||||
)?;
|
|
||||||
let checksum_text = std::fs::read_to_string(&checksum_path)?;
|
let checksum_text = std::fs::read_to_string(&checksum_path)?;
|
||||||
let expected = checksum_text
|
let expected = checksum_text
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
|
|
@ -66,7 +61,9 @@ pub fn verify(
|
||||||
repo,
|
repo,
|
||||||
])
|
])
|
||||||
.output()
|
.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();
|
let passed = output.status.success();
|
||||||
Ok(VerificationResult {
|
Ok(VerificationResult {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue