Compare commits
5
Commits
f6071f73b5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21749a62ea
|
||
|
|
947b3c2eda
|
||
|
|
9992a2bff0 | ||
|
|
c5ac78a310 | ||
|
|
0f230a1a4f
|
No files matched your search
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v0.8.1 (2026-10-12)
|
||||
|
||||
### Feat
|
||||
|
||||
- all CLI-based errors now come out in the same JSON format to further improve scripting and LLM integration
|
||||
|
||||
### Fix
|
||||
|
||||
- never panic when the log directory is unwritable; disable file logging instead [#67]
|
||||
|
||||
## v0.8.0 (2026-09-30)
|
||||
|
||||
### Feat
|
||||
|
||||
Generated
+159
-106
@@ -158,7 +158,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -184,9 +184,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
version = "1.18.1"
|
||||
version = "1.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e"
|
||||
checksum = "faac5829c2b74c28f830747e7818ccfb684261b5f48a1118b1e2a13d36dfab13"
|
||||
dependencies = [
|
||||
"aws-lc-sys",
|
||||
"zeroize",
|
||||
@@ -194,9 +194,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-sys"
|
||||
version = "0.45.0"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27"
|
||||
checksum = "f1622d8446a2d4b2ce0c7eefc73dd43a99779028d5ee5c2dd8073a658ba8a2bc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cmake",
|
||||
@@ -252,11 +252,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
checksum = "56d87354e4229f54a44f7bf2435906a4656dba36026ab6eaca629a2c436a691c"
|
||||
dependencies = [
|
||||
"bit-vec 0.8.0",
|
||||
"bit-vec 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -267,9 +267,13 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
checksum = "5727b15fa97d4f4fee0a3b7c3d550ed0269f54329207b86388de918604e31269"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
@@ -301,6 +305,30 @@ dependencies = [
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a"
|
||||
dependencies = [
|
||||
"borsh-derive",
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-derive"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12cdfe656708a01f89b451a7d36466e6fe6c414de0aa18fc54f864f6f9ca9f56"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.13.1"
|
||||
@@ -353,9 +381,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.5.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f360145194ee8e21db5ee7f3fcd4fe52210864c75c985dae33218202c8bbe040"
|
||||
checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -441,7 +469,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -742,7 +770,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -775,7 +803,7 @@ checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785"
|
||||
dependencies = [
|
||||
"darling_core 0.24.1",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -911,7 +939,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -937,9 +965,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
|
||||
checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
@@ -1147,7 +1175,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1244,9 +1272,9 @@ checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.19"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
|
||||
checksum = "7d29020232d6aa3fb1daca64c1127cf662cf97f254ae16c18c05b8ab635fc118"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -1373,9 +1401,9 @@ checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.11.1"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43"
|
||||
checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -1593,18 +1621,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.7"
|
||||
version = "2.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
||||
checksum = "a37b2691796cffeb8a8cd305ac66e65841559f147f4e63231d0eafa4db5384d1"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.48.0"
|
||||
version = "1.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0f8fee8c926415c58d6ae43a08523a26faccb2323f5e6b644fe7dd4ef6b82"
|
||||
checksum = "b67d3d2e287e4b86c10b3f3b641033d1f89b74bdb39d05f34952e2b9a6fe21cd"
|
||||
dependencies = [
|
||||
"console 0.16.6",
|
||||
"once_cell",
|
||||
@@ -1622,7 +1650,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1741,15 +1769,15 @@ checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.189"
|
||||
version = "0.2.190"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||
checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -1795,9 +1823,9 @@ checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -1869,17 +1897,17 @@ checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f"
|
||||
|
||||
[[package]]
|
||||
name = "mac_address"
|
||||
version = "1.1.8"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303"
|
||||
checksum = "f3d0581a75c45969c63afd2bb87b1d8b25cfcc556c7a918cf9bb13b0fe8d2381"
|
||||
dependencies = [
|
||||
"nix 0.29.0",
|
||||
"winapi",
|
||||
"nix 0.31.3",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "managarr"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
@@ -1984,9 +2012,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.2.3"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8"
|
||||
checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@@ -2067,7 +2095,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2080,6 +2107,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2144,9 +2172,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
|
||||
checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
@@ -2332,19 +2360,21 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.9.2"
|
||||
version = "2.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45d3aca230fad2e6f6317ca0a72724338c4960cb97168a85cdee66df4a9a21a8"
|
||||
checksum = "b568374ba38b33a6c627141f891faf16902b08d2db26b8ede1bcb0a15b1919fa"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"psm",
|
||||
"stacker",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.9.2"
|
||||
version = "2.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284b60557f2c4a2e72ad3f2d34d42685a2fa4a6a61d0d2a10c0ae2a5e916c2cf"
|
||||
checksum = "b66e184b924cebaaff20ab2256ca52f12332d528a39aa76553b5d96f92aacf7f"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -2352,22 +2382,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.9.2"
|
||||
version = "2.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d9d1f08a115309ee99268cf85e5228e0e56aa9caf8841ec12866b6be07c3109"
|
||||
checksum = "a87478d267e4de54a626af9754f2f0f58e927aac6ed0575fe89bc05ad6851694"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.9.2"
|
||||
version = "2.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed93ba1a9ffcca32130a5188701c81c0c49cf00d4b7c5007d5148951d743adcb"
|
||||
checksum = "4f986f248b4241ac359b831f6139aaa34e03b08a37b6caf7e201a33f95c869e1"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
@@ -2453,9 +2483,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
@@ -2523,16 +2553,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.11.0"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
|
||||
checksum = "b8530004ccb15eae51c7e40009fbe317f341f804db54dc033eec1c50be28cfa0"
|
||||
dependencies = [
|
||||
"bit-set 0.8.0",
|
||||
"bit-vec 0.8.0",
|
||||
"bit-set 0.11.1",
|
||||
"bit-vec 0.10.1",
|
||||
"bitflags 2.13.2",
|
||||
"chacha20",
|
||||
"core_detect",
|
||||
"num-traits",
|
||||
"rand 0.9.5",
|
||||
"rand_chacha",
|
||||
"rand 0.10.3",
|
||||
"rand_xorshift",
|
||||
"regex-syntax",
|
||||
"rusty-fork",
|
||||
@@ -2540,6 +2571,15 @@ dependencies = [
|
||||
"unarray",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
@@ -2696,11 +2736,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||
checksum = "60aa6af80be32871323012e02e6e65f8a7cc7890931ae421d217ad8fe0df2ccf"
|
||||
dependencies = [
|
||||
"rand_core 0.9.5",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3179,14 +3219,14 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.151"
|
||||
version = "1.0.154"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -3197,9 +3237,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@@ -3338,9 +3378,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.16.2"
|
||||
version = "1.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e"
|
||||
checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
@@ -3358,6 +3398,19 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -3443,9 +3496,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.6"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
|
||||
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3469,7 +3522,7 @@ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3649,7 +3702,7 @@ checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3701,9 +3754,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.53.1"
|
||||
version = "1.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
|
||||
checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@@ -3724,7 +3777,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3739,9 +3792,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.19"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
|
||||
checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -3753,9 +3806,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.6+spec-1.1.0"
|
||||
version = "1.1.8+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
|
||||
checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
@@ -3765,18 +3818,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.25.15+spec-1.1.0"
|
||||
version = "0.25.17+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614"
|
||||
checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -3786,18 +3839,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
version = "1.1.5+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||
checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
@@ -3898,9 +3951,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
@@ -3984,9 +4037,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.26.1"
|
||||
version = "1.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce"
|
||||
checksum = "7cc1186384beb7dd8eedea376413fd654937285ea6c9cfbb928dc3043ea4b606"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"getrandom 0.4.3",
|
||||
@@ -4076,9 +4129,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
@@ -4141,7 +4194,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4544,24 +4597,24 @@ checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.59"
|
||||
version = "0.8.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6df92bf3d9227be3d53173901ddbffac2babc27ae50f397776ffd6dc33f800cb"
|
||||
checksum = "86502bf56ac7c77571a32e2647bb2a15894565e981fb2a48d7bde2d91c965a9d"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.59"
|
||||
version = "0.8.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac4f328cf2f05d084e496c3e9c3f33ed0a183656a16e1fcec4d464d8373aec82"
|
||||
checksum = "5457206954b06561e2608c7e19cf58b1926586d999c246eebe4502f7e2039d1a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4585,15 +4638,15 @@ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.9.0"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||
checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
@@ -4625,7 +4678,7 @@ checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "managarr"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||
description = "A TUI and CLI to manage your Servarrs"
|
||||
keywords = ["managarr", "ratatui", "dashboard", "servarr", "tui"]
|
||||
|
||||
+2
-2
@@ -51,8 +51,8 @@ pub enum Command {
|
||||
},
|
||||
|
||||
#[command(about = indoc!{"
|
||||
Print the full path to the default configuration file.
|
||||
This file can be changed to another location using the '--config-file' flag
|
||||
Print the full path to the configuration file in use.
|
||||
This is the default configuration file location unless overridden with the '--config-file' flag
|
||||
"})]
|
||||
ConfigPath,
|
||||
}
|
||||
|
||||
+6
-2
@@ -131,7 +131,10 @@ async fn main() -> Result<()> {
|
||||
let default_config_path = config_file_path.display().to_string();
|
||||
|
||||
if matches!(args.command, Some(Command::ConfigPath)) {
|
||||
println!("{default_config_path}");
|
||||
match args.global.config_file {
|
||||
Some(ref config_file) => println!("{}", config_file.display()),
|
||||
None => println!("{default_config_path}"),
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -153,7 +156,6 @@ async fn main() -> Result<()> {
|
||||
config.validate(&config_path);
|
||||
config.post_process_initialization();
|
||||
|
||||
let reqwest_client = build_network_client(&config);
|
||||
let (sync_network_tx, sync_network_rx) = mpsc::channel(500);
|
||||
let cancellation_token = CancellationToken::new();
|
||||
let ctrlc_cancellation_token = cancellation_token.clone();
|
||||
@@ -185,6 +187,7 @@ async fn main() -> Result<()> {
|
||||
match args.command {
|
||||
Some(command) => match command {
|
||||
Command::Radarr(_) | Command::Sonarr(_) | Command::Lidarr(_) | Command::Readarr(_) => {
|
||||
let reqwest_client = build_network_client(&config);
|
||||
if spinner_disabled {
|
||||
start_cli_no_spinner(
|
||||
config,
|
||||
@@ -219,6 +222,7 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let reqwest_client = build_network_client(&config);
|
||||
let app_nw = Arc::clone(&app);
|
||||
std::thread::spawn(move || {
|
||||
start_networking(sync_network_rx, &app_nw, cancellation_token, reqwest_client)
|
||||
|
||||
+65
-20
@@ -2,7 +2,7 @@ use std::fs::{self, File};
|
||||
use std::io::{BufRead, BufReader, Seek, SeekFrom};
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Once};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
@@ -34,52 +34,92 @@ use crate::ui::theme::ThemeDefinitionsWrapper;
|
||||
mod utils_tests;
|
||||
|
||||
pub fn get_log_path() -> PathBuf {
|
||||
let mut log_path = if cfg!(target_os = "linux") {
|
||||
let preferred = if cfg!(target_os = "linux") {
|
||||
dirs_next::cache_dir().unwrap_or_else(|| PathBuf::from("~/.cache"))
|
||||
} else if cfg!(target_os = "macos") {
|
||||
dirs_next::home_dir().unwrap().join("Library/Logs")
|
||||
dirs_next::home_dir()
|
||||
.map(|h| h.join("Library/Logs"))
|
||||
.unwrap_or_else(std::env::temp_dir)
|
||||
} else {
|
||||
dirs_next::data_local_dir().unwrap_or_else(|| PathBuf::from("C:\\Logs"))
|
||||
};
|
||||
}
|
||||
.join("managarr");
|
||||
|
||||
log_path.push("managarr");
|
||||
resolve_log_dir(preferred).join("managarr.log")
|
||||
}
|
||||
|
||||
if let Err(e) = fs::create_dir_all(&log_path) {
|
||||
eprintln!("Failed to create log directory: {e:?}");
|
||||
fn resolve_log_dir(preferred: PathBuf) -> PathBuf {
|
||||
static WARN_ONCE: Once = Once::new();
|
||||
|
||||
if let Err(e) = fs::create_dir_all(&preferred) {
|
||||
WARN_ONCE.call_once(|| {
|
||||
eprintln!(
|
||||
"Failed to create log directory {}: {e}; file logging will be disabled",
|
||||
preferred.display()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
log_path.push("managarr.log");
|
||||
log_path
|
||||
preferred
|
||||
}
|
||||
|
||||
pub fn init_logging_config() -> log4rs::Config {
|
||||
let log_path = get_log_path();
|
||||
build_logging_config(get_log_path())
|
||||
}
|
||||
|
||||
fn build_logging_config(log_path: PathBuf) -> log4rs::Config {
|
||||
let archive_pattern = log_path
|
||||
.with_file_name("managarr.{}.log")
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
|
||||
let trigger = SizeTrigger::new(10 * 1024 * 1024);
|
||||
let roller = FixedWindowRoller::builder()
|
||||
.build(&archive_pattern, 3)
|
||||
.expect("Failed to build log roller");
|
||||
let roller = match FixedWindowRoller::builder().build(&archive_pattern, 3) {
|
||||
Ok(roller) => roller,
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"Failed to build log roller for {archive_pattern}: {e}; file logging will be disabled"
|
||||
);
|
||||
return disabled_logging_config();
|
||||
}
|
||||
};
|
||||
let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller));
|
||||
|
||||
let logfile = RollingFileAppender::builder()
|
||||
let logfile = match RollingFileAppender::builder()
|
||||
.encoder(Box::new(PatternEncoder::new(
|
||||
"{d(%Y-%m-%d %H:%M:%S%.3f)(utc)} <{i}> [{l}] {f}:{L} - {m}{n}",
|
||||
)))
|
||||
.build(log_path, Box::new(policy))
|
||||
.expect("Failed to build rolling file appender");
|
||||
.build(&log_path, Box::new(policy))
|
||||
{
|
||||
Ok(logfile) => logfile,
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"Failed to build rolling file appender for {}: {e}; file logging will be disabled",
|
||||
log_path.display()
|
||||
);
|
||||
return disabled_logging_config();
|
||||
}
|
||||
};
|
||||
|
||||
log4rs::Config::builder()
|
||||
match log4rs::Config::builder()
|
||||
.appender(Appender::builder().build("logfile", Box::new(logfile)))
|
||||
.build(
|
||||
Root::builder()
|
||||
.appender("logfile")
|
||||
.build(LevelFilter::Debug),
|
||||
)
|
||||
.unwrap()
|
||||
) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to build logging config: {e}; file logging will be disabled");
|
||||
disabled_logging_config()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn disabled_logging_config() -> log4rs::Config {
|
||||
log4rs::Config::builder()
|
||||
.build(Root::builder().build(LevelFilter::Debug))
|
||||
.expect("appender-free logging config is infallibly valid")
|
||||
}
|
||||
|
||||
pub fn format_size(bytes: i64, precision: usize) -> String {
|
||||
@@ -102,7 +142,12 @@ pub fn convert_runtime(runtime: i64) -> (i64, i64) {
|
||||
pub async fn tail_logs(no_color: bool) -> Result<()> {
|
||||
let re = Regex::new(r"^(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+<(?P<opid>[^\s>]+)>\s+\[(?P<level>[A-Z]+)]\s+(?P<logger>[^:]+):(?P<line>\d+)\s+-\s+(?P<message>.*)$").unwrap();
|
||||
let file_path = get_log_path();
|
||||
let file = File::open(&file_path).expect("Cannot open file");
|
||||
let file = File::open(&file_path).with_context(|| {
|
||||
format!(
|
||||
"Unable to open log file {}; file logging may be disabled",
|
||||
file_path.display()
|
||||
)
|
||||
})?;
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
reader
|
||||
|
||||
+77
-1
@@ -15,7 +15,8 @@ mod tests {
|
||||
app::{App, AppConfig, ServarrConfig},
|
||||
network::NetworkError,
|
||||
utils::{
|
||||
convert_runtime, format_cli_error, format_size, select_cli_configuration, was_log_rotated,
|
||||
build_logging_config, convert_runtime, format_cli_error, format_size, resolve_log_dir,
|
||||
select_cli_configuration, was_log_rotated,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -236,4 +237,79 @@ mod tests {
|
||||
|
||||
fs::remove_file(&path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_log_dir_returns_preferred_dir_when_creatable() {
|
||||
let preferred = std::env::temp_dir().join(format!(
|
||||
"managarr_test_preferred_log_dir_{}",
|
||||
std::process::id()
|
||||
));
|
||||
let _ = fs::remove_dir_all(&preferred);
|
||||
|
||||
let resolved = resolve_log_dir(preferred.clone());
|
||||
|
||||
assert_eq!(resolved, preferred);
|
||||
assert!(preferred.is_dir());
|
||||
|
||||
fs::remove_dir_all(&preferred).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_log_dir_keeps_preferred_dir_and_disables_file_logging_when_uncreatable() {
|
||||
let blocker = std::env::temp_dir().join(format!(
|
||||
"managarr_test_log_dir_blocker_{}",
|
||||
std::process::id()
|
||||
));
|
||||
let _ = fs::remove_file(&blocker);
|
||||
fs::write(&blocker, "not a directory").unwrap();
|
||||
let preferred = blocker.join("logs");
|
||||
|
||||
let resolved = resolve_log_dir(preferred.clone());
|
||||
|
||||
assert_eq!(resolved, preferred);
|
||||
assert!(!resolved.exists());
|
||||
|
||||
let config = build_logging_config(resolved.join("managarr.log"));
|
||||
|
||||
assert!(config.appenders().is_empty());
|
||||
assert!(config.root().appenders().is_empty());
|
||||
|
||||
fs::remove_file(&blocker).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_logging_config_uses_logfile_appender_when_log_path_is_writable() {
|
||||
let log_dir = std::env::temp_dir().join(format!(
|
||||
"managarr_test_logging_config_{}",
|
||||
std::process::id()
|
||||
));
|
||||
let _ = fs::remove_dir_all(&log_dir);
|
||||
fs::create_dir_all(&log_dir).unwrap();
|
||||
|
||||
let config = build_logging_config(log_dir.join("managarr.log"));
|
||||
|
||||
assert_eq!(config.appenders().len(), 1);
|
||||
assert_str_eq!(config.appenders()[0].name(), "logfile");
|
||||
assert_eq!(config.root().appenders(), ["logfile"]);
|
||||
|
||||
drop(config);
|
||||
fs::remove_dir_all(&log_dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_logging_config_disables_logging_when_appender_is_unbuildable() {
|
||||
let blocker = std::env::temp_dir().join(format!(
|
||||
"managarr_test_appender_blocker_{}",
|
||||
std::process::id()
|
||||
));
|
||||
let _ = fs::remove_file(&blocker);
|
||||
fs::write(&blocker, "not a directory").unwrap();
|
||||
|
||||
let config = build_logging_config(blocker.join("logs").join("managarr.log"));
|
||||
|
||||
assert!(config.appenders().is_empty());
|
||||
assert!(config.root().appenders().is_empty());
|
||||
|
||||
fs::remove_file(&blocker).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
|
||||
use assert_cmd::cargo::cargo_bin_cmd;
|
||||
use assert_cmd::{Command, cargo::cargo_bin_cmd};
|
||||
use mockito::Server;
|
||||
|
||||
static NEXT_CONFIG_ID: AtomicU64 = AtomicU64::new(0);
|
||||
@@ -36,6 +36,14 @@ impl Drop for TemporaryConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn managarr_command() -> Command {
|
||||
let home = std::env::temp_dir().join(format!("managarr-servarr-name-home-{}", process::id()));
|
||||
fs::create_dir_all(&home).unwrap();
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
command.env("HOME", &home).env("XDG_CACHE_HOME", &home);
|
||||
command
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn named_instance_of_different_servarr_type_rejects_command() {
|
||||
let mut radarr_server = Server::new();
|
||||
@@ -62,7 +70,7 @@ fn named_instance_of_different_servarr_type_rejects_command() {
|
||||
sonarr_server.url()
|
||||
));
|
||||
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
let mut command = managarr_command();
|
||||
let assertion = command
|
||||
.arg("--config-file")
|
||||
.arg(config.path())
|
||||
@@ -112,7 +120,7 @@ fn named_readarr_instance_selected_for_readarr_command() {
|
||||
readarr_server.url()
|
||||
));
|
||||
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
let mut command = managarr_command();
|
||||
let assertion = command
|
||||
.arg("--config-file")
|
||||
.arg(config.path())
|
||||
@@ -163,7 +171,7 @@ fn same_named_servarrs_select_command_matching_instance() {
|
||||
sonarr_server.url()
|
||||
));
|
||||
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
let mut command = managarr_command();
|
||||
let assertion = command
|
||||
.arg("--config-file")
|
||||
.arg(config.path())
|
||||
@@ -204,7 +212,7 @@ fn unknown_servarr_name_fails() {
|
||||
"radarr:\n - name: Movies\n uri: {}\n api_token: test-token\n",
|
||||
radarr_server.url()
|
||||
));
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
let mut command = managarr_command();
|
||||
let assertion = command
|
||||
.arg("--config-file")
|
||||
.arg(config.path())
|
||||
@@ -243,7 +251,7 @@ fn whitespace_servarr_name_trimmed() {
|
||||
"radarr:\n - name: Movies\n uri: {}\n api_token: test-token\n",
|
||||
radarr_server.url()
|
||||
));
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
let mut command = managarr_command();
|
||||
let assertion = command
|
||||
.arg("--config-file")
|
||||
.arg(config.path())
|
||||
@@ -293,7 +301,7 @@ fn no_servarr_name_uses_first_configured_instance() {
|
||||
radarr_server2.url()
|
||||
));
|
||||
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
let mut command = managarr_command();
|
||||
let assertion = command
|
||||
.arg("--config-file")
|
||||
.arg(config.path())
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
#![cfg(unix)]
|
||||
|
||||
use std::{fs, path::PathBuf, process, str};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use assert_cmd::Command;
|
||||
use assert_cmd::cargo::cargo_bin_cmd;
|
||||
|
||||
struct ProbeEnv {
|
||||
base: PathBuf,
|
||||
}
|
||||
|
||||
impl ProbeEnv {
|
||||
fn new(test_name: &str) -> Self {
|
||||
let base = std::env::temp_dir().join(format!(
|
||||
"managarr_unwritable_log_dir_{test_name}_{}",
|
||||
process::id()
|
||||
));
|
||||
let _ = fs::remove_dir_all(&base);
|
||||
fs::create_dir_all(base.join("home")).unwrap();
|
||||
fs::create_dir_all(base.join("fresh_tmp")).unwrap();
|
||||
fs::write(base.join("blockfile"), "not a directory").unwrap();
|
||||
fs::write(
|
||||
base.join("config.yml"),
|
||||
"radarr:\n - name: Movies\n host: 127.0.0.1\n port: 7878\n api_token: test-token\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Self { base }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn blocked_cache_dir(&self) -> PathBuf {
|
||||
self.base.join("blockfile").join("cache")
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn command(&self) -> Command {
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
command
|
||||
.env_clear()
|
||||
.env("PATH", std::env::var_os("PATH").unwrap())
|
||||
.env("HOME", self.base.join("home"))
|
||||
.env("XDG_CACHE_HOME", self.blocked_cache_dir())
|
||||
.env("TMPDIR", self.base.join("fresh_tmp"))
|
||||
.env("SSL_CERT_FILE", self.base.join("no_certs.crt"))
|
||||
.env("SSL_CERT_DIR", self.base.join("no_certs_dir"))
|
||||
.arg("--config-file")
|
||||
.arg(self.base.join("config.yml"));
|
||||
command
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ProbeEnv {
|
||||
fn drop(&mut self) {
|
||||
let _ = fs::remove_dir_all(&self.base);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn unwritable_log_dir_warns_and_cli_command_still_succeeds() {
|
||||
let env = ProbeEnv::new("completions");
|
||||
let expected_log_dir = env.blocked_cache_dir().join("managarr");
|
||||
|
||||
let mut command = env.command();
|
||||
let assertion = command.args(["completions", "bash"]).assert().success();
|
||||
let output = assertion.get_output();
|
||||
let stderr = str::from_utf8(&output.stderr).unwrap();
|
||||
|
||||
assert!(
|
||||
!output.stdout.is_empty(),
|
||||
"completions produced no stdout despite exiting successfully"
|
||||
);
|
||||
assert!(
|
||||
stderr.contains(&format!(
|
||||
"Failed to create log directory {}",
|
||||
expected_log_dir.display()
|
||||
)),
|
||||
"stderr did not warn about the uncreatable log directory: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
stderr.contains("file logging will be disabled"),
|
||||
"stderr did not announce that file logging is disabled: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("panicked"),
|
||||
"binary panicked instead of degrading gracefully: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
fs::read_dir(env.base.join("fresh_tmp")).unwrap().count() == 0,
|
||||
"log output fell back to a temp directory despite the no-fallback ruling"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn tail_logs_with_unopenable_log_file_returns_error_without_panicking() {
|
||||
let env = ProbeEnv::new("tail_logs");
|
||||
let expected_log_file = env
|
||||
.blocked_cache_dir()
|
||||
.join("managarr")
|
||||
.join("managarr.log");
|
||||
|
||||
let mut command = env.command();
|
||||
let assertion = command
|
||||
.arg("tail-logs")
|
||||
.timeout(std::time::Duration::from_secs(30))
|
||||
.assert()
|
||||
.failure();
|
||||
let stderr = str::from_utf8(&assertion.get_output().stderr).unwrap();
|
||||
|
||||
assert!(
|
||||
stderr.contains(&format!(
|
||||
"Unable to open log file {}",
|
||||
expected_log_file.display()
|
||||
)),
|
||||
"tail-logs error did not name the log file: {stderr}"
|
||||
);
|
||||
assert_eq!(
|
||||
stderr.matches("Failed to create log directory").count(),
|
||||
1,
|
||||
"the create-dir warning must print exactly once per process: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
stderr.contains("file logging may be disabled"),
|
||||
"tail-logs error did not mention that file logging may be disabled: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("panicked"),
|
||||
"tail-logs panicked instead of returning an error: {stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn writable_cache_dir_creates_log_file_without_warnings() {
|
||||
let env = ProbeEnv::new("happy_path");
|
||||
let cache_dir = env.base.join("home").join("cache");
|
||||
fs::create_dir_all(&cache_dir).unwrap();
|
||||
|
||||
let mut command = env.command();
|
||||
let assertion = command
|
||||
.env("XDG_CACHE_HOME", &cache_dir)
|
||||
.args(["completions", "bash"])
|
||||
.assert()
|
||||
.success();
|
||||
let stderr = str::from_utf8(&assertion.get_output().stderr).unwrap();
|
||||
|
||||
assert!(
|
||||
stderr.is_empty(),
|
||||
"happy path emitted unexpected stderr: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
cache_dir.join("managarr").join("managarr.log").is_file(),
|
||||
"logfile appender did not create managarr.log in the writable cache dir"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unset_home_does_not_panic_on_startup() {
|
||||
let env = ProbeEnv::new("unset_home");
|
||||
|
||||
let mut command = cargo_bin_cmd!("managarr");
|
||||
let assertion = command
|
||||
.env_clear()
|
||||
.env("PATH", std::env::var_os("PATH").unwrap())
|
||||
.env("TMPDIR", env.base.join("fresh_tmp"))
|
||||
.env("SSL_CERT_FILE", env.base.join("no_certs.crt"))
|
||||
.env("SSL_CERT_DIR", env.base.join("no_certs_dir"))
|
||||
.current_dir(&env.base)
|
||||
.arg("--config-file")
|
||||
.arg(env.base.join("config.yml"))
|
||||
.args(["completions", "bash"])
|
||||
.assert()
|
||||
.success();
|
||||
let output = assertion.get_output();
|
||||
let stderr = str::from_utf8(&output.stderr).unwrap();
|
||||
|
||||
assert!(
|
||||
!output.stdout.is_empty(),
|
||||
"completions produced no stdout despite exiting successfully"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("panicked"),
|
||||
"binary panicked instead of degrading gracefully: {stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn config_path_succeeds_without_loadable_tls_certificates() {
|
||||
let env = ProbeEnv::new("config_path");
|
||||
|
||||
let mut command = env.command();
|
||||
let assertion = command.arg("config-path").assert().success();
|
||||
let output = assertion.get_output();
|
||||
let stderr = str::from_utf8(&output.stderr).unwrap();
|
||||
|
||||
let stdout = str::from_utf8(&output.stdout).unwrap();
|
||||
assert_eq!(
|
||||
stdout.trim_end(),
|
||||
env.base.join("config.yml").display().to_string(),
|
||||
"config-path did not print the config file passed via --config-file"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("builder error"),
|
||||
"config-path built the reqwest client despite needing no network: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("panicked"),
|
||||
"binary panicked instead of degrading gracefully: {stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn servarr_command_without_loadable_tls_certificates_reports_builder_error() {
|
||||
let env = ProbeEnv::new("servarr_builder_error");
|
||||
|
||||
let mut command = env.command();
|
||||
let assertion = command
|
||||
.args(["--disable-spinner", "radarr", "list", "movies"])
|
||||
.assert()
|
||||
.failure();
|
||||
let stderr = str::from_utf8(&assertion.get_output().stderr).unwrap();
|
||||
|
||||
assert!(
|
||||
stderr.contains("error: builder error"),
|
||||
"network-bound command did not surface the client builder failure: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("panicked"),
|
||||
"builder failure panicked instead of exiting cleanly: {stderr}"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user