From 15f619ccf76768c152eb0c495fdfb4efd3b374a0 Mon Sep 17 00:00:00 2001 From: qpismont Date: Fri, 31 Jul 2026 20:06:37 +0000 Subject: [PATCH 1/7] Move to multi crates project Starting impl devcontainer spec --- .devcontainer/Dockerfile | 11 +- .devcontainer/devcontainer.json | 1 + .dockerignore | 4 - .vscode/settings.json | 3 - .zed/settings.json | 11 ++ Cargo.lock | 17 +- Cargo.toml | 22 ++- Containerfile | 13 +- crates/devcontainer-rs/Cargo.toml | 14 ++ crates/devcontainer-rs/src/lib.rs | 175 ++++++++++++++++++ crates/herald-server/Cargo.toml | 30 +++ {src => crates/herald-server/src}/api.rs | 0 {src => crates/herald-server/src}/bot.rs | 0 .../herald-server/src}/bot_actions/mod.rs | 0 .../herald-server/src}/bot_actions/review.rs | 0 {src => crates/herald-server/src}/consts.rs | 0 {src => crates/herald-server/src}/env.rs | 0 {src => crates/herald-server/src}/errors.rs | 0 {src => crates/herald-server/src}/gitea.rs | 0 {src => crates/herald-server/src}/main.rs | 0 {src => crates/herald-server/src}/metrics.rs | 0 .../herald-server/src}/open_router.rs | 0 {src => crates/herald-server/src}/state.rs | 0 23 files changed, 271 insertions(+), 30 deletions(-) delete mode 100644 .dockerignore delete mode 100644 .vscode/settings.json create mode 100644 .zed/settings.json create mode 100644 crates/devcontainer-rs/Cargo.toml create mode 100644 crates/devcontainer-rs/src/lib.rs create mode 100644 crates/herald-server/Cargo.toml rename {src => crates/herald-server/src}/api.rs (100%) rename {src => crates/herald-server/src}/bot.rs (100%) rename {src => crates/herald-server/src}/bot_actions/mod.rs (100%) rename {src => crates/herald-server/src}/bot_actions/review.rs (100%) rename {src => crates/herald-server/src}/consts.rs (100%) rename {src => crates/herald-server/src}/env.rs (100%) rename {src => crates/herald-server/src}/errors.rs (100%) rename {src => crates/herald-server/src}/gitea.rs (100%) rename {src => crates/herald-server/src}/main.rs (100%) rename {src => crates/herald-server/src}/metrics.rs (100%) rename {src => crates/herald-server/src}/open_router.rs (100%) rename {src => crates/herald-server/src}/state.rs (100%) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 1b89658..dd02efa 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:trixie +FROM rust:1.97-trixie ARG USERNAME=dev ARG USER_UID=1000 @@ -18,11 +18,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* RUN groupadd --gid ${USER_GID:-1000} $USERNAME \ - && useradd --uid ${USER_UID:-1000} --gid ${USER_GID:-1000} -m $USERNAME + && useradd --uid ${USER_UID:-1000} --gid ${USER_GID:-1000} -m $USERNAME \ + && rustup component add clippy + + USER $USERNAME WORKDIR /home/$USERNAME - -ENV PATH="/home/${USERNAME}/.cargo/bin:${PATH}" - -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a0fc52a..b9310d6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -19,5 +19,6 @@ }, "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/herald,type=bind", "workspaceFolder": "/workspaces/herald", + "runArgs": ["--userns=keep-id", "--security-opt", "label=disable"], "appPort": [3000] } diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 89343aa..0000000 --- a/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -target/ -.env -.devcontainer/ -docs/ diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 660eb93..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rust-analyzer.check.command": "clippy" -} \ No newline at end of file diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000..d569920 --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,11 @@ +{ + "lsp": { + "rust-analyzer": { + "initialization_options": { + "check": { + "command": "clippy" + } + } + } + } +} diff --git a/Cargo.lock b/Cargo.lock index 7f4d25b..3def019 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,6 +490,18 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "devcontainer-rs" +version = "0.1.0" +dependencies = [ + "anyhow", + "serde", + "serde_json", + "tempfile", + "thiserror 2.0.18", + "tokio", +] + [[package]] name = "dispatch2" version = "0.3.1" @@ -787,12 +799,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] -name = "herald" -version = "1.1.0" +name = "herald-server" +version = "1.2.0" dependencies = [ "anyhow", "axum", "bytes", + "devcontainer-rs", "dotenvy", "futures-util", "hex", diff --git a/Cargo.toml b/Cargo.toml index 3340e25..0365198 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,11 @@ -[package] -name = "herald" -version = "1.1.0" -edition = "2024" +[workspace] +members = [ + "crates/herald-server", + "crates/devcontainer-rs", +] +resolver = "3" -[profile.release] -debug = 1 - -[dependencies] +[workspace.dependencies] reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } tokio = { version = "1.53", features = ["full"] } tokio-stream = "0.1" @@ -19,9 +18,9 @@ sentry-anyhow = { version = "0.48", features = ["backtrace"] } openrouter-rs = "0.12" dotenvy = "0.15" tower = "0.5" -tower-http = {version = "0.6", features = ["trace"] } +tower-http = { version = "0.6", features = ["trace"] } tracing = "0.1" -tracing-subscriber = { version = "0.3", features=["env-filter"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } axum = "0.8" anyhow = { version = "1.0", features = ["backtrace"] } thiserror = "2.0" @@ -30,3 +29,6 @@ hex = "0.4" bytes = "1.1" metrics = "0.24" metrics-exporter-prometheus = { version = "0.18", default-features = false, features = ["http-listener"] } + +[profile.release] +debug = 1 diff --git a/Containerfile b/Containerfile index 6508b0c..7150f27 100644 --- a/Containerfile +++ b/Containerfile @@ -1,12 +1,15 @@ -FROM rust:1.96 as builder +FROM rust:1.97-trixie as builder WORKDIR /app -COPY . . -RUN cargo build --release + +COPY Cargo.toml Cargo.lock ./ +COPY crates/ crates/ + +RUN cargo build --release --package herald-server FROM debian:trixie-slim WORKDIR /app -COPY --from=builder /app/target/release/herald . -CMD [ "./herald" ] +COPY --from=builder /app/target/release/herald-server . +CMD [ "./herald-server" ] diff --git a/crates/devcontainer-rs/Cargo.toml b/crates/devcontainer-rs/Cargo.toml new file mode 100644 index 0000000..1dd07fb --- /dev/null +++ b/crates/devcontainer-rs/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "devcontainer-rs" +version = "0.1.0" +edition = "2024" + +[dependencies] +tokio = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +tempfile = "3" diff --git a/crates/devcontainer-rs/src/lib.rs b/crates/devcontainer-rs/src/lib.rs new file mode 100644 index 0000000..44c8bc3 --- /dev/null +++ b/crates/devcontainer-rs/src/lib.rs @@ -0,0 +1,175 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct DevContainerBuildSchema { + #[serde(default)] + pub dockerfile: Option, + #[serde(default)] + pub args: HashMap, +} + +#[derive(Debug, Deserialize)] +pub struct DevContainerSchema { + #[serde(default)] + pub name: Option, + pub build: DevContainerBuildSchema, + + #[serde(rename = "workspaceFolder", default)] + pub workspace_folder: Option, + + #[serde(rename = "containerEnv", default)] + pub container_env: HashMap, + + #[serde(rename = "postCreateCommand", default)] + pub post_create_command: Option, + + #[serde(rename = "postStartCommand", default)] + pub post_start_command: Option, +} + +#[derive(Debug)] +pub struct DevContainer { + /// Absolute or relative path to the Dockerfile/Containerfile to build. + pub container_file_path: PathBuf, + pub name: Option, + pub build_args: HashMap, + pub container_env: HashMap, + pub workspace_folder: Option, + pub post_create_command: Option, + pub post_start_command: Option, +} + +#[derive(Debug, thiserror::Error)] +pub enum ParseError { + #[error("failed to read devcontainer file `{path}`: {source}")] + Read { + path: PathBuf, + source: std::io::Error, + }, + + #[error("invalid devcontainer JSON in `{path}`: {source}")] + Json { + path: PathBuf, + source: serde_json::Error, + }, + + #[error("container file `{0}` does not exist or is not a regular file")] + ContainerFileNotFound(PathBuf), + + #[error("the devcontainer file path has no parent directory: `{0}`")] + InvalidDevContainerPath(PathBuf), +} + +impl TryFrom<(DevContainerSchema, PathBuf)> for DevContainer { + type Error = ParseError; + + fn try_from((schema, devcontainer_path): (DevContainerSchema, PathBuf)) -> Result { + let base_dir = devcontainer_path + .parent() + .ok_or_else(|| ParseError::InvalidDevContainerPath(devcontainer_path.clone()))?; + + let container_file_path = match schema.build.dockerfile.as_deref() { + Some(file) => base_dir.join(file), + None => first_existing_container_file(base_dir), + }; + + if !container_file_path.is_file() { + return Err(ParseError::ContainerFileNotFound(container_file_path)); + } + + Ok(Self { + container_file_path, + name: schema.name, + build_args: schema.build.args, + container_env: schema.container_env, + workspace_folder: schema.workspace_folder, + post_create_command: schema.post_create_command, + post_start_command: schema.post_start_command, + }) + } +} + +fn first_existing_container_file(base_dir: &Path) -> PathBuf { + ["Dockerfile", "Containerfile"] + .iter() + .map(|filename| base_dir.join(filename)) + .find(|path| path.is_file()) + .unwrap_or_else(|| base_dir.join("Dockerfile")) +} + +pub async fn parse(path: impl AsRef) -> Result { + let path = path.as_ref().to_path_buf(); + let contents = tokio::fs::read_to_string(&path) + .await + .map_err(|source| ParseError::Read { + path: path.clone(), + source, + })?; + + let schema = serde_json::from_str::(&contents).map_err(|source| { + ParseError::Json { + path: path.clone(), + source, + } + })?; + + DevContainer::try_from((schema, path)) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + + #[test] + fn resolves_configured_containerfile_relative_to_devcontainer_file() { + let dir = tempfile::tempdir().unwrap(); + let devcontainer_path = dir.path().join("devcontainer.json"); + let containerfile_path = dir.path().join("Containerfile"); + fs::write(&containerfile_path, "FROM alpine\n").unwrap(); + + let schema = DevContainerSchema { + name: Some("test".into()), + build: DevContainerBuildSchema { + dockerfile: Some("Containerfile".into()), + args: HashMap::new(), + }, + workspace_folder: None, + container_env: HashMap::new(), + post_create_command: None, + post_start_command: None, + }; + + let config = DevContainer::try_from((schema, devcontainer_path)).unwrap(); + assert_eq!(config.container_file_path, containerfile_path); + } + + #[test] + fn falls_back_to_dockerfile_before_containerfile() { + let dir = tempfile::tempdir().unwrap(); + let devcontainer_path = dir.path().join("devcontainer.json"); + let dockerfile_path = dir.path().join("Dockerfile"); + fs::write(&dockerfile_path, "FROM alpine\n").unwrap(); + fs::write(dir.path().join("Containerfile"), "FROM busybox\n").unwrap(); + + let schema = DevContainerSchema { + name: None, + build: DevContainerBuildSchema { + dockerfile: None, + args: HashMap::new(), + }, + workspace_folder: None, + container_env: HashMap::new(), + post_create_command: None, + post_start_command: None, + }; + + let config = DevContainer::try_from((schema, devcontainer_path)).unwrap(); + assert_eq!(config.container_file_path, dockerfile_path); + } +} diff --git a/crates/herald-server/Cargo.toml b/crates/herald-server/Cargo.toml new file mode 100644 index 0000000..8e015d9 --- /dev/null +++ b/crates/herald-server/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "herald-server" +version = "1.2.0" +edition = "2024" + +[dependencies] +reqwest = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tokio-util = { workspace = true } +futures-util = { workspace = true } +serde_json = { workspace = true } +serde = { workspace = true } +sentry = { workspace = true } +sentry-anyhow = { workspace = true } +openrouter-rs = { workspace = true } +dotenvy = { workspace = true } +tower = { workspace = true } +tower-http = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +axum = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } +ring = { workspace = true } +hex = { workspace = true } +bytes = { workspace = true } +metrics = { workspace = true } +metrics-exporter-prometheus = { workspace = true } +devcontainer-rs = { path = "../devcontainer-rs" } \ No newline at end of file diff --git a/src/api.rs b/crates/herald-server/src/api.rs similarity index 100% rename from src/api.rs rename to crates/herald-server/src/api.rs diff --git a/src/bot.rs b/crates/herald-server/src/bot.rs similarity index 100% rename from src/bot.rs rename to crates/herald-server/src/bot.rs diff --git a/src/bot_actions/mod.rs b/crates/herald-server/src/bot_actions/mod.rs similarity index 100% rename from src/bot_actions/mod.rs rename to crates/herald-server/src/bot_actions/mod.rs diff --git a/src/bot_actions/review.rs b/crates/herald-server/src/bot_actions/review.rs similarity index 100% rename from src/bot_actions/review.rs rename to crates/herald-server/src/bot_actions/review.rs diff --git a/src/consts.rs b/crates/herald-server/src/consts.rs similarity index 100% rename from src/consts.rs rename to crates/herald-server/src/consts.rs diff --git a/src/env.rs b/crates/herald-server/src/env.rs similarity index 100% rename from src/env.rs rename to crates/herald-server/src/env.rs diff --git a/src/errors.rs b/crates/herald-server/src/errors.rs similarity index 100% rename from src/errors.rs rename to crates/herald-server/src/errors.rs diff --git a/src/gitea.rs b/crates/herald-server/src/gitea.rs similarity index 100% rename from src/gitea.rs rename to crates/herald-server/src/gitea.rs diff --git a/src/main.rs b/crates/herald-server/src/main.rs similarity index 100% rename from src/main.rs rename to crates/herald-server/src/main.rs diff --git a/src/metrics.rs b/crates/herald-server/src/metrics.rs similarity index 100% rename from src/metrics.rs rename to crates/herald-server/src/metrics.rs diff --git a/src/open_router.rs b/crates/herald-server/src/open_router.rs similarity index 100% rename from src/open_router.rs rename to crates/herald-server/src/open_router.rs diff --git a/src/state.rs b/crates/herald-server/src/state.rs similarity index 100% rename from src/state.rs rename to crates/herald-server/src/state.rs -- 2.54.0 From 5b9d870b464053ec3cdf6bccca25b2a01cd21e74 Mon Sep 17 00:00:00 2001 From: qpismont Date: Fri, 31 Jul 2026 20:18:29 +0000 Subject: [PATCH 2/7] Dockerfile field must be present --- crates/devcontainer-rs/src/lib.rs | 79 +++++++++++-------------------- 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/crates/devcontainer-rs/src/lib.rs b/crates/devcontainer-rs/src/lib.rs index 44c8bc3..1dd6d69 100644 --- a/crates/devcontainer-rs/src/lib.rs +++ b/crates/devcontainer-rs/src/lib.rs @@ -8,7 +8,7 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct DevContainerBuildSchema { #[serde(default)] - pub dockerfile: Option, + pub dockerfile: String, #[serde(default)] pub args: HashMap, } @@ -34,7 +34,6 @@ pub struct DevContainerSchema { #[derive(Debug)] pub struct DevContainer { - /// Absolute or relative path to the Dockerfile/Containerfile to build. pub container_file_path: PathBuf, pub name: Option, pub build_args: HashMap, @@ -73,10 +72,7 @@ impl TryFrom<(DevContainerSchema, PathBuf)> for DevContainer { .parent() .ok_or_else(|| ParseError::InvalidDevContainerPath(devcontainer_path.clone()))?; - let container_file_path = match schema.build.dockerfile.as_deref() { - Some(file) => base_dir.join(file), - None => first_existing_container_file(base_dir), - }; + let container_file_path = base_dir.join(schema.build.dockerfile); if !container_file_path.is_file() { return Err(ParseError::ContainerFileNotFound(container_file_path)); @@ -94,14 +90,6 @@ impl TryFrom<(DevContainerSchema, PathBuf)> for DevContainer { } } -fn first_existing_container_file(base_dir: &Path) -> PathBuf { - ["Dockerfile", "Containerfile"] - .iter() - .map(|filename| base_dir.join(filename)) - .find(|path| path.is_file()) - .unwrap_or_else(|| base_dir.join("Dockerfile")) -} - pub async fn parse(path: impl AsRef) -> Result { let path = path.as_ref().to_path_buf(); let contents = tokio::fs::read_to_string(&path) @@ -126,50 +114,37 @@ mod tests { use super::*; use std::fs; - #[test] - fn resolves_configured_containerfile_relative_to_devcontainer_file() { - let dir = tempfile::tempdir().unwrap(); - let devcontainer_path = dir.path().join("devcontainer.json"); - let containerfile_path = dir.path().join("Containerfile"); - fs::write(&containerfile_path, "FROM alpine\n").unwrap(); - - let schema = DevContainerSchema { - name: Some("test".into()), - build: DevContainerBuildSchema { - dockerfile: Some("Containerfile".into()), - args: HashMap::new(), - }, - workspace_folder: None, - container_env: HashMap::new(), - post_create_command: None, - post_start_command: None, - }; - - let config = DevContainer::try_from((schema, devcontainer_path)).unwrap(); - assert_eq!(config.container_file_path, containerfile_path); - } - - #[test] - fn falls_back_to_dockerfile_before_containerfile() { + #[tokio::test] + async fn parses_devcontainer_file() { let dir = tempfile::tempdir().unwrap(); let devcontainer_path = dir.path().join("devcontainer.json"); let dockerfile_path = dir.path().join("Dockerfile"); + fs::write(&dockerfile_path, "FROM alpine\n").unwrap(); - fs::write(dir.path().join("Containerfile"), "FROM busybox\n").unwrap(); + fs::write( + &devcontainer_path, + r#"{ + "name": "test", + "build": { + "dockerfile": "Dockerfile", + "args": { + "VERSION": "1" + } + }, + "workspaceFolder": "/workspace", + "containerEnv": { + "RUST_LOG": "debug" + } + }"#, + ) + .unwrap(); - let schema = DevContainerSchema { - name: None, - build: DevContainerBuildSchema { - dockerfile: None, - args: HashMap::new(), - }, - workspace_folder: None, - container_env: HashMap::new(), - post_create_command: None, - post_start_command: None, - }; + let config = parse(&devcontainer_path).await.unwrap(); - let config = DevContainer::try_from((schema, devcontainer_path)).unwrap(); + assert_eq!(config.name.as_deref(), Some("test")); assert_eq!(config.container_file_path, dockerfile_path); + assert_eq!(config.build_args.get("VERSION").unwrap(), "1"); + assert_eq!(config.container_env.get("RUST_LOG").unwrap(), "debug"); + assert_eq!(config.workspace_folder.as_deref(), Some("/workspace")); } } -- 2.54.0 From b3a0cb63e9095a7de643b153bd38d01cbc931184 Mon Sep 17 00:00:00 2001 From: qpismont Date: Fri, 31 Jul 2026 20:21:05 +0000 Subject: [PATCH 3/7] Update woodpecker rust job (1.96 => 1.97) --- .woodpecker/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker/tests.yml b/.woodpecker/tests.yml index b151ff1..acb5c7b 100644 --- a/.woodpecker/tests.yml +++ b/.woodpecker/tests.yml @@ -4,12 +4,12 @@ when: steps: - name: clippy - image: rust:1.96 + image: rust:1.97 commands: - rustup component add clippy - cargo clippy - name: test - image: rust:1.96 + image: rust:1.97 commands: - cargo test -- 2.54.0 From 6a21c7d6c3be5cc04cdb976b545422568383edd3 Mon Sep 17 00:00:00 2001 From: qpismont Date: Fri, 31 Jul 2026 20:25:37 +0000 Subject: [PATCH 4/7] Renforce woodpecker tests --- .woodpecker/tests.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.woodpecker/tests.yml b/.woodpecker/tests.yml index acb5c7b..7d128af 100644 --- a/.woodpecker/tests.yml +++ b/.woodpecker/tests.yml @@ -3,13 +3,27 @@ when: - push steps: + - name: fmt + image: rust:1.97 + commands: + - rustup component add rustfmt + - cargo fmt --all -- --check + - name: clippy image: rust:1.97 commands: - rustup component add clippy - - cargo clippy + - cargo clippy --workspace --all-targets --all-features -- -D warnings - name: test image: rust:1.97 commands: - - cargo test + - cargo test --workspace --all-targets + + - name: container-build + image: quay.io/buildah/stable + privileged: true + volumes: + - /data/woodpecker-builds:/data + commands: + - buildah bud -f Containerfile -t herald-ci . -- 2.54.0 From f0e64e0c1d2f83e31df089b8cbae8a35a5935dc3 Mon Sep 17 00:00:00 2001 From: qpismont Date: Fri, 31 Jul 2026 20:32:59 +0000 Subject: [PATCH 5/7] Fix fmt --- .devcontainer/Dockerfile | 3 +- .zed/settings.json | 6 ++++ crates/devcontainer-rs/src/lib.rs | 4 ++- crates/herald-server/src/bot.rs | 4 ++- crates/herald-server/src/main.rs | 7 +++- crates/herald-server/src/metrics.rs | 50 ++++++++++++++++++++++------- 6 files changed, 58 insertions(+), 16 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index dd02efa..ba2efc9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -19,7 +19,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN groupadd --gid ${USER_GID:-1000} $USERNAME \ && useradd --uid ${USER_UID:-1000} --gid ${USER_GID:-1000} -m $USERNAME \ - && rustup component add clippy + && rustup component add clippy \ + && rustup component add rustfmt diff --git a/.zed/settings.json b/.zed/settings.json index d569920..afccddd 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -1,4 +1,10 @@ { + "languages": { + "Rust": { + "format_on_save": "on", + "formatter": "language_server" + } + }, "lsp": { "rust-analyzer": { "initialization_options": { diff --git a/crates/devcontainer-rs/src/lib.rs b/crates/devcontainer-rs/src/lib.rs index 1dd6d69..81960de 100644 --- a/crates/devcontainer-rs/src/lib.rs +++ b/crates/devcontainer-rs/src/lib.rs @@ -67,7 +67,9 @@ pub enum ParseError { impl TryFrom<(DevContainerSchema, PathBuf)> for DevContainer { type Error = ParseError; - fn try_from((schema, devcontainer_path): (DevContainerSchema, PathBuf)) -> Result { + fn try_from( + (schema, devcontainer_path): (DevContainerSchema, PathBuf), + ) -> Result { let base_dir = devcontainer_path .parent() .ok_or_else(|| ParseError::InvalidDevContainerPath(devcontainer_path.clone()))?; diff --git a/crates/herald-server/src/bot.rs b/crates/herald-server/src/bot.rs index 470990f..12ba2d7 100644 --- a/crates/herald-server/src/bot.rs +++ b/crates/herald-server/src/bot.rs @@ -1,5 +1,7 @@ use crate::{ - gitea::{GiteaAPI, WebhookType}, metrics, open_router::OpenRouterClient, + gitea::{GiteaAPI, WebhookType}, + metrics, + open_router::OpenRouterClient, }; use serde::Deserialize; use std::{collections::HashSet, sync::Arc}; diff --git a/crates/herald-server/src/main.rs b/crates/herald-server/src/main.rs index 27d0554..3a5ab03 100644 --- a/crates/herald-server/src/main.rs +++ b/crates/herald-server/src/main.rs @@ -1,4 +1,9 @@ -use crate::{bot::Bot, gitea::{GiteaAPI, WebhookType}, open_router::OpenRouterClient, state::AppState}; +use crate::{ + bot::Bot, + gitea::{GiteaAPI, WebhookType}, + open_router::OpenRouterClient, + state::AppState, +}; use dotenvy::dotenv; use tokio::signal::unix::{SignalKind, signal}; diff --git a/crates/herald-server/src/metrics.rs b/crates/herald-server/src/metrics.rs index 23698d8..44d3347 100644 --- a/crates/herald-server/src/metrics.rs +++ b/crates/herald-server/src/metrics.rs @@ -1,10 +1,9 @@ use std::{net::SocketAddr, str::FromStr}; -use metrics::{Unit, describe_counter, describe_gauge, counter, gauge}; +use metrics::{Unit, counter, describe_counter, describe_gauge, gauge}; pub fn webhook_received(event_type: &str) { - counter!("herald_webhooks_received_total", "event_type" => event_type.to_string()) - .increment(1); + counter!("herald_webhooks_received_total", "event_type" => event_type.to_string()).increment(1); } pub fn webhook_duplicate(event_type: &str) { @@ -31,8 +30,7 @@ pub fn task_completed(event_type: &str) { } pub fn task_failed(event_type: &str) { - counter!("herald_bot_tasks_failed_total", "event_type" => event_type.to_string()) - .increment(1); + counter!("herald_bot_tasks_failed_total", "event_type" => event_type.to_string()).increment(1); } pub fn openrouter_cost_usd(cost: f64) { @@ -40,13 +38,41 @@ pub fn openrouter_cost_usd(cost: f64) { } pub fn describe() { - describe_counter!("herald_webhooks_received_total", Unit::Count, "Total webhooks received"); - describe_counter!("herald_webhooks_duplicate_total", Unit::Count, "Webhooks rejected as duplicates"); - describe_counter!("herald_webhooks_channel_full_total", Unit::Count, "Webhooks dropped because the bot channel was full"); - describe_gauge!("herald_bot_tasks_active", Unit::Count, "Bot tasks currently in progress"); - describe_counter!("herald_bot_tasks_completed_total", Unit::Count, "Bot tasks completed successfully"); - describe_counter!("herald_bot_tasks_failed_total", Unit::Count, "Bot tasks that failed"); - describe_counter!("herald_openrouter_cost_cents_total", Unit::Count, "Total OpenRouter cost in cents (divide by 100 for USD)"); + describe_counter!( + "herald_webhooks_received_total", + Unit::Count, + "Total webhooks received" + ); + describe_counter!( + "herald_webhooks_duplicate_total", + Unit::Count, + "Webhooks rejected as duplicates" + ); + describe_counter!( + "herald_webhooks_channel_full_total", + Unit::Count, + "Webhooks dropped because the bot channel was full" + ); + describe_gauge!( + "herald_bot_tasks_active", + Unit::Count, + "Bot tasks currently in progress" + ); + describe_counter!( + "herald_bot_tasks_completed_total", + Unit::Count, + "Bot tasks completed successfully" + ); + describe_counter!( + "herald_bot_tasks_failed_total", + Unit::Count, + "Bot tasks that failed" + ); + describe_counter!( + "herald_openrouter_cost_cents_total", + Unit::Count, + "Total OpenRouter cost in cents (divide by 100 for USD)" + ); } pub fn install(bind_addr: &str) -> anyhow::Result<()> { -- 2.54.0 From 8c53bc0e200a2c6f743180973304b2cb4dd13e76 Mon Sep 17 00:00:00 2001 From: qpismont Date: Fri, 31 Jul 2026 20:33:47 +0000 Subject: [PATCH 6/7] re fix fmt lol --- crates/herald-server/src/bot_actions/review.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/herald-server/src/bot_actions/review.rs b/crates/herald-server/src/bot_actions/review.rs index c156aa2..8ca234a 100644 --- a/crates/herald-server/src/bot_actions/review.rs +++ b/crates/herald-server/src/bot_actions/review.rs @@ -4,7 +4,11 @@ use tokio_util::io::StreamReader; use tracing::instrument; use crate::{ - bot::ReviewResult, consts::{BOT_PROCESS_MSG, MAX_DIFF_SIZE, REVIEW_PROMPT}, gitea::{GiteaAPI, ReviewPayload}, metrics, open_router::OpenRouterClient, + bot::ReviewResult, + consts::{BOT_PROCESS_MSG, MAX_DIFF_SIZE, REVIEW_PROMPT}, + gitea::{GiteaAPI, ReviewPayload}, + metrics, + open_router::OpenRouterClient, }; #[instrument(skip(gitea_api, open_router_client, http_client, review_payload))] -- 2.54.0 From a29051b0e458ed50269c92b74e587d9e6e97dbe0 Mon Sep 17 00:00:00 2001 From: qpismont Date: Fri, 31 Jul 2026 20:39:50 +0000 Subject: [PATCH 7/7] Fix clippy errors --- .zed/settings.json | 7 ++++++- crates/herald-server/src/bot.rs | 1 - crates/herald-server/src/gitea.rs | 7 ------- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.zed/settings.json b/.zed/settings.json index afccddd..c873cae 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -9,7 +9,12 @@ "rust-analyzer": { "initialization_options": { "check": { - "command": "clippy" + "command": "clippy", + "extraArgs": [ + "--", + "-D", + "warnings" + ] } } } diff --git a/crates/herald-server/src/bot.rs b/crates/herald-server/src/bot.rs index 12ba2d7..0a4482c 100644 --- a/crates/herald-server/src/bot.rs +++ b/crates/herald-server/src/bot.rs @@ -20,7 +20,6 @@ pub struct ReviewResult { pub struct ReviewItem { pub filename: String, pub line: Option, - pub code: String, pub message: String, } diff --git a/crates/herald-server/src/gitea.rs b/crates/herald-server/src/gitea.rs index 8477b4b..b6b48fb 100644 --- a/crates/herald-server/src/gitea.rs +++ b/crates/herald-server/src/gitea.rs @@ -197,7 +197,6 @@ pub struct ReviewPayload { #[derive(Deserialize, Debug)] pub struct PullRequest { - pub id: u64, pub diff_url: String, pub number: u64, pub title: String, @@ -207,12 +206,10 @@ pub struct PullRequest { pub struct Comment { pub id: u64, pub body: String, - pub user: User, } #[derive(Deserialize, Debug)] pub struct User { - pub id: u64, pub login: String, } @@ -282,10 +279,8 @@ mod tests { match result.unwrap() { WebhookType::Review(payload) => { assert_eq!(payload.action, "created"); - assert_eq!(payload.pull_request.id, 42); assert_eq!(payload.comment.id, 7); assert_eq!(payload.comment.body, "@test_bot LGTM"); - assert_eq!(payload.comment.user.id, 100); } } } @@ -375,10 +370,8 @@ mod tests { let payload: ReviewPayload = serde_json::from_value(json).unwrap(); assert_eq!(payload.action, "created"); - assert_eq!(payload.pull_request.id, 99); assert_eq!(payload.comment.id, 12); assert_eq!(payload.comment.body, "Needs work"); - assert_eq!(payload.comment.user.id, 200); } #[test] -- 2.54.0