diff --git a/Cargo.lock b/Cargo.lock index 932ccdf..370bf27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,18 +67,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" version = "1.5.0" @@ -102,9 +90,15 @@ dependencies = [ [[package]] name = "base64" -version = "0.22.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" @@ -319,17 +313,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.3.3" @@ -352,7 +335,6 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" name = "gitea_mirror" version = "0.1.0" dependencies = [ - "anyhow", "clap", "reqwest", "serde", @@ -361,20 +343,19 @@ dependencies = [ "toml", "tracing", "tracing-subscriber", - "url", ] [[package]] name = "h2" -version = "0.4.12" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ - "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", + "futures-util", "http", "indexmap", "slab", @@ -397,9 +378,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "http" -version = "1.3.1" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -408,24 +389,12 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", "pin-project-lite", ] @@ -435,84 +404,47 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" -version = "1.7.0" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" -dependencies = [ - "base64", "bytes", "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", + "httparse", + "httpdate", + "itoa", "pin-project-lite", - "socket2", - "system-configuration", + "socket2 0.5.10", "tokio", "tower-service", "tracing", - "windows-registry", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", ] [[package]] @@ -638,7 +570,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags", + "bitflags 2.9.4", "cfg-if", "libc", ] @@ -649,16 +581,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" -[[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -806,7 +728,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags", + "bitflags 2.9.4", "cfg-if", "foreign-types", "libc", @@ -930,61 +852,47 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags", + "bitflags 2.9.4", ] [[package]] name = "reqwest" -version = "0.12.23" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64", "bytes", "encoding_rs", "futures-core", + "futures-util", "h2", "http", "http-body", - "http-body-util", "hyper", - "hyper-rustls", "hyper-tls", - "hyper-util", + "ipnet", "js-sys", "log", "mime", "native-tls", + "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pki-types", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", - "tower", - "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.16", - "libc", - "untrusted", - "windows-sys 0.52.0", + "winreg", ] [[package]] @@ -999,7 +907,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys", @@ -1007,36 +915,12 @@ dependencies = [ ] [[package]] -name = "rustls" -version = "0.23.32" +name = "rustls-pemfile" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "base64", ] [[package]] @@ -1072,7 +956,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.9.4", "core-foundation", "core-foundation-sys", "libc", @@ -1134,11 +1018,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.2" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ - "serde_core", + "serde", ] [[package]] @@ -1189,6 +1073,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.0" @@ -1211,12 +1105,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "2.0.106" @@ -1230,12 +1118,9 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "1.0.2" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "synstructure" @@ -1250,20 +1135,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ "core-foundation-sys", "libc", @@ -1276,7 +1161,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom", "once_cell", "rustix", "windows-sys 0.61.0", @@ -1316,7 +1201,7 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "slab", - "socket2", + "socket2 0.6.0", "tokio-macros", "windows-sys 0.59.0", ] @@ -1342,16 +1227,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" -dependencies = [ - "rustls", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.16" @@ -1367,81 +1242,44 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.7" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ - "indexmap", - "serde_core", + "serde", "serde_spanned", "toml_datetime", - "toml_parser", - "toml_writer", - "winnow", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ - "serde_core", + "serde", ] [[package]] -name = "toml_parser" -version = "1.0.3" +name = "toml_edit" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", "winnow", ] [[package]] -name = "toml_writer" -version = "1.0.3" +name = "toml_write" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "tower-service" @@ -1518,12 +1356,6 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "url" version = "2.5.7" @@ -1688,32 +1520,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" [[package]] -name = "windows-registry" -version = "0.5.3" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-link 0.1.3", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", + "windows-targets 0.48.5", ] [[package]] @@ -1752,6 +1564,21 @@ dependencies = [ "windows-link 0.2.0", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1785,6 +1612,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[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_gnullvm" version = "0.52.6" @@ -1797,6 +1630,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -1809,6 +1648,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[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_gnu" version = "0.52.6" @@ -1833,6 +1678,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -1845,6 +1696,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[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_gnu" version = "0.52.6" @@ -1857,6 +1714,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[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_gnullvm" version = "0.52.6" @@ -1869,6 +1732,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -1886,6 +1755,19 @@ name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] [[package]] name = "wit-bindgen" @@ -1944,12 +1826,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - [[package]] name = "zerotrie" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index c62d4ec..ad2979b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,13 +4,11 @@ version = "0.1.0" edition = "2024" [dependencies] -anyhow = "1.0.100" -clap = { version = "4.5", features = ["derive", "env"] } -reqwest = { version = "0.12.23", features = ["json"] } +clap = { version = "4.0", features = ["derive"] } +tokio = { version = "1", features = ["full"] } +reqwest = { version = "0.11", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -tokio = { version = "1.35", features = ["full"] } -toml = "0.9.7" +toml = "0.8" tracing = "0.1" tracing-subscriber = "0.3" -url = "2.5.7" diff --git a/src/main.rs b/src/main.rs index fb2318d..c944604 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,229 +1,326 @@ -use anyhow::{Context, Result}; -use clap::Parser; -use reqwest::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE, USER_AGENT}; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; -use std::fs; -use std::path::PathBuf; -use tracing::{error, info, warn}; -use url::Url; +//! # Gitea Mirror +//! +//! A simple command-line tool to ensure a list of remote git repositories are mirrored to a Gitea instance. +//! It checks if a repository already exists and, if not, creates a mirror migration. -// --- Structs (Unchanged) --- +use clap::Parser; +use serde::Deserialize; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; +use tracing::{Level, error, info, instrument, warn}; +use tracing_subscriber; + +// Represents the command-line arguments. #[derive(Parser, Debug)] -#[command( - author, - version, - about = "A simple tool to ensure git repositories are mirrored to Gitea." -)] -struct Cli { - #[arg(short, long, env = "GITEA_MIRROR_CONFIG")] +#[clap(author, version, about, long_about = None)] +struct Args { + /// Path to the TOML configuration file. + #[clap(short, long, value_parser)] config: PathBuf, - #[arg(long)] + + /// Perform a dry run without creating any migrations. + #[clap(short, long)] dry_run: bool, } -#[derive(Deserialize, Debug)] -struct RepoToMirror { +// Represents a single repository entry in the config file. +#[derive(Deserialize, Debug, Clone)] +struct RepoConfig { url: String, rename: Option, } +// Represents the main structure of the TOML configuration file. #[derive(Deserialize, Debug)] struct Config { gitea_url: String, api_key: String, - repos: Vec, + repos: Option>, + organizations: Option>, } -// --- Gitea API Structs (Corrected) --- -#[derive(Deserialize, Debug)] -struct GiteaUser { - id: i64, - login: String, -} - -// **MODIFIED**: This struct now includes `name` and the correct `mirror_url` field. -#[derive(Deserialize, Debug)] -struct GiteaRepo { - name: String, - mirror: bool, - mirror_url: Option, // The original source URL of the mirror -} - -#[derive(Serialize, Debug)] -struct MigrationRequest<'a> { +// Represents the payload for creating a migration in Gitea. +#[derive(serde::Serialize, Debug)] +struct MigrateRepoPayload<'a> { clone_addr: &'a str, - uid: i64, repo_name: &'a str, mirror: bool, private: bool, - description: String, + description: &'a str, + uid: i64, // The user ID of the owner. We'll fetch this. } +// Represents a user as returned by the Gitea API. +#[derive(Deserialize, Debug)] +struct GiteaUser { + id: i64, +} + +/// Entry point of the application. #[tokio::main] -async fn main() -> Result<()> { - tracing_subscriber::fmt::init(); - let cli = Cli::parse(); +async fn main() -> Result<(), Box> { + // Initialize the tracing subscriber for logging. + tracing_subscriber::fmt().with_max_level(Level::INFO).init(); - let config_content = fs::read_to_string(&cli.config) - .with_context(|| format!("Failed to read config file at {:?}", cli.config))?; - let config: Config = - toml::from_str(&config_content).context("Failed to parse TOML configuration")?; + // Parse command-line arguments or get config path from environment variable. + let args = match Args::try_parse() { + Ok(args) => args, + Err(_) => { + // If parsing fails, check for the environment variable. + if let Ok(config_path) = env::var("GITEA_MIRROR_CONFIG") { + Args { + config: PathBuf::from(config_path), + // Check for a dry-run env var as well, defaulting to false. + dry_run: env::var("GITEA_MIRROR_DRY_RUN") + .unwrap_or_else(|_| "false".to_string()) + .parse() + .unwrap_or(false), + } + } else { + // If no env var, show help and exit. + Args::parse() + } + } + }; - if cli.dry_run { - info!("Performing a dry run. No migrations will be created."); + info!("Starting Gitea mirror process. Dry run: {}", args.dry_run); + + // Read and parse the configuration file. + let config = load_config(&args.config)?; + let http_client = reqwest::Client::new(); + + // Fetch the Gitea user ID for the authenticated user. + let user_id = get_gitea_user_id(&http_client, &config.gitea_url, &config.api_key).await?; + info!( + "Successfully authenticated and retrieved user ID: {}", + user_id + ); + + // Process repositories from the static list. + if let Some(repos) = &config.repos { + for repo_config in repos { + process_repo( + &repo_config.url, + repo_config.rename.as_deref(), + user_id, + &http_client, + &config, + args.dry_run, + ) + .await?; + } } - let mut headers = reqwest::header::HeaderMap::new(); - headers.insert(ACCEPT, "application/json".parse()?); - headers.insert(CONTENT_TYPE, "application/json".parse()?); - headers.insert(USER_AGENT, "gitea-mirror-tool/0.1.0".parse()?); - headers.insert(AUTHORIZATION, format!("token {}", config.api_key).parse()?); - let client = reqwest::Client::builder() - .default_headers(headers) - .build()?; + // Process repositories from the organizations/users list. + if let Some(org_urls) = &config.organizations { + for org_url in org_urls { + info!("Fetching repositories from organization: {}", org_url); + match fetch_org_repos(&http_client, org_url, &config.api_key).await { + Ok(repo_urls) => { + info!("Found {} repositories for {}", repo_urls.len(), org_url); + for url in repo_urls { + process_repo( + &url, + None, // No rename support for orgs + user_id, + &http_client, + &config, + args.dry_run, + ) + .await?; + } + } + Err(e) => error!("Failed to fetch repos from {}: {}", org_url, e), + } + } + } - info!("🔗 Connecting to Gitea instance at {}", config.gitea_url); + info!("Gitea mirror process completed."); + Ok(()) +} - let user_url = format!("{}/api/v1/user", config.gitea_url); - let user = client - .get(&user_url) +/// Loads and parses the TOML configuration file. +#[instrument(skip(path))] +fn load_config(path: &Path) -> Result> { + info!("Loading configuration from: {:?}", path); + let content = fs::read_to_string(path)?; + let config: Config = toml::from_str(&content)?; + Ok(config) +} + +/// Fetches the authenticated user's ID from Gitea. +#[instrument(skip(http_client, gitea_url, api_key))] +async fn get_gitea_user_id( + http_client: &reqwest::Client, + gitea_url: &str, + api_key: &str, +) -> Result { + let url = format!("{}/api/v1/user", gitea_url); + let user: GiteaUser = http_client + .get(&url) + .header("Authorization", format!("token {}", api_key)) .send() .await? .error_for_status()? - .json::() - .await - .context("Failed to get Gitea user info. Check your API key and Gitea URL.")?; - info!("Authenticated as user '{}' (ID: {})", user.login, user.id); + .json() + .await?; + Ok(user.id) +} - // **MODIFIED**: We now build two sets: one for source URLs and one for existing repo names. - info!("🔍 Fetching all existing repositories to build a local cache..."); - let mut existing_mirror_sources: HashSet = HashSet::new(); - let mut existing_repo_names: HashSet = HashSet::new(); +/// Checks if a repository already exists in Gitea for the user. +#[instrument(skip(http_client, gitea_url, api_key))] +async fn repo_exists( + http_client: &reqwest::Client, + gitea_url: &str, + api_key: &str, + repo_name: &str, +) -> Result { + let url = format!("{}/api/v1/repos/search", gitea_url); + let response: serde_json::Value = http_client + .get(&url) + .query(&[("q", repo_name), ("limit", "1")]) + .header("Authorization", format!("token {}", api_key)) + .send() + .await? + .error_for_status()? + .json() + .await?; + + if let Some(data) = response.get("data").and_then(|d| d.as_array()) { + for repo in data { + if let Some(name) = repo.get("name").and_then(|n| n.as_str()) { + if name.eq_ignore_ascii_case(repo_name) { + return Ok(true); + } + } + } + } + + Ok(false) +} + +/// Creates a mirror migration in Gitea. +#[instrument(skip(http_client, config, payload))] +async fn create_migration( + http_client: &reqwest::Client, + config: &Config, + payload: &MigrateRepoPayload<'_>, +) -> Result<(), reqwest::Error> { + let url = format!("{}/api/v1/repos/migrate", config.gitea_url); + http_client + .post(&url) + .header("Authorization", format!("token {}", config.api_key)) + .json(payload) + .send() + .await? + .error_for_status()?; + Ok(()) +} + +/// Fetches all repository clone URLs from a given Gitea/GitHub organization/user page. +#[instrument(skip(http_client, api_key))] +async fn fetch_org_repos( + http_client: &reqwest::Client, + org_url: &str, + api_key: &str, +) -> Result, Box> { + // This is a simplified fetcher. It assumes Gitea API compatibility. + // For GitHub, you might need a different base URL and auth method. + let api_url = if org_url.contains("github.com") { + let parts: Vec<&str> = org_url.trim_end_matches('/').split('/').collect(); + let user_or_org = parts.last().ok_or("Invalid GitHub URL")?; + format!("https://api.github.com/users/{}/repos", user_or_org) + } else { + // Assuming Gitea-like URL structure + let parts: Vec<&str> = org_url.trim_end_matches('/').split('/').collect(); + let user_or_org = parts.last().ok_or("Invalid Gitea URL")?; + format!( + "{}s/{}/repos", + org_url.replace(user_or_org, &format!("api/v1/user")), + user_or_org + ) + }; + + info!("Querying API endpoint: {}", api_url); + + let mut repos: Vec = Vec::new(); let mut page = 1; loop { - let repos_url = format!("{}/api/v1/user/repos", config.gitea_url); - let repos_on_page = client - .get(&repos_url) - .query(&[("limit", "50"), ("page", &page.to_string())]) + let response: Vec = http_client + .get(&api_url) + .query(&[("page", page.to_string())]) + // For GitHub, a User-Agent is required. + .header("User-Agent", "gitea-mirror-rust-client") + .header("Authorization", format!("token {}", api_key)) .send() .await? .error_for_status()? - .json::>() - .await - .context("Failed to fetch a page of existing repositories.")?; + .json() + .await?; - if repos_on_page.is_empty() { - break; + if response.is_empty() { + break; // No more pages } - for repo in repos_on_page { - // Add the name of EVERY repo to prevent any name collisions. - existing_repo_names.insert(repo.name); - - // If it's a mirror, store its ORIGINAL source URL for an exact match. - if repo.mirror { - if let Some(mirror_url) = repo.mirror_url { - existing_mirror_sources.insert(mirror_url); - } + for repo in response { + if let Some(clone_url) = repo.get("clone_url").and_then(|u| u.as_str()) { + repos.push(clone_url.to_string()); } } page += 1; } - info!( - "Found {} existing repositories and {} configured mirrors.", - existing_repo_names.len(), - existing_mirror_sources.len() - ); + Ok(repos) +} - // **MODIFIED**: The main checking logic is now much more robust. - for repo_config in &config.repos { - let url_to_mirror = &repo_config.url; +/// Core logic to process a single repository. +#[instrument(skip(user_id, http_client, config, dry_run))] +async fn process_repo( + repo_url: &str, + rename: Option<&str>, + user_id: i64, + http_client: &reqwest::Client, + config: &Config, + dry_run: bool, +) -> Result<(), Box> { + let repo_name = match rename { + Some(name) => name, + None => extract_repo_name(repo_url).ok_or("Could not extract repo name from URL")?, + }; - // CHECK 1: Has this exact source URL already been mirrored? - if existing_mirror_sources.contains(url_to_mirror) { - info!( - "Mirror for source URL '{}' already exists. Skipping.", - url_to_mirror - ); - continue; - } + info!("Processing repo '{}' -> '{}'", repo_url, repo_name); - // Determine the target name for the new repository. - let target_repo_name = match &repo_config.rename { - Some(name) => name.clone(), - None => get_repo_name_from_url(url_to_mirror).with_context(|| { - format!("Could not parse repo name from URL: {}", url_to_mirror) - })?, - }; - - // CHECK 2: Will creating this mirror cause a name collision? - if existing_repo_names.contains(&target_repo_name) { - warn!( - "Cannot create mirror for '{}'. A repository named '{}' already exists. Skipping.", - url_to_mirror, target_repo_name - ); - continue; - } - - // If both checks pass, we are clear to create the migration. - info!( - "Mirror for '{}' not found and name '{}' is available. Needs creation.", - url_to_mirror, target_repo_name - ); - - if cli.dry_run { - warn!( - "--dry-run enabled, skipping migration for '{}'.", - url_to_mirror - ); - continue; - } - - let migration_payload = MigrationRequest { - clone_addr: url_to_mirror, - uid: user.id, - repo_name: &target_repo_name, - mirror: true, - private: false, - description: format!("Mirror of {}", url_to_mirror), - }; - - info!( - "🚀 Creating migration for '{}' as new repo '{}'...", - url_to_mirror, target_repo_name - ); - - let migrate_url = format!("{}/api/v1/repos/migrate", config.gitea_url); - let response = client - .post(&migrate_url) - .json(&migration_payload) - .send() - .await?; - - if response.status().is_success() { - info!("Successfully initiated migration for '{}'.", url_to_mirror); + if repo_exists(http_client, &config.gitea_url, &config.api_key, repo_name).await? { + info!("Repo '{}' already exists. Skipping.", repo_name); + } else { + warn!("Repo '{}' does not exist. Migration needed.", repo_name); + if !dry_run { + info!("Initiating migration for '{}'...", repo_name); + let payload = MigrateRepoPayload { + clone_addr: repo_url, + repo_name, + mirror: true, + private: false, // Defaulting to public, change if needed + description: "", + uid: user_id, + }; + if let Err(e) = create_migration(http_client, config, &payload).await { + error!("Failed to create migration for '{}': {}", repo_name, e); + } else { + info!("Successfully started migration for '{}'.", repo_name); + } } else { - let status = response.status(); - let error_body = response - .text() - .await - .unwrap_or_else(|_| "Could not read error body".to_string()); - error!( - "Failed to create migration for '{}'. Status: {}. Body: {}", - url_to_mirror, status, error_body + info!( + "Dry run enabled. Skipping actual migration for '{}'.", + repo_name ); } } - - info!("All tasks completed."); Ok(()) } -fn get_repo_name_from_url(git_url: &str) -> Option { - Url::parse(git_url) - .ok() - .and_then(|url| url.path_segments()?.last().map(|s| s.to_string())) - .map(|name| name.strip_suffix(".git").unwrap_or(&name).to_string()) +/// Extracts a repository name from a git URL (e.g., "https://.../repo.git" -> "repo"). +fn extract_repo_name(url: &str) -> Option<&str> { + url.split('/').last().map(|s| s.trim_end_matches(".git")) }