From 39c2afa0a73dfc538ebc0355044168e9942a0209 Mon Sep 17 00:00:00 2001 From: qpismont Date: Tue, 9 Jun 2026 20:58:38 +0000 Subject: [PATCH 1/6] Starting impl Sentry and tracing --- .devcontainer/Dockerfile | 1 + Cargo.lock | 1366 ++++++++++++++++++++++++++++++++++++- Cargo.toml | 6 + src/api.rs | 10 +- src/bot.rs | 48 +- src/bot_actions/review.rs | 35 +- src/env.rs | 5 +- src/errors.rs | 11 +- src/gitea.rs | 23 +- src/main.rs | 35 +- 10 files changed, 1456 insertions(+), 84 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6a69d54..1b89658 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -11,6 +11,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ git \ build-essential \ + libssl-dev \ cmake \ pkg-config \ clang \ diff --git a/Cargo.lock b/Cargo.lock index da87dd3..402dbf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,187 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93acb4a42f64936f9b8cae4a433b237599dd6eb6ed06124eb67132ef8cc90662" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "bitflags", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "foldhash", + "futures-core", + "http 0.2.12", + "httparse", + "httpdate", + "itoa", + "language-tags", + "mime", + "percent-encoding", + "pin-project-lite", + "smallvec", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-router" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7" +dependencies = [ + "bytestring", + "cfg-if", + "http 0.2.12", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.10", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf" +dependencies = [ + "actix-codec", + "actix-http", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "bytes", + "bytestring", + "cfg-if", + "derive_more", + "encoding_rs", + "foldhash", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.6.3", + "time", + "tracing", + "url", +] + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.102" @@ -24,7 +205,7 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http", + "http 1.4.0", "http-body", "http-body-util", "hyper", @@ -55,7 +236,7 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", - "http", + "http 1.4.0", "http-body", "http-body-util", "mime", @@ -66,12 +247,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bitflags" version = "2.11.1" @@ -87,6 +289,15 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -99,6 +310,15 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "bytestring" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86566c496f2f47d9b8147a4c8b02ffdb69c919fe0c2b2e7195d22cbba0e635c9" +dependencies = [ + "bytes", +] + [[package]] name = "cc" version = "1.2.62" @@ -133,6 +353,31 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.3.0" @@ -195,6 +440,35 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + +[[package]] +name = "der" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +dependencies = [ + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_builder" version = "0.20.2" @@ -226,6 +500,29 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", + "unicode-xid", +] + [[package]] name = "digest" version = "0.11.3" @@ -238,6 +535,16 @@ dependencies = [ "ctutils", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -273,6 +580,21 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -283,18 +605,57 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -311,6 +672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -391,6 +753,37 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "herald" version = "0.1.0" @@ -403,7 +796,9 @@ dependencies = [ "hex", "hmac", "openrouter-rs", - "reqwest", + "reqwest 0.12.28", + "sentry", + "sentry-anyhow", "serde", "serde_json", "sha2", @@ -412,6 +807,10 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", ] [[package]] @@ -429,6 +828,28 @@ dependencies = [ "digest", ] +[[package]] +name = "hostname" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" +dependencies = [ + "cfg-if", + "libc", + "windows-link", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.4.0" @@ -446,7 +867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.0", ] [[package]] @@ -457,7 +878,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", + "http 1.4.0", "http-body", "pin-project-lite", ] @@ -493,7 +914,8 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "http", + "h2", + "http 1.4.0", "http-body", "httparse", "httpdate", @@ -510,7 +932,7 @@ version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http", + "http 1.4.0", "hyper", "hyper-util", "rustls", @@ -520,6 +942,22 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -530,14 +968,14 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", + "http 1.4.0", "http-body", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -652,6 +1090,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -676,18 +1130,42 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "litemap" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + [[package]] name = "lock_api" version = "0.4.14" @@ -709,6 +1187,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "matchit" version = "0.8.4" @@ -727,6 +1214,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "1.2.0" @@ -734,10 +1230,223 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -753,8 +1462,8 @@ dependencies = [ "derive_builder", "dotenvy_macro", "futures-util", - "http", - "reqwest", + "http 1.4.0", + "reqwest 0.12.28", "schemars", "serde", "serde_json", @@ -764,6 +1473,65 @@ dependencies = [ "urlencoding", ] +[[package]] +name = "openssl" +version = "0.10.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -787,18 +1555,53 @@ dependencies = [ "windows-link", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + [[package]] name = "potential_utf" version = "0.1.5" @@ -808,6 +1611,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -839,7 +1648,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.6.3", "thiserror 2.0.18", "tokio", "tracing", @@ -876,7 +1685,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] @@ -954,6 +1763,41 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + [[package]] name = "reqwest" version = "0.12.28" @@ -964,7 +1808,7 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http", + "http 1.4.0", "http-body", "http-body-util", "hyper", @@ -995,6 +1839,45 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" @@ -1009,12 +1892,40 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + [[package]] name = "rustc-hash" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + [[package]] name = "rustls" version = "0.23.40" @@ -1062,6 +1973,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "schemars" version = "1.2.1" @@ -1093,6 +2013,184 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "sentry" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931a20b0da02350676e3d6d3c9028d58eaa448cf42a866712eec5845a505421e" +dependencies = [ + "cfg_aliases", + "httpdate", + "native-tls", + "reqwest 0.13.4", + "sentry-actix", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tower", + "sentry-tracing", + "tokio", + "ureq", +] + +[[package]] +name = "sentry-actix" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffb8fd78b8f4527146013ab52293e03242770a31dcd97eee4b82b7f770ffab3" +dependencies = [ + "actix-http", + "actix-web", + "bytes", + "futures-util", + "sentry-core", +] + +[[package]] +name = "sentry-anyhow" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8bb4a03e7cf5562ea7ff658b2a254c06d2e09b2ec7383dfd5fc7025d40227b" +dependencies = [ + "anyhow", + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-backtrace" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911ee36abf5b7fa335fccd5f54361ba9c16baea5f0c3bb361a687b6c195c21cf" +dependencies = [ + "backtrace", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9d7d469e9e22741c17ca23fb8b42d79861590eb7cf330f3da34fc1e4bc1bc6" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545dc562b6758d646ac19e1407f4ebc26d452111386743e03323464bc48bb2e0" +dependencies = [ + "rand", + "sentry-types", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "sentry-debug-images" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "660e9def38a573a869a182f7e90f58aaaa460f38b92b31fd1755ec537193bb48" +dependencies = [ + "findshlibs", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772d9de150c8ca910c835353c85f434457348fdd21208f9b3da3574202b1dc5d" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tower" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2abea154597936d5df2d39fbe8aac16d584de6b3572c70c39558764d9d2efe15" +dependencies = [ + "axum", + "http 1.4.0", + "pin-project", + "sentry-core", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "sentry-tracing" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51ec9620a4d398dcdf7ee90effbf8d8691cfa24e91978bfa8565cac039d4980" +dependencies = [ + "bitflags", + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "041359745a44dd2e14fe21b7510fe7ca8b5beffce6636a0b52e5bc7d5f736887" +dependencies = [ + "debugid", + "hex", + "rand", + "serde", + "serde_json", + "thiserror 2.0.18", + "time", + "url", + "uuid", +] + [[package]] name = "serde" version = "1.0.228" @@ -1181,6 +2279,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1209,6 +2316,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.3" @@ -1279,6 +2396,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1319,6 +2449,46 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinystr" version = "0.8.3" @@ -1356,7 +2526,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] @@ -1372,6 +2542,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -1431,12 +2611,13 @@ dependencies = [ "bitflags", "bytes", "futures-util", - "http", + "http 1.4.0", "http-body", "pin-project-lite", "tower", "tower-layer", "tower-service", + "tracing", "url", ] @@ -1460,9 +2641,21 @@ checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tracing-core" version = "0.1.36" @@ -1470,6 +2663,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -1484,18 +2707,68 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +dependencies = [ + "base64", + "der", + "log", + "native-tls", + "percent-encoding", + "rustls-pki-types", + "ureq-proto", + "utf8-zero", + "webpki-root-certs", +] + +[[package]] +name = "ureq-proto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +dependencies = [ + "base64", + "http 1.4.0", + "httparse", + "log", +] + [[package]] name = "url" version = "2.5.8" @@ -1506,6 +2779,7 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -1514,12 +2788,41 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "uuid" +version = "1.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +dependencies = [ + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "want" version = "0.3.1" @@ -1632,6 +2935,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "1.0.7" @@ -1641,6 +2953,28 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index d036f9a..f5025f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,14 @@ tokio-util = "0.7" futures-util = "0.3" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } +sentry = { version = "0.48", features = ["tower-axum-matched-path"] } +sentry-anyhow = "0.48" openrouter-rs = "0.10" dotenvy = "0.15" +tower = "0.5" +tower-http = {version = "0.6", features = ["trace"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features=["env-filter"] } axum = "0.8" anyhow = "1.0" thiserror = "2.0" diff --git a/src/api.rs b/src/api.rs index e98f736..9e307ed 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,13 +1,17 @@ -use axum::body::{Bytes, to_bytes}; +use axum::body::{Body, Bytes, to_bytes}; use axum::extract::{FromRef, FromRequest, State}; +use axum::http::Request; use axum::response::IntoResponse; use axum::routing::{get, post}; use axum::{Json, Router}; use hmac::{Hmac, KeyInit, Mac}; use reqwest::StatusCode; +use sentry::integrations::tower::NewSentryLayer; use serde_json::Value; use sha2::Sha256; use subtle::ConstantTimeEq; +use tower_http::trace::TraceLayer; +use tracing::info; use crate::consts::{GITEA_EVENT_TYPE_HEADER_NAME, GITEA_SIG_HEADER_NAME, MAX_WEBHOOK_BODY_SIZE}; use crate::errors::AppError; @@ -18,11 +22,15 @@ pub async fn start(app_state: AppState) -> anyhow::Result<()> { let http_port = app_state.config.http_port; let app = Router::new() + .layer(NewSentryLayer::>::new_from_top()) + .layer(TraceLayer::new_for_http()) .route("/", get(root)) .route("/webhook", post(webhook)) .with_state(app_state); let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", http_port)).await?; + info!("Listening API on port {}", http_port); + axum::serve(listener, app) .await .map_err(anyhow::Error::from) diff --git a/src/bot.rs b/src/bot.rs index bd9b62c..009d90e 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -1,5 +1,6 @@ use serde::Deserialize; use std::time::Duration; +use tracing::{error, info}; use crate::{ env::EnvConfig, @@ -22,44 +23,6 @@ pub struct ReviewItem { pub message: String, } -/// Map a filename to a markdown language identifier for syntax highlighting. -fn lang_from_filename(filename: &str) -> &str { - match std::path::Path::new(filename) - .extension() - .and_then(|e| e.to_str()) - .unwrap_or("") - { - "rs" => "rust", - "py" => "python", - "js" | "mjs" => "javascript", - "ts" => "typescript", - "jsx" => "jsx", - "tsx" => "tsx", - "go" => "go", - "java" => "java", - "kt" | "kts" => "kotlin", - "scala" => "scala", - "c" | "h" => "c", - "cpp" | "cc" | "cxx" | "hpp" | "hxx" => "cpp", - "rb" => "ruby", - "php" => "php", - "swift" => "swift", - "sh" | "bash" | "zsh" => "bash", - "sql" => "sql", - "html" | "htm" => "html", - "css" => "css", - "scss" | "sass" => "scss", - "json" => "json", - "yaml" | "yml" => "yaml", - "xml" => "xml", - "toml" => "toml", - "md" | "mdx" => "markdown", - "dockerfile" | "Dockerfile" => "dockerfile", - "Makefile" => "makefile", - _ => "", - } -} - pub struct Bot { config: EnvConfig, gitea_api: GiteaAPI, @@ -90,6 +53,8 @@ impl Bot { &self, mut rx: tokio::sync::mpsc::Receiver, ) -> anyhow::Result<()> { + info!("Bot started"); + while let Some(wb) = rx.recv().await { self.exec(wb).await; } @@ -110,8 +75,11 @@ impl Bot { .await; match exec_result { - Ok(_) => println!("Task completed"), - Err(err) => println!("{}", err), + Ok(_) => info!("Task completed"), + Err(err) => { + error!("{}", err); + sentry_anyhow::capture_anyhow(&err); + } } } } diff --git a/src/bot_actions/review.rs b/src/bot_actions/review.rs index 8ffc3b1..8882c32 100644 --- a/src/bot_actions/review.rs +++ b/src/bot_actions/review.rs @@ -16,7 +16,7 @@ pub async fn exec_review( http_client: &reqwest::Client, model: &str, review_payload: ReviewPayload, -) -> Result<(), AppError> { +) -> anyhow::Result<()> { let new_comment = gitea_api .comment( &BOT_PROCESS_MSG.replace("{model}", model), @@ -41,9 +41,12 @@ pub async fn exec_review( review_result.cost = chat_result.cost; + let final_review_markdown = review_result_to_markdown(&review_result); + gitea_api .post_pull_request_review( &review_result, + &final_review_markdown, &review_payload.repository.full_name, review_payload.pull_request.number, ) @@ -53,20 +56,22 @@ pub async fn exec_review( } .await; - let edit_msg = match bot_result { - Ok(bot_result) => review_result_to_markdown(&bot_result), - Err(e) => format!("Error while reviewing: {}", e), - }; - - gitea_api - .edit_comment( - &edit_msg, - &review_payload.repository.full_name, - new_comment.id, - ) - .await?; - - Ok(()) + match bot_result { + Ok(_) => { + gitea_api + .delete_comment(&review_payload.repository.full_name, new_comment.id) + .await + } + Err(e) => { + gitea_api + .edit_comment( + &format!("Error while reviewing: {}", e), + &review_payload.repository.full_name, + new_comment.id, + ) + .await + } + } } fn review_result_to_markdown(review_result: &ReviewResult) -> String { diff --git a/src/env.rs b/src/env.rs index 6d013be..79ce619 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,5 +1,4 @@ use anyhow::anyhow; -use dotenvy::dotenv; #[derive(Clone)] pub struct EnvConfig { @@ -15,8 +14,6 @@ pub struct EnvConfig { } pub fn load_config() -> anyhow::Result { - dotenv().ok(); - let http_port = try_get_env("HTTP_PORT")?.parse()?; let bot_name = try_get_env("BOT_NAME")?; let webhook_secret = try_get_env("WEBHOOK_SIG_HEADER_SECRET")?; @@ -40,7 +37,7 @@ pub fn load_config() -> anyhow::Result { }) } -fn try_get_env(key: &str) -> anyhow::Result { +pub fn try_get_env(key: &str) -> anyhow::Result { let env = std::env::var(key)?; if env.trim().is_empty() { diff --git a/src/errors.rs b/src/errors.rs index 95b70ae..ade1b35 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -54,10 +54,13 @@ impl IntoResponse for AppError { StatusCode::UNAUTHORIZED, "WebHook sig header is invalid".to_string(), ), - AppError::Other(_) => ( - StatusCode::INTERNAL_SERVER_ERROR, - "Internal server error".to_string(), - ), + AppError::Other(err) => { + sentry_anyhow::capture_anyhow(&err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "Internal server error".to_string(), + ) + } } .into_response() } diff --git a/src/gitea.rs b/src/gitea.rs index 5a725ea..f70c619 100644 --- a/src/gitea.rs +++ b/src/gitea.rs @@ -84,9 +84,28 @@ impl GiteaAPI { Ok(()) } + pub async fn delete_comment(&self, full_name: &str, comment_id: u64) -> anyhow::Result<()> { + let url = format!( + "{}/api/v1/repos/{}/issues/comments/{}", + self.base_url, full_name, comment_id + ); + + let res = self.client.delete(url).send().await?; + + if !res.status().is_success() { + return Err(anyhow::anyhow!( + "Failed to delete comment: {}", + res.status() + )); + } + + Ok(()) + } + pub async fn post_pull_request_review( &self, review_result: &ReviewResult, + final_comment: &str, full_name: &str, index: u64, ) -> anyhow::Result<()> { @@ -95,7 +114,7 @@ impl GiteaAPI { self.base_url, full_name, index ); - let comments = &&review_result + let comments = &review_result .reviews .iter() .filter(|r| r.line.is_some()) @@ -117,7 +136,7 @@ impl GiteaAPI { .post(url) .json(&json!({ "event": "COMMENT", - "body": review_result.comment, + "body": final_comment, "comments": comments })) .send() diff --git a/src/main.rs b/src/main.rs index f1a8ad1..68b2ba6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,7 @@ use crate::{bot::Bot, gitea::WebhookType, state::AppState}; +use dotenvy::dotenv; +use tracing::{info, warn}; +use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt}; mod api; mod bot; @@ -10,8 +13,36 @@ mod gitea; mod open_router; mod state; -#[tokio::main] -async fn main() -> anyhow::Result<()> { +fn main() -> anyhow::Result<()> { + dotenv().ok(); + + tracing_subscriber::registry() + .with(fmt::layer()) + .with( + EnvFilter::try_from_default_env() // lit RUST_LOG depuis l'env + .unwrap_or_else(|_| EnvFilter::new("info")), + ) + .init(); + + if let Ok(sentry_dsn) = env::try_get_env("SENTRY_DSN") { + info!("Initialize sentry"); + + let _guard = sentry::init(( + sentry_dsn, + sentry::ClientOptions { + release: sentry::release_name!(), + send_default_pii: true, + ..Default::default() + }, + )); + } else { + warn!("SENTRY_DSN not set, sentry will not be initialized"); + } + + tokio::runtime::Runtime::new()?.block_on(run()) +} + +async fn run() -> anyhow::Result<()> { let config = env::load_config()?; let bot = Bot::new(config.clone())?; From efb35d5a8a6466be42994ff09057458005b41c49 Mon Sep 17 00:00:00 2001 From: qpismont Date: Tue, 9 Jun 2026 21:17:03 +0000 Subject: [PATCH 2/6] continue tracing impl --- src/api.rs | 6 +++++- src/bot.rs | 12 +++++++----- src/bot_actions/review.rs | 10 +++++++++- src/errors.rs | 3 ++- src/gitea.rs | 10 ++++++---- src/main.rs | 9 +++++++++ src/open_router.rs | 2 ++ 7 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/api.rs b/src/api.rs index 9e307ed..3a2eb42 100644 --- a/src/api.rs +++ b/src/api.rs @@ -11,7 +11,7 @@ use serde_json::Value; use sha2::Sha256; use subtle::ConstantTimeEq; use tower_http::trace::TraceLayer; -use tracing::info; +use tracing::{info, instrument}; use crate::consts::{GITEA_EVENT_TYPE_HEADER_NAME, GITEA_SIG_HEADER_NAME, MAX_WEBHOOK_BODY_SIZE}; use crate::errors::AppError; @@ -40,10 +40,13 @@ async fn root() -> &'static str { "Hi, i'm Herald :)" } +#[instrument(skip(app_state), fields(webhook_type), err)] async fn webhook( State(app_state): State, WebhookExtract(wb): WebhookExtract, ) -> Result { + tracing::Span::current().record("webhook_type", tracing::field::debug(&wb)); + app_state .bot_tx .send(wb) @@ -62,6 +65,7 @@ where { type Rejection = AppError; + #[instrument(skip(req, state), err)] async fn from_request(req: axum::extract::Request, state: &S) -> Result { let app_state = AppState::from_ref(state); let headers = req.headers(); diff --git a/src/bot.rs b/src/bot.rs index 009d90e..c5d4a7e 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -1,12 +1,11 @@ -use serde::Deserialize; -use std::time::Duration; -use tracing::{error, info}; - use crate::{ env::EnvConfig, gitea::{GiteaAPI, WebhookType}, open_router::OpenRouterClient, }; +use serde::Deserialize; +use std::time::Duration; +use tracing::{error, info, instrument}; #[derive(Deserialize, Debug)] pub struct ReviewResult { @@ -59,10 +58,13 @@ impl Bot { self.exec(wb).await; } + info!("Bot shutting down, channel closed"); Ok(()) } + #[instrument(skip(self))] pub async fn exec(&self, webhook: WebhookType) { + tracing::Span::current().record("webhook_type", tracing::field::debug(&webhook)); let exec_result = match webhook { WebhookType::Review(review_payload) => crate::bot_actions::review::exec_review( &self.gitea_api, @@ -77,7 +79,7 @@ impl Bot { match exec_result { Ok(_) => info!("Task completed"), Err(err) => { - error!("{}", err); + error!(%err, "Task error"); sentry_anyhow::capture_anyhow(&err); } } diff --git a/src/bot_actions/review.rs b/src/bot_actions/review.rs index 8882c32..83df3fa 100644 --- a/src/bot_actions/review.rs +++ b/src/bot_actions/review.rs @@ -1,15 +1,16 @@ use futures_util::stream::TryStreamExt; use tokio::io::AsyncReadExt; use tokio_util::io::StreamReader; +use tracing::instrument; use crate::{ bot::ReviewResult, consts::{BOT_PROCESS_MSG, MAX_DIFF_SIZE, REVIEW_PROMPT}, - errors::AppError, gitea::{GiteaAPI, ReviewPayload}, open_router::OpenRouterClient, }; +#[instrument(skip(gitea_api, open_router_client, http_client, review_payload), err)] pub async fn exec_review( gitea_api: &GiteaAPI, open_router_client: &OpenRouterClient, @@ -17,6 +18,13 @@ pub async fn exec_review( model: &str, review_payload: ReviewPayload, ) -> anyhow::Result<()> { + tracing::info!( + repo = %review_payload.repository.full_name, + pr = review_payload.pull_request.number, + action = %review_payload.action, + "Starting review" + ); + let new_comment = gitea_api .comment( &BOT_PROCESS_MSG.replace("{model}", model), diff --git a/src/errors.rs b/src/errors.rs index ade1b35..d4425b5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,6 @@ use axum::response::IntoResponse; use reqwest::StatusCode; +use tracing::error; #[derive(thiserror::Error, Debug)] pub enum AppError { @@ -55,7 +56,7 @@ impl IntoResponse for AppError { "WebHook sig header is invalid".to_string(), ), AppError::Other(err) => { - sentry_anyhow::capture_anyhow(&err); + error!(%err, "Internal server error"); ( StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string(), diff --git a/src/gitea.rs b/src/gitea.rs index f70c619..e137e0a 100644 --- a/src/gitea.rs +++ b/src/gitea.rs @@ -2,11 +2,9 @@ use std::time::Duration; use serde::Deserialize; use serde_json::{Value, json}; +use tracing::instrument; -use crate::{ - bot::{ReviewItem, ReviewResult}, - errors::AppError, -}; +use crate::{bot::ReviewResult, errors::AppError}; pub struct GiteaAPI { base_url: String, @@ -30,6 +28,7 @@ impl GiteaAPI { }) } + #[instrument(skip(self), err)] pub async fn comment( &self, body: &str, @@ -57,6 +56,7 @@ impl GiteaAPI { res.json::().await.map_err(anyhow::Error::from) } + #[instrument(skip(self), err)] pub async fn edit_comment( &self, body: &str, @@ -84,6 +84,7 @@ impl GiteaAPI { Ok(()) } + #[instrument(skip(self), err)] pub async fn delete_comment(&self, full_name: &str, comment_id: u64) -> anyhow::Result<()> { let url = format!( "{}/api/v1/repos/{}/issues/comments/{}", @@ -102,6 +103,7 @@ impl GiteaAPI { Ok(()) } + #[instrument(skip(self, review_result), err)] pub async fn post_pull_request_review( &self, review_result: &ReviewResult, diff --git a/src/main.rs b/src/main.rs index 68b2ba6..50b1f07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,15 @@ fn main() -> anyhow::Result<()> { async fn run() -> anyhow::Result<()> { let config = env::load_config()?; + + info!( + port = config.http_port, + model = %config.open_router_model, + gitea_url = %config.gitea_url, + bot_name = %config.bot_name, + "Starting Herald" + ); + let bot = Bot::new(config.clone())?; let (tx, rx) = tokio::sync::mpsc::channel::(1); diff --git a/src/open_router.rs b/src/open_router.rs index b5602d5..1a3585a 100644 --- a/src/open_router.rs +++ b/src/open_router.rs @@ -1,6 +1,7 @@ use std::time::Duration; use openrouter_rs::{Message, api::chat::ChatCompletionRequest}; +use tracing::instrument; pub struct ChatResult { pub message: String, @@ -27,6 +28,7 @@ impl OpenRouterClient { }) } + #[instrument(skip(self), err)] pub async fn chat(&self, msg: &str) -> anyhow::Result { let request = ChatCompletionRequest::builder() .model(&self.model) From a2d898c07d8129f9f2200482b2912c8778a925ff Mon Sep 17 00:00:00 2001 From: qpismont Date: Wed, 10 Jun 2026 17:50:24 +0000 Subject: [PATCH 3/6] Fix sentry http request info Add async bot running with semaphore --- src/api.rs | 27 +++++++++++++++++++++------ src/bot.rs | 24 ++++++++++++++++++++++-- src/env.rs | 3 +++ src/errors.rs | 14 ++++++++++++-- src/gitea.rs | 1 + src/main.rs | 13 ++++++------- src/open_router.rs | 1 + 7 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/api.rs b/src/api.rs index 3a2eb42..5179232 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,3 +1,4 @@ +use anyhow::anyhow; use axum::body::{Body, Bytes, to_bytes}; use axum::extract::{FromRef, FromRequest, State}; use axum::http::Request; @@ -6,10 +7,11 @@ use axum::routing::{get, post}; use axum::{Json, Router}; use hmac::{Hmac, KeyInit, Mac}; use reqwest::StatusCode; -use sentry::integrations::tower::NewSentryLayer; +use sentry::integrations::tower::{NewSentryLayer, SentryHttpLayer}; use serde_json::Value; use sha2::Sha256; use subtle::ConstantTimeEq; +use tower::ServiceBuilder; use tower_http::trace::TraceLayer; use tracing::{info, instrument}; @@ -22,10 +24,14 @@ pub async fn start(app_state: AppState) -> anyhow::Result<()> { let http_port = app_state.config.http_port; let app = Router::new() - .layer(NewSentryLayer::>::new_from_top()) - .layer(TraceLayer::new_for_http()) .route("/", get(root)) .route("/webhook", post(webhook)) + .layer( + ServiceBuilder::new() + .layer(NewSentryLayer::>::new_from_top()) + .layer(SentryHttpLayer::new()) + .layer(TraceLayer::new_for_http()), + ) .with_state(app_state); let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", http_port)).await?; @@ -49,9 +55,8 @@ async fn webhook( app_state .bot_tx - .send(wb) - .await - .map_err(anyhow::Error::from)?; + .try_send(wb) + .map_err(|_| AppError::ChannelFullErr)?; Ok((StatusCode::CREATED, "Task started")) } @@ -74,6 +79,16 @@ where let type_header = extract_header(GITEA_EVENT_TYPE_HEADER_NAME, headers)?; let body_bytes = read_body(req.into_body()).await?; + let body_str = String::from_utf8_lossy(&body_bytes).into_owned(); + sentry::configure_scope(|scope| { + scope.add_event_processor(move |mut event| { + let mut request = event.request.take().unwrap_or_default(); + request.data = Some(body_str.clone()); + event.request = Some(request); + Some(event) + }); + }); + verify_signature( app_state.config.webhook_secret.as_bytes(), &sig_header, diff --git a/src/bot.rs b/src/bot.rs index c5d4a7e..b3cf700 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -4,7 +4,7 @@ use crate::{ open_router::OpenRouterClient, }; use serde::Deserialize; -use std::time::Duration; +use std::{sync::Arc, time::Duration}; use tracing::{error, info, instrument}; #[derive(Deserialize, Debug)] @@ -22,11 +22,13 @@ pub struct ReviewItem { pub message: String, } +#[derive(Clone)] pub struct Bot { config: EnvConfig, gitea_api: GiteaAPI, open_router_client: OpenRouterClient, http_client: reqwest::Client, + max_concurrent: usize, } impl Bot { @@ -41,6 +43,7 @@ impl Bot { &config.open_router_model, open_router_timeout, )?, + max_concurrent: config.bot_max_concurrent, config, http_client: reqwest::Client::builder() .timeout(Duration::from_secs(gitea_timeout)) @@ -54,8 +57,25 @@ impl Bot { ) -> anyhow::Result<()> { info!("Bot started"); + let sem = Arc::new(tokio::sync::Semaphore::new(self.max_concurrent)); + let mut tasks = tokio::task::JoinSet::new(); + while let Some(wb) = rx.recv().await { - self.exec(wb).await; + let permit = sem.clone().acquire_owned().await?; + let self_clone = self.clone(); + + tasks.spawn(async move { + self_clone.exec(wb).await; + drop(permit); + }); + } + + // Le channel est fermé : on attend que les tâches en cours se terminent + // proprement avant de rendre la main + while let Some(res) = tasks.join_next().await { + if let Err(e) = res { + error!("Task panicked: {e}"); + } } info!("Bot shutting down, channel closed"); diff --git a/src/env.rs b/src/env.rs index 79ce619..514adfd 100644 --- a/src/env.rs +++ b/src/env.rs @@ -8,6 +8,7 @@ pub struct EnvConfig { pub open_router_model: String, pub open_router_timeout: u64, pub bot_name: String, + pub bot_max_concurrent: usize, pub gitea_url: String, pub gitea_token: String, pub gitea_timeout: u64, @@ -20,6 +21,7 @@ pub fn load_config() -> anyhow::Result { let open_router_api_key = try_get_env("OPEN_ROUTER_API_KEY")?; let open_router_model = try_get_env("OPEN_ROUTER_MODEL")?; let open_router_timeout = try_get_env("OPEN_ROUTER_TIMEOUT")?.parse()?; + let bot_max_concurrent = try_get_env("BOT_MAX_CONCURRENT")?.parse()?; let gitea_url = try_get_env("GITEA_URL")?; let gitea_token = try_get_env("GITEA_TOKEN")?; let gitea_timeout = try_get_env("GITEA_TIMEOUT")?.parse()?; @@ -31,6 +33,7 @@ pub fn load_config() -> anyhow::Result { open_router_api_key, open_router_model, open_router_timeout, + bot_max_concurrent, gitea_url, gitea_token, gitea_timeout, diff --git a/src/errors.rs b/src/errors.rs index d4425b5..65c9830 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,6 +1,6 @@ +use anyhow::anyhow; use axum::response::IntoResponse; use reqwest::StatusCode; -use tracing::error; #[derive(thiserror::Error, Debug)] pub enum AppError { @@ -22,6 +22,9 @@ pub enum AppError { #[error("WebHook have bad action")] InvalidActionErr, + #[error("Channel full")] + ChannelFullErr, + #[error(transparent)] BadJsonStructErr(#[from] serde_json::Error), @@ -55,8 +58,15 @@ impl IntoResponse for AppError { StatusCode::UNAUTHORIZED, "WebHook sig header is invalid".to_string(), ), + AppError::ChannelFullErr => { + sentry_anyhow::capture_anyhow(&anyhow!("Max concurrent tasks reached")); + ( + StatusCode::SERVICE_UNAVAILABLE, + "Max concurrent tasks reached".to_string(), + ) + } AppError::Other(err) => { - error!(%err, "Internal server error"); + sentry_anyhow::capture_anyhow(&err); ( StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string(), diff --git a/src/gitea.rs b/src/gitea.rs index e137e0a..49e4ced 100644 --- a/src/gitea.rs +++ b/src/gitea.rs @@ -6,6 +6,7 @@ use tracing::instrument; use crate::{bot::ReviewResult, errors::AppError}; +#[derive(Clone)] pub struct GiteaAPI { base_url: String, client: reqwest::Client, diff --git a/src/main.rs b/src/main.rs index 50b1f07..68687ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,20 +24,21 @@ fn main() -> anyhow::Result<()> { ) .init(); - if let Ok(sentry_dsn) = env::try_get_env("SENTRY_DSN") { + let _sentry_guard = if let Ok(sentry_dsn) = env::try_get_env("SENTRY_DSN") { info!("Initialize sentry"); - let _guard = sentry::init(( + Some(sentry::init(( sentry_dsn, sentry::ClientOptions { release: sentry::release_name!(), send_default_pii: true, ..Default::default() }, - )); + ))) } else { warn!("SENTRY_DSN not set, sentry will not be initialized"); - } + None + }; tokio::runtime::Runtime::new()?.block_on(run()) } @@ -54,9 +55,7 @@ async fn run() -> anyhow::Result<()> { ); let bot = Bot::new(config.clone())?; - - let (tx, rx) = tokio::sync::mpsc::channel::(1); - + let (tx, rx) = tokio::sync::mpsc::channel::(config.bot_max_concurrent * 2); let app_state = AppState { bot_tx: tx, config }; tokio::try_join!(bot.start(rx), api::start(app_state))?; diff --git a/src/open_router.rs b/src/open_router.rs index 1a3585a..94bfe84 100644 --- a/src/open_router.rs +++ b/src/open_router.rs @@ -8,6 +8,7 @@ pub struct ChatResult { pub cost: Option, } +#[derive(Clone)] pub struct OpenRouterClient { client: openrouter_rs::OpenRouterClient, model: String, From 6599c20c3086543ada64f301fa9759e6343edb19 Mon Sep 17 00:00:00 2001 From: qpismont Date: Wed, 10 Jun 2026 18:08:35 +0000 Subject: [PATCH 4/6] verify_signature before adding body to sentry event --- src/api.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api.rs b/src/api.rs index 5179232..2958b1f 100644 --- a/src/api.rs +++ b/src/api.rs @@ -79,6 +79,12 @@ where let type_header = extract_header(GITEA_EVENT_TYPE_HEADER_NAME, headers)?; let body_bytes = read_body(req.into_body()).await?; + verify_signature( + app_state.config.webhook_secret.as_bytes(), + &sig_header, + &body_bytes, + )?; + let body_str = String::from_utf8_lossy(&body_bytes).into_owned(); sentry::configure_scope(|scope| { scope.add_event_processor(move |mut event| { @@ -89,12 +95,6 @@ where }); }); - verify_signature( - app_state.config.webhook_secret.as_bytes(), - &sig_header, - &body_bytes, - )?; - let webhook = parse_webhook(&type_header, &app_state.config.bot_name, &body_bytes)?; Ok(WebhookExtract(webhook)) } From 3d751ae6c6eab070361c29c7e303b3463fdf36fe Mon Sep 17 00:00:00 2001 From: qpismont Date: Wed, 10 Jun 2026 18:19:18 +0000 Subject: [PATCH 5/6] drain completed tasks and log webhook queue stats --- src/bot.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/bot.rs b/src/bot.rs index b3cf700..56cd999 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -61,6 +61,14 @@ impl Bot { let mut tasks = tokio::task::JoinSet::new(); while let Some(wb) = rx.recv().await { + // Drain completed tasks to avoid the JoinSet growing unbounded + while let Some(res) = tasks.try_join_next() { + if let Err(e) = res { + error!("Task panicked: {e}"); + } + } + + info!(queued = rx.len(), active = tasks.len(), "Webhook received"); let permit = sem.clone().acquire_owned().await?; let self_clone = self.clone(); @@ -70,8 +78,8 @@ impl Bot { }); } - // Le channel est fermé : on attend que les tâches en cours se terminent - // proprement avant de rendre la main + // When all webhook tasks have completed, we can safely exit + // properly before returning while let Some(res) = tasks.join_next().await { if let Err(e) = res { error!("Task panicked: {e}"); From 71ebfdd276c5e71f38201e69988ebf5f43526858 Mon Sep 17 00:00:00 2001 From: qpismont Date: Wed, 10 Jun 2026 18:26:55 +0000 Subject: [PATCH 6/6] tasks.join_next => join_all() --- src/bot.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bot.rs b/src/bot.rs index 56cd999..c237c17 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -80,11 +80,7 @@ impl Bot { // When all webhook tasks have completed, we can safely exit // properly before returning - while let Some(res) = tasks.join_next().await { - if let Err(e) = res { - error!("Task panicked: {e}"); - } - } + tasks.join_all().await; info!("Bot shutting down, channel closed"); Ok(())