diff --git a/.env.sample b/.env.sample index 405d97dd..d79667d3 100644 --- a/.env.sample +++ b/.env.sample @@ -5,3 +5,9 @@ GITHUB_WEBHOOK_SECRET=MUST_BE_CONFIGURED # for logging, refer to this document: https://rust-lang-nursery.github.io/rust-cookbook/development_tools/debugging/config_log.html # `RUSTC_LOG` is not required to run the application, but it makes local development easier # RUST_LOG=MUST_BE_CONFIGURED + +# If you are running a bot on non-rustbot account, +# this allows to configure that username which the bot will respond to. +# For example write blahblahblah here, if you want for this bot to +# respond to @blahblahblah claim. +# TRIAGEBOT_USERNAME=CAN_BE_CONFIGURED diff --git a/Cargo.lock b/Cargo.lock index e93ddd29..8e481ef2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + [[package]] name = "addr2line" version = "0.17.0" @@ -17,6 +23,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -26,6 +43,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + [[package]] name = "ansi_term" version = "0.12.1" @@ -37,9 +60,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.56" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arc-swap" @@ -61,7 +84,7 @@ checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.99", ] [[package]] @@ -91,12 +114,42 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.7.3" @@ -148,6 +201,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.99", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -182,7 +257,7 @@ dependencies = [ "num-integer", "num-traits", "serde", - "time", + "time 0.1.44", "winapi", ] @@ -217,6 +292,12 @@ dependencies = [ "xdg", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "core-foundation" version = "0.9.3" @@ -282,23 +363,14 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctor" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "cynic" -version = "2.2.2" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a3b1c069f38d62c0795fd74df7a55bde42d3ff44e58a00576d1e5c65222fa8" +checksum = "bf035d785657f3621eee03fdfeefab48127d8b1643b6f9edf8b3cd66cbd86e9b" dependencies = [ "cynic-proc-macros", + "ref-cast", "serde", "static_assertions", "thiserror", @@ -306,35 +378,39 @@ dependencies = [ [[package]] name = "cynic-codegen" -version = "2.2.2" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aefc758a26044bd02a2a0ecb871b158abcb98c92d93d7bc40750285064de602c" +checksum = "4e8e65b71a8bd2751712ab38326b73a5f98405b6b5f8fd4dae658e58c1576d09" dependencies = [ "counter", "darling", "graphql-parser", "once_cell", + "ouroboros", "proc-macro2", "quote", + "rkyv", "strsim", - "syn", + "syn 1.0.99", + "thiserror", ] [[package]] name = "cynic-proc-macros" -version = "2.2.2" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c948605f5b7ae37c2e70b42c71679172c4a147351ee1b475578ea8f7d70680db" +checksum = "4a933ea1f357cbd48f2068c59457631696ae58d554f89290e4da272b1f69ebf1" dependencies = [ "cynic-codegen", - "syn", + "quote", + "syn 1.0.99", ] [[package]] name = "darling" -version = "0.13.4" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -342,29 +418,59 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.4" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn", + "syn 1.0.99", ] [[package]] name = "darling_macro" -version = "0.13.4" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.99", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", ] +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + [[package]] name = "diff" version = "0.1.13" @@ -425,9 +531,9 @@ checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" [[package]] name = "either" -version = "1.6.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" @@ -444,6 +550,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "factori" version = "1.1.0" @@ -461,7 +573,7 @@ checksum = "6d6344ded92b0a4a1d90a816632f7ff2a12e01401d325d6295810dacca1dbdd6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.99", ] [[package]] @@ -485,6 +597,12 @@ dependencies = [ "instant", ] +[[package]] +name = "flagset" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779" + [[package]] name = "fnv" version = "1.0.7" @@ -508,14 +626,19 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ - "matches", "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.21" @@ -572,7 +695,7 @@ checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.99", ] [[package]] @@ -656,6 +779,7 @@ version = "0.1.0" dependencies = [ "chrono", "cynic", + "cynic-codegen", ] [[package]] @@ -683,7 +807,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "ignore", "walkdir", ] @@ -700,9 +824,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.13" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -710,7 +834,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.8.1", "slab", "tokio", "tokio-util 0.7.1", @@ -723,6 +847,27 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -749,9 +894,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -760,20 +905,26 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + [[package]] name = "httparse" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6330e8a36bd8c859f3fa6d9382911fbb7147ec39807f63b923933a247240b9ba" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -783,9 +934,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.18" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -806,32 +957,44 @@ dependencies = [ ] [[package]] -name = "hyper-tls" -version = "0.5.0" +name = "hyper-rustls" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ - "bytes", + "futures-util", + "http", "hyper", - "native-tls", + "log", + "rustls", + "rustls-native-certs", "tokio", - "tokio-native-tls", + "tokio-rustls", ] [[package]] -name = "hyperx" -version = "1.4.0" +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "base64", "bytes", - "http", - "httpdate", - "language-tags", - "mime", - "percent-encoding", - "unicase", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", ] [[package]] @@ -842,11 +1005,10 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.2.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -876,7 +1038,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", "serde", ] @@ -920,10 +1092,18 @@ dependencies = [ ] [[package]] -name = "language-tags" -version = "0.3.2" +name = "jsonwebtoken" +version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.4", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] [[package]] name = "lazy_static" @@ -933,9 +1113,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.123" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "lock_api" @@ -949,12 +1129,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.16" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "maplit" @@ -971,12 +1148,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "md-5" version = "0.10.1" @@ -988,9 +1159,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -1074,6 +1245,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -1086,9 +1268,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -1114,30 +1296,46 @@ dependencies = [ [[package]] name = "octocrab" -version = "0.9.1" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c564a454a4124b45eb0170398624efcc75783b079034e7af13202575580e1db5" +checksum = "bbed1b1298bc70dd4ae89fd44dd7c13f0a1c80a506d731eb1b939f14f5e367de" dependencies = [ "arc-swap", "async-trait", - "base64", + "base64 0.21.4", "bytes", + "cfg-if", "chrono", - "hyperx", + "either", + "futures", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-timeout", + "jsonwebtoken", "once_cell", - "reqwest", + "percent-encoding", + "pin-project", + "secrecy", "serde", "serde_json", "serde_path_to_error", + "serde_urlencoded", "snafu", + "tokio", + "tower", + "tower-http", + "tracing", "url", ] [[package]] name = "once_cell" -version = "1.10.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -1151,7 +1349,7 @@ version = "0.10.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -1179,12 +1377,26 @@ dependencies = [ ] [[package]] -name = "output_vt100" -version = "0.1.3" +name = "ouroboros" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" dependencies = [ - "winapi", + "aliasable", + "ouroboros_macro", +] + +[[package]] +name = "ouroboros_macro" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.99", ] [[package]] @@ -1223,11 +1435,29 @@ dependencies = [ "serde", ] +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.0", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" @@ -1258,7 +1488,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 1.0.99", ] [[package]] @@ -1292,29 +1522,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.10" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.10" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1336,7 +1566,7 @@ checksum = "9e76c801e97c9cf696097369e517785b98056e98b21149384c812febfc5912f2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.99", ] [[package]] @@ -1358,7 +1588,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "878c6cbf956e03af9aa8204b407b9cbf47c072164800aa918c516cd4b056c50c" dependencies = [ - "base64", + "base64 0.13.0", "byteorder", "bytes", "fallible-iterator", @@ -1383,7 +1613,7 @@ dependencies = [ "postgres-protocol", "serde", "serde_json", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -1394,32 +1624,74 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "pretty_assertions" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ansi_term", - "ctor", "diff", - "output_vt100", + "yansi", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.99", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.99", +] + [[package]] name = "pulldown-cmark" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca36dea94d187597e104a5c8e4b07576a8a45aa5db48a65e12940d3eb7461f55" dependencies = [ - "bitflags", + "bitflags 1.3.2", "getopts", "memchr", "unicase", @@ -1427,13 +1699,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -1470,7 +1748,7 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1484,6 +1762,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ref-cast" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "regex" version = "1.6.0" @@ -1520,13 +1818,22 @@ dependencies = [ ] [[package]] -name = "reqwest" -version = "0.11.10" +name = "rend" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" +checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" dependencies = [ - "base64", - "bytes", + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +dependencies = [ + "base64 0.21.4", + "bytes", "encoding_rs", "futures-core", "futures-util", @@ -1537,10 +1844,10 @@ dependencies = [ "hyper-tls", "ipnet", "js-sys", - "lazy_static", "log", "mime", "native-tls", + "once_cell", "percent-encoding", "pin-project-lite", "serde", @@ -1548,6 +1855,7 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-native-tls", + "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -1555,6 +1863,49 @@ dependencies = [ "winreg", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rkyv" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" +dependencies = [ + "bitvec", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid 1.5.0", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.99", +] + [[package]] name = "route-recognizer" version = "0.3.1" @@ -1564,9 +1915,9 @@ checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" [[package]] name = "rust_team_data" version = "1.0.0" -source = "git+https://github.com/rust-lang/team#49683ec8af9ca6b546b3af516ea4654424e302eb" +source = "git+https://github.com/rust-lang/team#1ff0fa95e5ead9fbbb4be3975cac8ede35b9d3d5" dependencies = [ - "indexmap", + "indexmap 2.1.0", "serde", ] @@ -1576,6 +1927,49 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +[[package]] +name = "rustls" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.4", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.9" @@ -1607,13 +2001,38 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "security-framework" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1632,29 +2051,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1670,6 +2089,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1720,6 +2148,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time 0.3.28", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -1740,9 +2186,9 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "snafu" -version = "0.6.10" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" dependencies = [ "backtrace", "doc-comment", @@ -1751,25 +2197,42 @@ dependencies = [ [[package]] name = "snafu-derive" -version = "0.6.10" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ + "heck", "proc-macro2", "quote", - "syn", + "syn 1.0.99", ] [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1809,6 +2272,23 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.3.0" @@ -1856,7 +2336,7 @@ checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.99", ] [[package]] @@ -1879,6 +2359,34 @@ dependencies = [ "winapi", ] +[[package]] +name = "time" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +dependencies = [ + "deranged", + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.5.1" @@ -1894,6 +2402,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tls_codec" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "tokio" version = "1.17.0" @@ -1911,6 +2440,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "1.7.0" @@ -1919,7 +2458,7 @@ checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.99", ] [[package]] @@ -1955,6 +2494,16 @@ dependencies = [ "tokio-util 0.6.9", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.6.9" @@ -1985,11 +2534,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -2009,6 +2583,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.4.0", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-layer" version = "0.3.1" @@ -2023,9 +2616,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.33" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b9fa4360528139bc96100c160b7ae879f5567f49f1782b0b02035b0358ebf3" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", @@ -2036,22 +2629,22 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.20" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] name = "tracing-core" -version = "0.1.25" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfce9f3241b150f36e8e54bb561a742d5daa1a47b5dd9a5ce369fd4a4db2210" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ - "lazy_static", + "once_cell", "valuable", ] @@ -2090,6 +2683,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "bytes", "chrono", "comrak", "cron", @@ -2128,7 +2722,8 @@ dependencies = [ "tracing", "tracing-subscriber", "url", - "uuid", + "uuid 0.8.2", + "x509-cert", ] [[package]] @@ -2232,21 +2827,21 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] @@ -2272,15 +2867,20 @@ dependencies = [ "void", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" -version = "2.2.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", "serde", ] @@ -2295,6 +2895,12 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" + [[package]] name = "valuable" version = "0.1.0" @@ -2373,7 +2979,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 1.0.99", "wasm-bindgen-shared", ] @@ -2407,7 +3013,7 @@ checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.99", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2459,13 +3065,110 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "spki", + "tls_codec", ] [[package]] @@ -2476,3 +3179,29 @@ checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" dependencies = [ "dirs", ] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] diff --git a/Cargo.toml b/Cargo.toml index 83c6139f..0e2af022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ hex = "0.4" parser = { path = "parser" } rust_team_data = { git = "https://github.com/rust-lang/team" } glob = "0.3.0" -toml = "0.5.1" +toml = "0.8.8" hyper = { version = "0.14.4", features = ["server", "stream"]} tokio = { version = "1.7.1", features = ["macros", "time", "rt"] } futures = { version = "0.3", default-features = false, features = ["std"] } @@ -32,11 +32,12 @@ chrono = { version = "0.4", features = ["serde"] } tokio-postgres = { version = "0.7.2", features = ["with-chrono-0_4", "with-serde_json-1", "with-uuid-0_8"] } postgres-native-tls = "0.5.0" native-tls = "0.2" +x509-cert = { version = "0.2.5", features = ["pem"] } serde_path_to_error = "0.1.2" -octocrab = "0.9.1" +octocrab = "0.30.1" comrak = { version = "0.8.2", default-features = false } route-recognizer = "0.3.0" -cynic = { version = "2.0.0" } +cynic = "3.2.2" itertools = "0.10.2" tower = { version = "0.4.13", features = ["util", "limit", "buffer", "load-shed"] } github-graphql = { path = "github-graphql" } @@ -44,6 +45,7 @@ rand = "0.8.5" ignore = "0.4.18" postgres-types = { version = "0.2.4", features = ["derive"] } cron = { version = "0.12.0" } +bytes = "1.1.0" [dependencies.serde] version = "1" diff --git a/README.md b/README.md index 69983c48..85216d9a 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ This is the triage and team assistance bot for the rust-lang organization. -Please see the [wiki] for our documentation, and feel free to contribute edits +Please see the [forge] for our documentation, and feel free to contribute edits if you find something helpful! -[wiki]: https://github.com/rust-lang/triagebot/wiki +[forge]: https://forge.rust-lang.org/triagebot/index.html ## How triagebot works @@ -34,38 +34,86 @@ Some developers may settle with testing in production as the risks tend to be lo The general overview of what you will need to do: -1. Install Postgres. Look online for any help with installing and setting up Postgres (particularly if you need to create a user and set up permissions). -2. Create a database: `createdb triagebot` -3. Provide a way for GitHub to access the Triagebot webserver. - There are various ways to do this (such as placing it behind a proxy, or poking holes in your firewall). - Or, you can use a service such as https://ngrok.com/ to access on your local dev machine via localhost. - Installation is fairly simple, though requires setting up a (free) account. - Run the command `ngrok http 8000` to forward to port 8000 on localhost. -4. Create a GitHub repo to run some tests on. -5. Configure the webhook in your GitHub repo. - I recommend at least skimming the [GitHub webhook documentation](https://docs.github.com/en/developers/webhooks-and-events/webhooks/about-webhooks) if you are not familiar with webhooks. In short: - - 1. Go to the settings page. - 2. Go to the webhook section. - 3. Click "Add webhook" - 4. Include the settings: - - - Payload URL: This is the URL to your Triagebot server, for example http://7e9ea9dc.ngrok.io/github-hook. This URL is displayed when you ran the `ngrok` command above. - - Content type: application/json - - Secret: Enter a shared secret (some longish random text) - - Events: "Send me everything" -6. Configure the `.env` file: +1. Create a repo on GitHub to run tests on. +2. [Configure a database](#configure-a-database) +3. [Configure webhook forwarding](#configure-webhook-forwarding) +4. Configure the `.env` file: 1. Copy `.env.sample` to `.env` 2. `GITHUB_API_TOKEN`: This is a token needed for Triagebot to send requests to GitHub. Go to GitHub Settings > Developer Settings > Personal Access Token, and create a new token. The `repo` permission should be sufficient. If this is not set, Triagebot will also look in `~/.gitconfig` in the `github.oauth-token` setting. - 3. `DATABASE_URL`: This is the URL to the Postgres database. Something like `postgres://eric@localhost/triagebot` should work, replacing `eric` with your username. + 3. `DATABASE_URL`: This is the URL to the database. See [Configuring a database](#configuring-a-database). 4. `GITHUB_WEBHOOK_SECRET`: Enter the secret you entered in the webhook above. 5. `RUST_LOG`: Set this to `debug`. -7. Run `cargo run --bin triagebot`. This starts the http server listening on port 8000. -8. Add a `triagebot.toml` file to the main branch of your GitHub repo with whichever services you want to try out. -9. Try interacting with your repo, such as issuing `@rustbot` commands or interacting with PRs and issues (depending on which services you enabled in `triagebot.toml`). Watch the logs from the server to see what's going on. +5. Run `cargo run --bin triagebot`. This starts the http server listening for webhooks on port 8000. +6. Add a `triagebot.toml` file to the main branch of your GitHub repo with whichever services you want to try out. +7. Try interacting with your repo, such as issuing `@rustbot` commands or interacting with PRs and issues (depending on which services you enabled in `triagebot.toml`). Watch the logs from the server to see what's going on. + +### Configure a database + +To use Postgres, you will need to install it and configure it: + +1. Install Postgres. Look online for any help with installing and setting up Postgres (particularly if you need to create a user and set up permissions). +2. Create a database: `createdb triagebot` +3. In the `.env` file, set the `DATABASE_URL`: + + ```sh + DATABASE_URL=postgres://eric@localhost/triagebot + ``` + + replacing `eric` with the username on your local system. + +### Configure webhook forwarding + +I recommend at least skimming the [GitHub webhook documentation](https://docs.github.com/en/developers/webhooks-and-events/webhooks/about-webhooks) if you are not familiar with webhooks. +In order for GitHub's webhooks to reach your triagebot server, you'll need to figure out some way to route them to your machine. +There are various options on how to do this. +You can poke holes into your firewall or use a proxy, but you shouldn't expose your machine to the the internet. +There are various services which help with this problem. +These generally involve running a program on your machine that connects to an external server which relays the hooks into your machine. +There are several to choose from: + +* [gh webhook](#gh-webhook) — This is a GitHub-native service. This is the easiest to use. +* [ngrok](#ngrok) — This is pretty easy to use, but requires setting up a free account. +* — This is another service recommended by GitHub. +* — This is another service recommended by GitHub. + +#### gh webhook + +The [`gh` CLI](https://github.com/cli/cli) is the official CLI tool which I highly recommend getting familiar with. +There is an official extension which provides webhook forwarding and also takes care of all the configuration. +See [cli/gh-webhook](https://docs.github.com/en/developers/webhooks-and-events/webhooks/receiving-webhooks-with-the-github-cli) for more information on installing it. + +This is super easy to use, and doesn't require manually configuring webhook settings. +The command to run looks something like: + +```sh +gh webhook forward --repo=ehuss/triagebot-test --events=* \ + --url=http://127.0.0.1:8000/github-hook --secret somelongsekrit +``` + +Where the value in `--secret` is the secret value you place in `GITHUB_WEBHOOK_SECRET` in the `.env` file, and `--repo` is the repo you want to test against. + +#### ngrok + +The following is an example of using to provide webhook forwarding. +You need to sign up for a free account, and also deal with configuring the GitHub webhook settings. + +1. Install ngrok. +2. Run `ngrok http 8000`. This will forward webhook events to localhost on port 8000. +3. Configure GitHub webhooks in the test repo you created. + In short: + + 1. Go to the settings page for your GitHub repo. + 2. Go to the webhook section. + 3. Click "Add webhook" + 4. Include the settings: + + * Payload URL: This is the URL to your Triagebot server, for example http://7e9ea9dc.ngrok.io/github-hook. This URL is displayed when you ran the `ngrok` command above. + * Content type: application/json + * Secret: Enter a shared secret (some longish random text) + * Events: "Send me everything" ## License diff --git a/github-graphql/Cargo.toml b/github-graphql/Cargo.toml index 08337d06..206fca32 100644 --- a/github-graphql/Cargo.toml +++ b/github-graphql/Cargo.toml @@ -5,4 +5,7 @@ edition = "2021" [dependencies] chrono = { version = "0.4", features = ["serde"] } -cynic = { version = "2.2.2" } \ No newline at end of file +cynic = { version = "3.2.2", features = ["rkyv"] } + +[build-dependencies] +cynic-codegen = { version = "3.2.2", features = ["rkyv"] } diff --git a/github-graphql/PullRequestsOpen.gql b/github-graphql/PullRequestsOpen.gql new file mode 100644 index 00000000..803773ed --- /dev/null +++ b/github-graphql/PullRequestsOpen.gql @@ -0,0 +1,26 @@ +query PullRequestsOpen ($repo_owner: String!, $repo_name: String!, $after: String) { + repository(owner: $repo_owner, name: $repo_name) { + pullRequests(first: 100, after: $after, states:OPEN) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + updatedAt + createdAt + assignees(first: 10) { + nodes { + login + databaseId + } + } + labels(first:5, orderBy:{field:NAME, direction:DESC}) { + nodes { + name + } + } + } + } + } +} diff --git a/github-graphql/README.md b/github-graphql/README.md new file mode 100644 index 00000000..ba42b79a --- /dev/null +++ b/github-graphql/README.md @@ -0,0 +1,31 @@ +# How to use GraphQL with Rust + +# GUI Clients (Electron apps) + +Use a client to experiment and build your GraphQL query/mutation. + +https://insomnia.rest/download + +https://docs.usebruno.com + +Once you're happy with the result, save your query in a `.gql` file in this directory. It will serve as +documentation on how to reproduce the Rust boilerplate. + +# Cynic CLI + +Introspect a schema and save it locally: + +```sh +cynic introspect \ + -H "User-Agent: cynic/3.4.3" \ + -H "Authorization: Bearer [GITHUB_TOKEN]" \ + "https://api.github.com/graphql" \ + -o schemas/github.graphql +``` + +Execute a GraphQL query/mutation and save locally the Rust boilerplate: + +``` sh +cynic querygen --schema schemas/github.graphql --query query.gql +``` + diff --git a/github-graphql/build.rs b/github-graphql/build.rs new file mode 100644 index 00000000..419d8011 --- /dev/null +++ b/github-graphql/build.rs @@ -0,0 +1,7 @@ +pub fn main() { + cynic_codegen::register_schema("github") + .from_sdl_file("src/github.graphql") + .unwrap() + .as_default() + .unwrap(); +} diff --git a/github-graphql/src/lib.rs b/github-graphql/src/lib.rs index 42ea4789..48b06264 100644 --- a/github-graphql/src/lib.rs +++ b/github-graphql/src/lib.rs @@ -3,18 +3,19 @@ //! See for more GitHub's GraphQL API. // This schema can be downloaded from https://docs.github.com/public/schema.docs.graphql -#[cynic::schema_for_derives(file = "src/github.graphql", module = "schema")] pub mod queries { use super::schema; + pub type Date = chrono::NaiveDate; pub type DateTime = chrono::DateTime; + cynic::impl_scalar!(Date, schema::Date); cynic::impl_scalar!(DateTime, schema::DateTime); #[derive(cynic::QueryVariables, Debug, Clone)] - pub struct LeastRecentlyReviewedPullRequestsArguments { - pub repository_owner: String, - pub repository_name: String, + pub struct LeastRecentlyReviewedPullRequestsArguments<'a> { + pub repository_owner: &'a str, + pub repository_name: &'a str, pub after: Option, } @@ -88,6 +89,7 @@ pub mod queries { #[derive(cynic::QueryFragment, Debug)] pub struct User { pub login: String, + pub database_id: Option, } #[derive(cynic::QueryFragment, Debug)] @@ -130,16 +132,15 @@ pub mod queries { pub struct Uri(pub String); } -#[cynic::schema_for_derives(file = "src/github.graphql", module = "schema")] pub mod docs_update_queries { use super::queries::{DateTime, PageInfo}; use super::schema; #[derive(cynic::QueryVariables, Clone, Debug)] - pub struct RecentCommitsArguments { - pub branch: String, - pub name: String, - pub owner: String, + pub struct RecentCommitsArguments<'a> { + pub branch: &'a str, + pub name: &'a str, + pub owner: &'a str, pub after: Option, } @@ -266,14 +267,11 @@ pub mod docs_update_queries { pub struct GitObjectID(pub String); } -#[allow(non_snake_case, non_camel_case_types)] -mod schema { - cynic::use_schema!("src/github.graphql"); -} +#[cynic::schema("github")] +mod schema {} -#[cynic::schema_for_derives(file = "src/github.graphql", module = "schema")] -pub mod project_items_by_status { - use super::queries::{PageInfo, Uri}; +pub mod project_items { + use super::queries::{Date, PageInfo, Uri}; use super::schema; #[derive(cynic::QueryVariables, Debug, Clone)] @@ -312,13 +310,29 @@ pub mod project_items_by_status { #[derive(cynic::QueryFragment, Debug)] pub struct ProjectV2Item { pub content: Option, + + // Currently we hard code the field names we care about here. + #[cynic(rename = "fieldValueByName")] #[arguments(name = "Status")] - pub field_value_by_name: Option, + pub status: Option, + #[cynic(rename = "fieldValueByName")] + #[arguments(name = "Date")] + pub date: Option, } impl ProjectV2Item { - pub fn status(&self) -> &Option { - &self.field_value_by_name + pub fn status(&self) -> Option<&str> { + let Some(ref status) = self.status else { + return None; + }; + status.as_str() + } + + pub fn date(&self) -> Option { + let Some(ref date) = self.date else { + return None; + }; + date.as_date() } } @@ -333,6 +347,7 @@ pub mod project_items_by_status { #[derive(cynic::InlineFragments, Debug)] pub enum ProjectV2ItemFieldValue { ProjectV2ItemFieldSingleSelectValue(ProjectV2ItemFieldSingleSelectValue), + ProjectV2ItemFieldDateValue(ProjectV2ItemFieldDateValue), #[cynic(fallback)] Other, @@ -345,6 +360,13 @@ pub mod project_items_by_status { _ => return None, }) } + + pub fn as_date(&self) -> Option { + match self { + Self::ProjectV2ItemFieldDateValue(val) => val.date, + _ => None, + } + } } #[derive(cynic::QueryFragment, Debug)] @@ -358,4 +380,50 @@ pub mod project_items_by_status { pub struct ProjectV2ItemFieldSingleSelectValue { pub name: Option, } + + #[derive(cynic::QueryFragment, Debug)] + pub struct ProjectV2ItemFieldDateValue { + pub date: Option, + } +} + +/// Retrieve all pull requests waiting on review from T-compiler +/// GraphQL query: see file github-graphql/PullRequestsOpen.gql +pub mod pull_requests_open { + use crate::queries::{LabelConnection, PullRequestConnection, UserConnection}; + + use super::queries::DateTime; + use super::schema; + + #[derive(cynic::QueryVariables, Clone, Debug)] + pub struct PullRequestsOpenVariables<'a> { + pub repo_owner: &'a str, + pub repo_name: &'a str, + pub after: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(graphql_type = "Query", variables = "PullRequestsOpenVariables")] + pub struct PullRequestsOpen { + #[arguments(owner: $repo_owner, name: $repo_name)] + pub repository: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(variables = "PullRequestsOpenVariables")] + pub struct Repository { + #[arguments(first: 100, after: $after, states: "OPEN")] + pub pull_requests: PullRequestConnection, + } + + #[derive(cynic::QueryFragment, Debug)] + pub struct PullRequest { + pub number: i32, + pub updated_at: DateTime, + pub created_at: DateTime, + #[arguments(first: 10)] + pub assignees: UserConnection, + #[arguments(first: 5, orderBy: { direction: "DESC", field: "NAME" })] + pub labels: Option, + } } diff --git a/src/actions.rs b/src/actions.rs index 6e7af814..73557078 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use std::sync::Arc; use async_trait::async_trait; -use reqwest::Client; use serde::{Deserialize, Serialize}; use tera::{Context, Tera}; @@ -52,6 +51,7 @@ pub struct IssueDecorator { pub updated_at_hts: String, pub fcp_details: Option, + pub mcp_details: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -62,6 +62,12 @@ pub struct FCPDetails { pub initiating_comment_content: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct MCPDetails { + pub zulip_link: String, + pub concerns: Option>, +} + lazy_static! { pub static ref TEMPLATES: Tera = { match Tera::new("templates/*") { @@ -87,7 +93,7 @@ pub fn to_human(d: DateTime) -> String { #[async_trait] impl<'a> Action for Step<'a> { async fn call(&self) -> anyhow::Result { - let gh = GithubClient::new_with_default_token(Client::new()); + let gh = GithubClient::new_from_env(); // retrieve all Rust compiler meetings // from today for 7 days @@ -112,6 +118,7 @@ impl<'a> Action for Step<'a> { // These are unused for query. default_branch: "master".to_string(), fork: false, + parent: None, }; for QueryMap { name, kind, query } in queries { @@ -123,8 +130,21 @@ impl<'a> Action for Step<'a> { let query = query.clone(); handles.push(tokio::task::spawn(async move { let _permit = semaphore.acquire().await?; + let mcps_groups = [ + "mcp_new_not_seconded", + "mcp_old_not_seconded", + "mcp_accepted", + "in_pre_fcp", + "in_fcp", + ]; let issues = query - .query(&repository, name == "proposed_fcp", &gh) + .query( + &repository, + name == "proposed_fcp", + mcps_groups.contains(&name.as_str()) + && repository.full_name.contains("rust-lang/compiler-team"), + &gh, + ) .await?; Ok((name, kind, issues)) })); diff --git a/src/agenda.rs b/src/agenda.rs index fe885b6d..238b4780 100644 --- a/src/agenda.rs +++ b/src/agenda.rs @@ -23,6 +23,7 @@ pub fn prioritization<'a>() -> Box { "major-change-accepted", "t-libs", "t-libs-api", + "t-rustdoc", ], }), }, @@ -39,6 +40,7 @@ pub fn prioritization<'a>() -> Box { "final-comment-period", "t-libs", "t-libs-api", + "t-rustdoc", ], }), }, @@ -48,7 +50,7 @@ pub fn prioritization<'a>() -> Box { query: Arc::new(github::Query { filters: vec![("state", "open")], include_labels: vec!["proposed-final-comment-period"], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, QueryMap { @@ -57,7 +59,7 @@ pub fn prioritization<'a>() -> Box { query: Arc::new(github::Query { filters: vec![("state", "open")], include_labels: vec!["final-comment-period"], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, QueryMap { @@ -66,7 +68,7 @@ pub fn prioritization<'a>() -> Box { query: Arc::new(github::Query { filters: vec![("state", "all")], include_labels: vec!["major-change-accepted", "to-announce"], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, QueryMap { @@ -79,7 +81,7 @@ pub fn prioritization<'a>() -> Box { "disposition-merge", "to-announce", ], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, ], @@ -93,7 +95,7 @@ pub fn prioritization<'a>() -> Box { query: Arc::new(github::Query { filters: vec![("state", "open")], include_labels: vec!["proposed-final-comment-period", "T-compiler"], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, QueryMap { @@ -102,7 +104,7 @@ pub fn prioritization<'a>() -> Box { query: Arc::new(github::Query { filters: vec![("state", "open")], include_labels: vec!["final-comment-period", "T-compiler"], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, QueryMap { @@ -115,7 +117,7 @@ pub fn prioritization<'a>() -> Box { "disposition-merge", "to-announce", ], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, ], @@ -129,7 +131,7 @@ pub fn prioritization<'a>() -> Box { query: Arc::new(github::Query { filters: vec![("state", "open")], include_labels: vec!["proposed-final-comment-period"], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, QueryMap { @@ -138,7 +140,7 @@ pub fn prioritization<'a>() -> Box { query: Arc::new(github::Query { filters: vec![("state", "open")], include_labels: vec!["final-comment-period"], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, QueryMap { @@ -151,7 +153,7 @@ pub fn prioritization<'a>() -> Box { "disposition-merge", "to-announce", ], - exclude_labels: vec!["t-libs", "t-libs-api"], + exclude_labels: vec!["t-libs", "t-libs-api", "t-rustdoc"], }), }, ], @@ -169,31 +171,33 @@ pub fn prioritization<'a>() -> Box { exclude_labels: vec!["beta-accepted"], }), }, + // stable nomination queries QueryMap { - name: "beta_nominated_t_rustdoc", + name: "stable_nominated_t_compiler", kind: QueryKind::List, query: Arc::new(github::Query { filters: vec![], - include_labels: vec!["beta-nominated", "T-rustdoc"], - exclude_labels: vec!["beta-accepted"], + include_labels: vec!["stable-nominated", "T-compiler"], + exclude_labels: vec!["stable-accepted"], }), }, - // stable nomination queries + // beta nomination t-types QueryMap { - name: "stable_nominated_t_compiler", + name: "beta_nominated_t_types", kind: QueryKind::List, query: Arc::new(github::Query { filters: vec![], - include_labels: vec!["stable-nominated", "T-compiler"], - exclude_labels: vec!["stable-accepted"], + include_labels: vec!["beta-nominated", "T-types"], + exclude_labels: vec!["beta-accepted"], }), }, + // stable nomination queries QueryMap { - name: "stable_nominated_t_rustdoc", + name: "stable_nominated_t_types", kind: QueryKind::List, query: Arc::new(github::Query { filters: vec![], - include_labels: vec!["stable-nominated", "T-rustdoc"], + include_labels: vec!["stable-nominated", "T-Types"], exclude_labels: vec!["stable-accepted"], }), }, @@ -370,15 +374,6 @@ pub fn prioritization<'a>() -> Box { exclude_labels: vec![], }), }, - QueryMap { - name: "p_critical_t_rustdoc", - kind: QueryKind::List, - query: Arc::new(github::Query { - filters: vec![("state", "open")], - include_labels: vec!["T-rustdoc", "P-critical"], - exclude_labels: vec![], - }), - }, QueryMap { name: "beta_regressions_p_high", kind: QueryKind::List, @@ -475,10 +470,8 @@ pub fn lang<'a>() -> Box { QueryMap { name: "scheduled_meetings", kind: QueryKind::List, - query: Arc::new(github::Query { - filters: vec![("state", "open"), ("is", "issue")], - include_labels: vec!["meeting-proposal", "meeting-scheduled"], - exclude_labels: vec![], + query: Arc::new(github::DesignMeetings { + with_status: github::DesignMeetingStatus::Scheduled, }), }, ], @@ -596,7 +589,9 @@ pub fn lang_planning<'a>() -> Box { QueryMap { name: "proposed_meetings", kind: QueryKind::List, - query: Arc::new(github::ProposedDesignMeetings), + query: Arc::new(github::DesignMeetings { + with_status: github::DesignMeetingStatus::Proposed, + }), }, ], }, @@ -616,6 +611,69 @@ pub fn lang_planning<'a>() -> Box { }) } +pub fn types_planning<'a>() -> Box { + Box::new(Step { + name: "types_planning_agenda", + actions: vec![ + Query { + repos: vec![("rust-lang", "types-team")], + queries: vec![ + QueryMap { + name: "roadmap_tracking_issues", + kind: QueryKind::List, + query: Arc::new(github::Query { + filters: vec![("state", "open"), ("is", "issue")], + include_labels: vec!["roadmap-tracking-issue"], + exclude_labels: vec![], + }), + }, + QueryMap { + name: "deep_dive_proposals", + kind: QueryKind::List, + query: Arc::new(github::Query { + filters: vec![("state", "open"), ("is", "issue")], + include_labels: vec!["deep-dive-proposal"], + exclude_labels: vec![], + }), + }, + QueryMap { + name: "major_changes", + kind: QueryKind::List, + query: Arc::new(github::Query { + filters: vec![("state", "open"), ("is", "issue")], + include_labels: vec!["major-change"], + exclude_labels: vec![], + }), + }, + ], + }, + Query { + repos: vec![("rust-lang", "rust")], + queries: vec![ + QueryMap { + name: "nominated_issues", + kind: QueryKind::List, + query: Arc::new(github::Query { + filters: vec![("state", "open"), ("is", "issue")], + include_labels: vec!["I-types-nominated"], + exclude_labels: vec![], + }), + }, + QueryMap { + name: "types_fcps", + kind: QueryKind::List, + query: Arc::new(github::Query { + filters: vec![("state", "open")], + include_labels: vec!["T-types", "proposed-final-comment-period"], + exclude_labels: vec![], + }), + }, + ], + }, + ], + }) +} + // Things to add (maybe): // - Compiler RFCs // - P-high issues @@ -644,6 +702,7 @@ pub static INDEX: &str = r#" diff --git a/src/bin/types.rs b/src/bin/types.rs new file mode 100644 index 00000000..ec411927 --- /dev/null +++ b/src/bin/types.rs @@ -0,0 +1,23 @@ +use triagebot::agenda; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> anyhow::Result<()> { + dotenv::dotenv().ok(); + tracing_subscriber::fmt::init(); + + let args: Vec = std::env::args().collect(); + if args.len() == 2 { + match &args[1][..] { + "planning" => { + let agenda = agenda::types_planning(); + print!("{}", agenda.call().await?); + return Ok(()); + } + _ => {} + } + } + + eprintln!("Usage: types (planning)"); + + Ok(()) +} diff --git a/src/config.rs b/src/config.rs index 203d2366..93ac941a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,7 +6,7 @@ use std::sync::{Arc, RwLock}; use std::time::{Duration, Instant}; use tracing as log; -static CONFIG_FILE_NAME: &str = "triagebot.toml"; +pub(crate) static CONFIG_FILE_NAME: &str = "triagebot.toml"; const REFRESH_EVERY: Duration = Duration::from_secs(2 * 60); // Every two minutes lazy_static::lazy_static! { @@ -17,6 +17,7 @@ lazy_static::lazy_static! { #[derive(PartialEq, Eq, Debug, serde::Deserialize)] #[serde(rename_all = "kebab-case")] +#[serde(deny_unknown_fields)] pub(crate) struct Config { pub(crate) relabel: Option, pub(crate) assign: Option, @@ -30,14 +31,19 @@ pub(crate) struct Config { pub(crate) notify_zulip: Option, pub(crate) github_releases: Option, pub(crate) review_submitted: Option, + pub(crate) review_requested: Option, pub(crate) shortcut: Option, pub(crate) note: Option, pub(crate) mentions: Option, pub(crate) no_merges: Option, pub(crate) decision: Option, + // We want this validation to run even without the entry in the config file + #[serde(default = "ValidateConfig::default")] + pub(crate) validate_config: Option, } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct NominateConfig { // team name -> label pub(crate) teams: HashMap, @@ -68,6 +74,7 @@ impl PingConfig { } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct PingTeamConfig { pub(crate) message: String, #[serde(default)] @@ -76,6 +83,7 @@ pub(crate) struct PingTeamConfig { } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct AssignConfig { /// If `true`, then posts a warning comment if the PR is opened against a /// different branch than the default (usually master or main). @@ -91,15 +99,38 @@ pub(crate) struct AssignConfig { /// usernames, team names, or ad-hoc groups. #[serde(default)] pub(crate) owners: HashMap>, + #[serde(default)] + pub(crate) users_on_vacation: HashSet, +} + +impl AssignConfig { + pub(crate) fn is_on_vacation(&self, user: &str) -> bool { + let name_lower = user.to_lowercase(); + self.users_on_vacation + .iter() + .any(|vacationer| name_lower == vacationer.to_lowercase()) + } } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct NoMergesConfig { + /// No action will be taken on PRs with these substrings in the title. #[serde(default)] - _empty: (), + pub(crate) exclude_titles: Vec, + /// Set these labels on the PR when merge commits are detected. + #[serde(default)] + pub(crate) labels: Vec, + /// Override the default message to post when merge commits are detected. + /// + /// This message will always be followed up with + /// "The following commits are merge commits:" and then + /// a list of the merge commits. + pub(crate) message: Option, } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct NoteConfig { #[serde(default)] _empty: (), @@ -112,6 +143,7 @@ pub(crate) struct MentionsConfig { } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct MentionsPathConfig { pub(crate) message: Option, #[serde(default)] @@ -120,22 +152,34 @@ pub(crate) struct MentionsPathConfig { #[derive(PartialEq, Eq, Debug, serde::Deserialize)] #[serde(rename_all = "kebab-case")] +#[serde(deny_unknown_fields)] pub(crate) struct RelabelConfig { #[serde(default)] pub(crate) allow_unauthenticated: Vec, } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct ShortcutConfig { #[serde(default)] _empty: (), } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct PrioritizeConfig { pub(crate) label: String, } +#[derive(PartialEq, Eq, Debug, serde::Deserialize)] +pub(crate) struct ValidateConfig {} + +impl ValidateConfig { + fn default() -> Option { + Some(ValidateConfig {}) + } +} + #[derive(PartialEq, Eq, Debug, serde::Deserialize)] pub(crate) struct AutolabelConfig { #[serde(flatten)] @@ -155,6 +199,7 @@ impl AutolabelConfig { } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct AutolabelLabelConfig { #[serde(default)] pub(crate) trigger_labels: Vec, @@ -164,6 +209,8 @@ pub(crate) struct AutolabelLabelConfig { pub(crate) trigger_files: Vec, #[serde(default)] pub(crate) new_pr: bool, + #[serde(default)] + pub(crate) new_issue: bool, } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] @@ -173,6 +220,7 @@ pub(crate) struct NotifyZulipConfig { } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct NotifyZulipLabelConfig { pub(crate) zulip_stream: u64, pub(crate) topic: String, @@ -185,6 +233,7 @@ pub(crate) struct NotifyZulipLabelConfig { } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct MajorChangeConfig { /// A username (typically a group, e.g. T-lang) to ping on Zulip for newly /// opened proposals. @@ -220,17 +269,27 @@ impl MajorChangeConfig { } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct GlacierConfig {} #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct CloseConfig {} #[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct ReviewSubmittedConfig { pub(crate) review_labels: Vec, pub(crate) reviewed_label: String, } +#[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct ReviewRequestedConfig { + pub(crate) remove_labels: Vec, + pub(crate) add_labels: Vec, +} + pub(crate) async fn get( gh: &GithubClient, repo: &Repository, @@ -251,6 +310,7 @@ pub(crate) async fn get( #[derive(PartialEq, Eq, Debug, serde::Deserialize)] #[serde(rename_all = "kebab-case")] +#[serde(deny_unknown_fields)] pub(crate) struct GitHubReleasesConfig { pub(crate) format: ChangelogFormat, pub(crate) project_name: String, @@ -284,7 +344,8 @@ async fn get_fresh_config( .await .map_err(|e| ConfigurationError::Http(Arc::new(e)))? .ok_or(ConfigurationError::Missing)?; - let config = Arc::new(toml::from_slice::(&contents).map_err(ConfigurationError::Toml)?); + let contents = String::from_utf8_lossy(&*contents); + let config = Arc::new(toml::from_str::(&contents).map_err(ConfigurationError::Toml)?); log::debug!("fresh configuration for {}: {:?}", repo.full_name, config); Ok(config) } @@ -307,10 +368,13 @@ impl fmt::Display for ConfigurationError { Add a `triagebot.toml` in the root of the default branch to enable it." ), ConfigurationError::Toml(e) => { - write!(f, "Malformed `triagebot.toml` in default branch.\n{}", e) + write!(f, "Malformed `triagebot.toml` in default branch.\n{e}") } - ConfigurationError::Http(_) => { - write!(f, "Failed to query configuration for this repository.") + ConfigurationError::Http(e) => { + write!( + f, + "Failed to query configuration for this repository.\n{e:?}" + ) } } } @@ -329,6 +393,7 @@ mod tests { ] [assign] + users_on_vacation = ["jyn514"] [note] @@ -387,6 +452,7 @@ mod tests { contributing_url: None, adhoc_groups: HashMap::new(), owners: HashMap::new(), + users_on_vacation: HashSet::from(["jyn514".into()]), }), note: Some(NoteConfig { _empty: () }), ping: Some(PingConfig { teams: ping_teams }), @@ -402,9 +468,11 @@ mod tests { notify_zulip: None, github_releases: None, review_submitted: None, + review_requested: None, mentions: None, no_merges: None, - decision: None + decision: None, + validate_config: Some(ValidateConfig {}), } ); } diff --git a/src/db.rs b/src/db.rs index bd3de5cc..4b671a02 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,5 +1,4 @@ -use crate::handlers::jobs::handle_job; -use crate::{db::jobs::*, handlers::Context}; +use crate::{db::jobs::*, handlers::Context, jobs::jobs}; use anyhow::Context as _; use chrono::Utc; use native_tls::{Certificate, TlsConnector}; @@ -14,10 +13,10 @@ pub mod jobs; pub mod notifications; pub mod rustc_commits; -const CERT_URL: &str = "https://s3.amazonaws.com/rds-downloads/rds-ca-2019-root.pem"; +const CERT_URL: &str = "https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem"; lazy_static::lazy_static! { - static ref CERTIFICATE_PEM: Vec = { + static ref CERTIFICATE_PEMS: Vec = { let client = reqwest::blocking::Client::new(); let resp = client .get(CERT_URL) @@ -96,12 +95,11 @@ impl ClientPool { async fn make_client() -> anyhow::Result { let db_url = std::env::var("DATABASE_URL").expect("needs DATABASE_URL"); if db_url.contains("rds.amazonaws.com") { - let cert = &CERTIFICATE_PEM[..]; - let cert = Certificate::from_pem(&cert).context("made certificate")?; - let connector = TlsConnector::builder() - .add_root_certificate(cert) - .build() - .context("built TlsConnector")?; + let mut builder = TlsConnector::builder(); + for cert in make_certificates() { + builder.add_root_certificate(cert); + } + let connector = builder.build().context("built TlsConnector")?; let connector = MakeTlsConnector::new(connector); let (db_client, connection) = match tokio_postgres::connect(&db_url, connector).await { @@ -136,6 +134,24 @@ async fn make_client() -> anyhow::Result { } } +fn make_certificates() -> Vec { + use x509_cert::der::pem::LineEnding; + use x509_cert::der::EncodePem; + + let certs = x509_cert::Certificate::load_pem_chain(&CERTIFICATE_PEMS[..]).unwrap(); + certs + .into_iter() + .map(|cert| Certificate::from_pem(cert.to_pem(LineEnding::LF).unwrap().as_bytes()).unwrap()) + .collect() +} + +// Makes sure we successfully parse the RDS certificates and load them into native-tls compatible +// format. +#[test] +fn cert() { + make_certificates(); +} + pub async fn run_migrations(client: &DbClient) -> anyhow::Result<()> { client .execute( @@ -189,16 +205,32 @@ pub async fn schedule_jobs(db: &DbClient, jobs: Vec) -> anyhow::Res let mut upcoming = job.schedule.upcoming(Utc).take(1); if let Some(scheduled_at) = upcoming.next() { - if let Err(_) = get_job_by_name_and_scheduled_at(&db, &job.name, &scheduled_at).await { - // mean there's no job already in the db with that name and scheduled_at - insert_job(&db, &job.name, &scheduled_at, &job.metadata).await?; - } + schedule_job(db, job.name, job.metadata, scheduled_at).await?; } } Ok(()) } +pub async fn schedule_job( + db: &DbClient, + job_name: &str, + job_metadata: serde_json::Value, + when: chrono::DateTime, +) -> anyhow::Result<()> { + let all_jobs = jobs(); + if !all_jobs.iter().any(|j| j.name() == job_name) { + anyhow::bail!("Job {} does not exist in the current job list.", job_name); + } + + if let Err(_) = get_job_by_name_and_scheduled_at(&db, job_name, &when).await { + // mean there's no job already in the db with that name and scheduled_at + insert_job(&db, job_name, &when, &job_metadata).await?; + } + + Ok(()) +} + pub async fn run_scheduled_jobs(ctx: &Context, db: &DbClient) -> anyhow::Result<()> { let jobs = get_jobs_to_execute(&db).await.unwrap(); tracing::trace!("jobs to execute: {:#?}", jobs); @@ -221,6 +253,26 @@ pub async fn run_scheduled_jobs(ctx: &Context, db: &DbClient) -> anyhow::Result< Ok(()) } +// Try to handle a specific job +async fn handle_job( + ctx: &Context, + name: &String, + metadata: &serde_json::Value, +) -> anyhow::Result<()> { + for job in jobs() { + if &job.name() == &name { + return job.run(ctx, metadata).await; + } + } + tracing::trace!( + "handle_job fell into default case: (name={:?}, metadata={:?})", + name, + metadata + ); + + Ok(()) +} + static MIGRATIONS: &[&str] = &[ " CREATE TABLE notifications ( @@ -269,15 +321,24 @@ CREATE TABLE jobs ( ); ", " -CREATE UNIQUE INDEX jobs_name_scheduled_at_unique_index +CREATE UNIQUE INDEX jobs_name_scheduled_at_unique_index ON jobs ( name, scheduled_at ); ", - " +" +CREATE table review_prefs ( + id UUID DEFAULT gen_random_uuid() PRIMARY KEY, + user_id BIGINT REFERENCES users(user_id), + assigned_prs INT[] NOT NULL DEFAULT array[]::INT[] +);", +" +CREATE UNIQUE INDEX review_prefs_user_id ON review_prefs(user_id); +", +" CREATE TYPE reversibility AS ENUM ('reversible', 'irreversible'); ", - " +" CREATE TYPE resolution AS ENUM ('hold', 'merge'); ", "CREATE TABLE issue_decision_state ( diff --git a/src/db/jobs.rs b/src/db/jobs.rs index 5db66b0f..cc0f3e65 100644 --- a/src/db/jobs.rs +++ b/src/db/jobs.rs @@ -7,7 +7,7 @@ use tokio_postgres::Client as DbClient; use uuid::Uuid; pub struct JobSchedule { - pub name: String, + pub name: &'static str, pub schedule: Schedule, pub metadata: serde_json::Value, } @@ -24,7 +24,7 @@ pub struct Job { pub async fn insert_job( db: &DbClient, - name: &String, + name: &str, scheduled_at: &DateTime, metadata: &serde_json::Value, ) -> Result<()> { @@ -76,7 +76,7 @@ pub async fn update_job_executed_at(db: &DbClient, id: &Uuid) -> Result<()> { pub async fn get_job_by_name_and_scheduled_at( db: &DbClient, - name: &String, + name: &str, scheduled_at: &DateTime, ) -> Result { tracing::trace!( diff --git a/src/db/notifications.rs b/src/db/notifications.rs index 5b185793..1e9f295a 100644 --- a/src/db/notifications.rs +++ b/src/db/notifications.rs @@ -4,7 +4,7 @@ use tokio_postgres::Client as DbClient; use tracing as log; pub struct Notification { - pub user_id: i64, + pub user_id: u64, pub origin_url: String, pub origin_html: String, pub short_description: Option, @@ -15,10 +15,11 @@ pub struct Notification { pub team_name: Option, } -pub async fn record_username(db: &DbClient, user_id: i64, username: String) -> anyhow::Result<()> { +/// Add a new user (if not existing) +pub async fn record_username(db: &DbClient, user_id: u64, username: &str) -> anyhow::Result<()> { db.execute( "INSERT INTO users (user_id, username) VALUES ($1, $2) ON CONFLICT DO NOTHING", - &[&user_id, &username], + &[&(user_id as i64), &username], ) .await .context("inserting user id / username")?; @@ -31,7 +32,7 @@ pub async fn record_ping(db: &DbClient, notification: &Notification) -> anyhow:: $1, $2, $3, $4, $5, $6, (SELECT max(notifications.idx) + 1 from notifications where notifications.user_id = $1) )", - &[¬ification.user_id, ¬ification.origin_url, ¬ification.origin_html, ¬ification.time, ¬ification.short_description, ¬ification.team_name], + &[&(notification.user_id as i64), ¬ification.origin_url, ¬ification.origin_html, ¬ification.time, ¬ification.short_description, ¬ification.team_name], ).await.context("inserting notification")?; Ok(()) @@ -40,14 +41,14 @@ pub async fn record_ping(db: &DbClient, notification: &Notification) -> anyhow:: #[derive(Copy, Clone)] pub enum Identifier<'a> { Url(&'a str), - Index(std::num::NonZeroUsize), + Index(std::num::NonZeroU32), /// Glob identifier (`all` or `*`). All, } pub async fn delete_ping( db: &mut DbClient, - user_id: i64, + user_id: u64, identifier: Identifier<'_>, ) -> anyhow::Result> { match identifier { @@ -56,7 +57,7 @@ pub async fn delete_ping( .query( "DELETE FROM notifications WHERE user_id = $1 and origin_url = $2 RETURNING origin_html, time, short_description, metadata", - &[&user_id, &origin_url], + &[&(user_id as i64), &origin_url], ) .await .context("delete notification query")?; @@ -91,13 +92,13 @@ pub async fn delete_ping( from notifications where user_id = $1 order by idx asc nulls last;", - &[&user_id], + &[&(user_id as i64)], ) .await .context("failed to get ordering")?; let notification_id: i64 = notifications - .get(idx.get() - 1) + .get((idx.get() - 1) as usize) .ok_or_else(|| anyhow::anyhow!("No such notification with index {}", idx.get()))? .get(0); @@ -144,7 +145,7 @@ pub async fn delete_ping( .query( "DELETE FROM notifications WHERE user_id = $1 RETURNING origin_url, origin_html, time, short_description, metadata", - &[&user_id], + &[&(user_id as i64)], ) .await .context("delete all notifications query")?; @@ -180,10 +181,12 @@ pub struct NotificationData { pub async fn move_indices( db: &mut DbClient, - user_id: i64, - from: usize, - to: usize, + user_id: u64, + from: u32, + to: u32, ) -> anyhow::Result<()> { + let from = usize::try_from(from)?; + let to = usize::try_from(to)?; loop { let t = db .build_transaction() @@ -198,7 +201,7 @@ pub async fn move_indices( from notifications where user_id = $1 order by idx asc nulls last;", - &[&user_id], + &[&(user_id as i64)], ) .await .context("failed to get initial ordering")?; @@ -257,10 +260,11 @@ pub async fn move_indices( pub async fn add_metadata( db: &mut DbClient, - user_id: i64, - idx: usize, + user_id: u64, + idx: u32, metadata: Option<&str>, ) -> anyhow::Result<()> { + let idx = usize::try_from(idx)?; loop { let t = db .build_transaction() @@ -275,7 +279,7 @@ pub async fn add_metadata( from notifications where user_id = $1 order by idx asc nulls last;", - &[&user_id], + &[&(user_id as i64)], ) .await .context("failed to get initial ordering")?; diff --git a/src/github.rs b/src/github.rs index 8b9dafbd..a7cb7e68 100644 --- a/src/github.rs +++ b/src/github.rs @@ -1,13 +1,14 @@ use anyhow::{anyhow, Context}; use async_trait::async_trait; +use bytes::Bytes; use chrono::{DateTime, FixedOffset, Utc}; use futures::{future::BoxFuture, FutureExt}; use hyper::header::HeaderValue; use once_cell::sync::OnceCell; +use regex::Regex; use reqwest::header::{AUTHORIZATION, USER_AGENT}; use reqwest::{Client, Request, RequestBuilder, Response, StatusCode}; use std::collections::{HashMap, HashSet}; -use std::convert::TryInto; use std::{ fmt, time::{Duration, SystemTime}, @@ -17,13 +18,13 @@ use tracing as log; #[derive(Debug, PartialEq, Eq, serde::Deserialize)] pub struct User { pub login: String, - pub id: Option, + pub id: Option, } impl GithubClient { - async fn _send_req(&self, req: RequestBuilder) -> anyhow::Result<(Response, String)> { - const MAX_ATTEMPTS: usize = 2; - log::debug!("_send_req with {:?}", req); + async fn send_req(&self, req: RequestBuilder) -> anyhow::Result<(Bytes, String)> { + const MAX_ATTEMPTS: u32 = 2; + log::debug!("send_req with {:?}", req); let req_dbg = format!("{:?}", req); let req = req .build() @@ -33,10 +34,17 @@ impl GithubClient { if let Some(sleep) = Self::needs_retry(&resp).await { resp = self.retry(req, sleep, MAX_ATTEMPTS).await?; } + let maybe_err = resp.error_for_status_ref().err(); + let body = resp + .bytes() + .await + .with_context(|| format!("failed to read response body {req_dbg}"))?; + if let Some(e) = maybe_err { + return Err(anyhow::Error::new(e)) + .with_context(|| format!("response: {}", String::from_utf8_lossy(&body))); + } - resp.error_for_status_ref()?; - - Ok((resp, req_dbg)) + Ok((body, req_dbg)) } async fn needs_retry(resp: &Response) -> Option { @@ -71,7 +79,7 @@ impl GithubClient { &self, req: Request, sleep: Duration, - remaining_attempts: usize, + remaining_attempts: u32, ) -> BoxFuture> { #[derive(Debug, serde::Deserialize)] struct RateLimit { @@ -110,12 +118,13 @@ impl GithubClient { .client .execute( self.client - .get("https://api.github.com/rate_limit") + .get(&format!("{}/rate_limit", self.api_url)) .configure(self) .build() .unwrap(), ) .await?; + rate_resp.error_for_status_ref()?; let rate_limit_response = rate_resp.json::().await?; // Check url for search path because github has different rate limits for the search api @@ -151,33 +160,20 @@ impl GithubClient { .boxed() } - async fn send_req(&self, req: RequestBuilder) -> anyhow::Result> { - let (mut resp, req_dbg) = self._send_req(req).await?; - - let mut body = Vec::new(); - while let Some(chunk) = resp.chunk().await.transpose() { - let chunk = chunk - .context("reading stream failed") - .map_err(anyhow::Error::from) - .context(req_dbg.clone())?; - body.extend_from_slice(&chunk); - } - - Ok(body) - } - pub async fn json(&self, req: RequestBuilder) -> anyhow::Result where T: serde::de::DeserializeOwned, { - let (resp, req_dbg) = self._send_req(req).await?; - Ok(resp.json().await.context(req_dbg)?) + let (body, _req_dbg) = self.send_req(req).await?; + Ok(serde_json::from_slice(&body)?) } } impl User { pub async fn current(client: &GithubClient) -> anyhow::Result { - client.json(client.get("https://api.github.com/user")).await + client + .json(client.get(&format!("{}/user", client.api_url))) + .await } pub async fn is_team_member<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result { @@ -206,7 +202,7 @@ impl User { } // Returns the ID of the given user, if the user is in the `all` team. - pub async fn get_id<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result> { + pub async fn get_id<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result> { let permission = crate::team_data::teams(client).await?; let map = permission.teams; Ok(map["all"] @@ -238,7 +234,30 @@ pub struct Label { /// needed at this time (merged_at, diff_url, html_url, patch_url, url). #[derive(Debug, serde::Deserialize)] pub struct PullRequestDetails { - // none for now + /// This is a slot to hold the diff for a PR. + /// + /// This will be filled in only once as an optimization since multiple + /// handlers want to see PR changes, and getting the diff can be + /// expensive. + #[serde(skip)] + files_changed: tokio::sync::OnceCell>, +} + +/// Representation of a diff to a single file. +#[derive(Debug)] +pub struct FileDiff { + /// The full path of the file. + pub path: String, + /// The diff for the file. + pub diff: String, +} + +impl PullRequestDetails { + pub fn new() -> PullRequestDetails { + PullRequestDetails { + files_changed: tokio::sync::OnceCell::new(), + } + } } /// An issue or pull request. @@ -402,18 +421,22 @@ impl fmt::Display for IssueRepository { } impl IssueRepository { - pub fn url(&self) -> String { + fn url(&self, client: &GithubClient) -> String { format!( - "https://api.github.com/repos/{}/{}", - self.organization, self.repository + "{}/repos/{}/{}", + client.api_url, self.organization, self.repository ) } + fn full_repo_name(&self) -> String { + format!("{}/{}", self.organization, self.repository) + } + async fn has_label(&self, client: &GithubClient, label: &str) -> anyhow::Result { #[allow(clippy::redundant_pattern_matching)] - let url = format!("{}/labels/{}", self.url(), label); - match client._send_req(client.get(&url)).await { - Ok((_, _)) => Ok(true), + let url = format!("{}/labels/{}", self.url(client), label); + match client.send_req(client.get(&url)).await { + Ok(_) => Ok(true), Err(e) => { if e.downcast_ref::() .map_or(false, |e| e.status() == Some(StatusCode::NOT_FOUND)) @@ -480,20 +503,34 @@ impl Issue { self.state == IssueState::Open } - pub async fn get_comment(&self, client: &GithubClient, id: usize) -> anyhow::Result { - let comment_url = format!("{}/issues/comments/{}", self.repository().url(), id); + pub async fn get_comment(&self, client: &GithubClient, id: u64) -> anyhow::Result { + let comment_url = format!("{}/issues/comments/{}", self.repository().url(client), id); let comment = client.json(client.get(&comment_url)).await?; Ok(comment) } + pub async fn get_first100_comments( + &self, + client: &GithubClient, + ) -> anyhow::Result> { + let comment_url = format!( + "{}/issues/{}/comments?page=1&per_page=100", + self.repository().url(client), + self.number, + ); + Ok(client + .json::>(client.get(&comment_url)) + .await?) + } + pub async fn edit_body(&self, client: &GithubClient, body: &str) -> anyhow::Result<()> { - let edit_url = format!("{}/issues/{}", self.repository().url(), self.number); + let edit_url = format!("{}/issues/{}", self.repository().url(client), self.number); #[derive(serde::Serialize)] struct ChangedIssue<'a> { body: &'a str, } client - ._send_req(client.patch(&edit_url).json(&ChangedIssue { body })) + .send_req(client.patch(&edit_url).json(&ChangedIssue { body })) .await .context("failed to edit issue body")?; Ok(()) @@ -502,16 +539,16 @@ impl Issue { pub async fn edit_comment( &self, client: &GithubClient, - id: usize, + id: u64, new_body: &str, ) -> anyhow::Result<()> { - let comment_url = format!("{}/issues/comments/{}", self.repository().url(), id); + let comment_url = format!("{}/issues/comments/{}", self.repository().url(client), id); #[derive(serde::Serialize)] struct NewComment<'a> { body: &'a str, } client - ._send_req( + .send_req( client .patch(&comment_url) .json(&NewComment { body: new_body }), @@ -526,8 +563,13 @@ impl Issue { struct PostComment<'a> { body: &'a str, } + let comments_path = self + .comments_url + .strip_prefix("https://api.github.com") + .expect("expected api host"); + let comments_url = format!("{}{comments_path}", client.api_url); client - ._send_req(client.post(&self.comments_url).json(&PostComment { body })) + .send_req(client.post(&comments_url).json(&PostComment { body })) .await .context("failed to post comment")?; Ok(()) @@ -538,7 +580,7 @@ impl Issue { // DELETE /repos/:owner/:repo/issues/:number/labels/{name} let url = format!( "{repo_url}/issues/{number}/labels/{name}", - repo_url = self.repository().url(), + repo_url = self.repository().url(client), number = self.number, name = label, ); @@ -553,7 +595,7 @@ impl Issue { } client - ._send_req(client.delete(&url)) + .send_req(client.delete(&url)) .await .context("failed to delete label")?; @@ -570,7 +612,7 @@ impl Issue { // repo_url = https://api.github.com/repos/Codertocat/Hello-World let url = format!( "{repo_url}/issues/{number}/labels", - repo_url = self.repository().url(), + repo_url = self.repository().url(client), number = self.number ); @@ -610,7 +652,7 @@ impl Issue { } client - ._send_req(client.post(&url).json(&LabelsReq { + .send_req(client.post(&url).json(&LabelsReq { labels: known_labels, })) .await @@ -637,7 +679,7 @@ impl Issue { log::info!("remove {:?} assignees for {}", selection, self.global_id()); let url = format!( "{repo_url}/issues/{number}/assignees", - repo_url = self.repository().url(), + repo_url = self.repository().url(client), number = self.number ); @@ -661,7 +703,7 @@ impl Issue { assignees: &'a [&'a str], } client - ._send_req(client.delete(&url).json(&AssigneeReq { + .send_req(client.delete(&url).json(&AssigneeReq { assignees: &assignees[..], })) .await @@ -677,7 +719,7 @@ impl Issue { log::info!("add_assignee {} for {}", user, self.global_id()); let url = format!( "{repo_url}/issues/{number}/assignees", - repo_url = self.repository().url(), + repo_url = self.repository().url(client), number = self.number ); @@ -716,6 +758,10 @@ impl Issue { Ok(()) } + /// Sets the milestone of the issue or PR. + /// + /// This will create the milestone if it does not exist. The new milestone + /// will start in the "open" state. pub async fn set_milestone(&self, client: &GithubClient, title: &str) -> anyhow::Result<()> { log::trace!( "Setting milestone for rust-lang/rust#{} to {}", @@ -723,53 +769,25 @@ impl Issue { title ); - let create_url = format!("{}/milestones", self.repository().url()); - let resp = client - .send_req( - client - .post(&create_url) - .body(serde_json::to_vec(&MilestoneCreateBody { title }).unwrap()), - ) - .await; - // Explicitly do *not* try to return Err(...) if this fails -- that's - // fine, it just means the milestone was already created. - log::trace!("Created milestone: {:?}", resp); - - let list_url = format!("{}/milestones", self.repository().url()); - let milestone_list: Vec = client.json(client.get(&list_url)).await?; - let milestone_no = if let Some(milestone) = milestone_list.iter().find(|v| v.title == title) - { - milestone.number - } else { - anyhow::bail!( - "Despite just creating milestone {} on {}, it does not exist?", - title, - self.repository() - ) - }; + let full_repo_name = self.repository().full_repo_name(); + let milestone = client + .get_or_create_milestone(&full_repo_name, title, "open") + .await?; - #[derive(serde::Serialize)] - struct SetMilestone { - milestone: u64, - } - let url = format!("{}/issues/{}", self.repository().url(), self.number); client - ._send_req(client.patch(&url).json(&SetMilestone { - milestone: milestone_no, - })) - .await - .context("failed to set milestone")?; + .set_milestone(&full_repo_name, &milestone, self.number) + .await?; Ok(()) } pub async fn close(&self, client: &GithubClient) -> anyhow::Result<()> { - let edit_url = format!("{}/issues/{}", self.repository().url(), self.number); + let edit_url = format!("{}/issues/{}", self.repository().url(client), self.number); #[derive(serde::Serialize)] struct CloseIssue<'a> { state: &'a str, } client - ._send_req( + .send_req( client .patch(&edit_url) .json(&CloseIssue { state: "closed" }), @@ -780,22 +798,36 @@ impl Issue { } /// Returns the diff in this event, for Open and Synchronize events for now. - pub async fn diff(&self, client: &GithubClient) -> anyhow::Result> { + /// + /// Returns `None` if the issue is not a PR. + pub async fn diff(&self, client: &GithubClient) -> anyhow::Result> { + let Some(pr) = &self.pull_request else { + return Ok(None); + }; let (before, after) = if let (Some(base), Some(head)) = (&self.base, &self.head) { - (base.sha.clone(), head.sha.clone()) + (&base.sha, &head.sha) } else { return Ok(None); }; - let mut req = client.get(&format!( - "{}/compare/{}...{}", - self.repository().url(), - before, - after - )); - req = req.header("Accept", "application/vnd.github.v3.diff"); - let diff = client.send_req(req).await?; - Ok(Some(String::from(String::from_utf8_lossy(&diff)))) + let diff = pr + .files_changed + .get_or_try_init::(|| async move { + let url = format!( + "{}/compare/{before}...{after}", + self.repository().url(client) + ); + let mut req = client.get(&url); + req = req.header("Accept", "application/vnd.github.v3.diff"); + let (diff, _) = client + .send_req(req) + .await + .with_context(|| format!("failed to fetch diff comparison for {url}"))?; + let body = String::from_utf8_lossy(&diff); + Ok(parse_diff(&body)) + }) + .await?; + Ok(Some(diff)) } /// Returns the commits from this pull request (no commits are returned if this `Issue` is not @@ -810,7 +842,7 @@ impl Issue { loop { let req = client.get(&format!( "{}/pulls/{}/commits?page={page}&per_page=100", - self.repository().url(), + self.repository().url(client), self.number )); @@ -832,7 +864,7 @@ impl Issue { let req = client.get(&format!( "{}/pulls/{}/files", - self.repository().url(), + self.repository().url(client), self.number )); Ok(client.json(req).await?) @@ -846,11 +878,6 @@ pub struct PullRequestFile { pub blob_url: String, } -#[derive(serde::Serialize)] -struct MilestoneCreateBody<'a> { - title: &'a str, -} - #[derive(Debug, serde::Deserialize)] pub struct Milestone { number: u64, @@ -916,7 +943,7 @@ pub struct IssueCommentEvent { } #[derive(PartialEq, Eq, Debug, serde::Deserialize)] -#[serde(rename_all = "snake_case")] +#[serde(rename_all = "snake_case", tag = "action")] pub enum IssuesAction { Opened, Edited, @@ -928,13 +955,22 @@ pub enum IssuesAction { Reopened, Assigned, Unassigned, - Labeled, - Unlabeled, + Labeled { + /// The label added from the issue + label: Label, + }, + Unlabeled { + /// The label removed from the issue + label: Label, + }, Locked, Unlocked, Milestoned, Demilestoned, - ReviewRequested, + ReviewRequested { + /// The person requested to review the pull request + requested_reviewer: User, + }, ReviewRequestRemoved, ReadyForReview, Synchronize, @@ -945,13 +981,14 @@ pub enum IssuesAction { #[derive(Debug, serde::Deserialize)] pub struct IssuesEvent { + #[serde(flatten)] pub action: IssuesAction, #[serde(alias = "pull_request")] pub issue: Issue, pub changes: Option, pub repository: Repository, - /// Some if action is IssuesAction::Labeled, for example - pub label: Option