diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b688a41..650f0f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo fmt --all -- --check - run: cargo clippy --all-targets --all-features --locked -- -D warnings - - name: ShellCheck the gather script - run: shellcheck -s sh crates/fleetdeck-core/src/gather.sh + - name: ShellCheck the shell scripts + run: shellcheck -s sh crates/fleetdeck-core/src/gather.sh install.sh test: # The gather script runs on GNU (Linux) and BSD (macOS) userlands. diff --git a/Cargo.lock b/Cargo.lock index 37fd657..88f2b5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.5" @@ -41,18 +47,59 @@ dependencies = [ "bytemuck", ] +[[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-rs" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bit-set" version = "0.5.3" @@ -89,6 +136,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -107,6 +163,18 @@ version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + [[package]] name = "castaway" version = "0.2.4" @@ -116,6 +184,18 @@ dependencies = [ "rustversion", ] +[[package]] +name = "cc" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.5" @@ -128,6 +208,36 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core 0.10.1", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compact_str" version = "0.9.1" @@ -142,6 +252,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "convert_case" version = "0.10.0" @@ -151,6 +273,57 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cookie" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -160,6 +333,24 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" +dependencies = [ + "cfg-if", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -203,6 +394,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "csscolorparser" version = "0.6.2" @@ -213,6 +413,33 @@ dependencies = [ "phf", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "darling" version = "0.24.1" @@ -253,6 +480,16 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.8" @@ -287,8 +524,30 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid 0.10.2", + "crypto-common 0.2.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", ] [[package]] @@ -300,12 +559,57 @@ dependencies = [ "litrs", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2 0.10.9", + "signature", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" +[[package]] +name = "encoding_rs" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -319,7 +623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -347,6 +651,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filedescriptor" version = "0.8.3" @@ -358,6 +668,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + [[package]] name = "finl_unicode" version = "1.5.0" @@ -370,22 +696,35 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + [[package]] name = "fleetdeck" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "fleetdeck-core", "ratatui", + "self_update", "serde_json", + "sha2 0.10.9", "unicode-width", ] [[package]] name = "fleetdeck-core" -version = "0.1.0" +version = "0.2.0" dependencies = [ - "base64", + "base64 0.22.1", "regex", "serde", "serde_json", @@ -405,12 +744,49 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + [[package]] name = "futures-task" version = "0.3.34" @@ -424,7 +800,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", + "futures-io", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -439,6 +818,19 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -458,8 +850,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] @@ -497,13 +911,225 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "ident_case" -version = "1.0.1" +name = "http" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] [[package]] -name = "indexmap" +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" @@ -534,6 +1160,12 @@ dependencies = [ "syn 3.0.6", ] +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + [[package]] name = "itertools" version = "0.14.0" @@ -549,6 +1181,65 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.21", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + [[package]] name = "js-sys" version = "0.3.106" @@ -610,6 +1301,12 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + [[package]] name = "litrs" version = "1.0.0" @@ -640,6 +1337,12 @@ dependencies = [ "hashbrown 0.17.1", ] +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + [[package]] name = "mac_address" version = "1.2.0" @@ -647,7 +1350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b367b50a9be9a5d3b5718c1da74e5d6b9c17647f89752ee2562a470cc3c4eb1a" dependencies = [ "nix 0.30.1", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -677,6 +1380,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.3" @@ -686,9 +1399,15 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.61.2", ] +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + [[package]] name = "nix" version = "0.29.0" @@ -765,6 +1484,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "ordered-float" version = "4.6.0" @@ -830,6 +1555,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pest" version = "2.9.2" @@ -899,7 +1630,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.8", ] [[package]] @@ -930,12 +1661,37 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + [[package]] name = "portable-atomic" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -951,6 +1707,63 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.21", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.3", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.21", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + [[package]] name = "quote" version = "1.0.47" @@ -978,7 +1791,18 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", ] [[package]] @@ -986,6 +1810,24 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] [[package]] name = "ratatui" @@ -1126,6 +1968,66 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "reqwest" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "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.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1145,27 +2047,179 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] -name = "rustversion" -version = "1.0.23" +name = "rustls" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] [[package]] -name = "ryu" -version = "1.0.23" +name = "rustls-native-certs" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "rustls-pki-types" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self-replace" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ec815b5eab420ab893f63393878d89c90fdd94c0bcc44c07abb8ad95552fb7" +dependencies = [ + "fastrand", + "tempfile", + "windows-sys 0.52.0", +] + +[[package]] +name = "self_update" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c39da82b90ac89d099e21109c81c91b70bd85a582d8e382a5e88aa007f5310b" +dependencies = [ + "flate2", + "http", + "log", + "regex", + "reqwest", + "self-replace", + "semver", + "serde", + "serde_json", + "sha2 0.11.0", + "tar", + "tempfile", + "ureq", + "urlencoding", + "zipsign-api", +] + [[package]] name = "semver" version = "1.0.28" @@ -1231,10 +2285,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook" version = "0.3.18" @@ -1266,6 +2337,38 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "1.0.4" @@ -1284,6 +2387,43 @@ version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1317,6 +2457,12 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -1350,6 +2496,37 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -1360,7 +2537,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1373,7 +2550,7 @@ dependencies = [ "parking_lot", "rustix", "signal-hook", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1404,7 +2581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "bitflags 2.13.2", "fancy-regex", "filedescriptor", @@ -1422,7 +2599,7 @@ dependencies = [ "pest", "pest_derive", "phf", - "sha2", + "sha2 0.10.9", "signal-hook", "siphasher", "terminfo", @@ -1492,6 +2669,7 @@ dependencies = [ "powerfmt", "serde_core", "time-core", + "time-macros", ] [[package]] @@ -1500,6 +2678,70 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "1.1.6+spec-1.1.0" @@ -1539,6 +2781,76 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.2", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.20.1" @@ -1580,6 +2892,76 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7ac20be9b7726e0bbdbf974c059676d9acb1cd414961f570a4e8231cacd7fc" +dependencies = [ + "base64 0.23.1", + "cookie_store", + "encoding_rs", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "socks", + "ureq-proto", + "utf8-zero", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86fd172ccca569e458f61b6bdd6220965a9ef36e672a6852953b51a0e1583be" +dependencies = [ + "base64 0.23.1", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1613,6 +2995,25 @@ dependencies = [ "utf8parse", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1641,6 +3042,17 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" +dependencies = [ + "js-sys", + "tokio", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.129" @@ -1673,6 +3085,44 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "wezterm-bidi" version = "0.2.3" @@ -1691,7 +3141,7 @@ checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" dependencies = [ "getrandom 0.3.4", "mac_address", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", "uuid", ] @@ -1761,6 +3211,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1773,6 +3232,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -1782,6 +3250,70 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "1.0.4" @@ -1794,6 +3326,122 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "zipsign-api" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a55ebb27e67d9a9d116dd3a19637ee8cc0570c8ef816fb504c453f15448c99" +dependencies = [ + "base64 0.22.1", + "ed25519-dalek", + "thiserror 2.0.21", +] + +[[package]] +name = "zlib-rs" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + [[package]] name = "zmij" version = "1.0.23" diff --git a/Cargo.toml b/Cargo.toml index 278af9e..af2d601 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "3" members = ["crates/fleetdeck-core", "crates/fleetdeck"] [workspace.package] -version = "0.1.0" +version = "0.2.0" edition = "2024" rust-version = "1.88" license = "MIT" diff --git a/README.md b/README.md index cc4d8bf..c9cec96 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,39 @@ Press `Enter` on any row to open a detail view: a task with all its meta keys an ## Install -### From a release +### With the install script + +Run this command on macOS or Linux: + +```sh +curl -fsSL https://raw.githubusercontent.com/jayjongcheolpark/fleetdeck/main/install.sh | sh +``` + +The script downloads the release archive for your OS and CPU, checks it against the release's `SHA256SUMS`, and installs `fleetdeck` to `~/.local/bin`. +If that directory is not on your `PATH`, the script tells you the line to add to your shell profile. + +- To install to a different directory, set `FLEETDECK_INSTALL_DIR`: `curl -fsSL … | FLEETDECK_INSTALL_DIR=/usr/local/bin sh`. +- To install a specific version, set `FLEETDECK_VERSION`, for example `FLEETDECK_VERSION=0.2.0`. + +### Update + +To replace the installed binary with the latest release, run: + +```sh +fleetdeck update +``` + +`fleetdeck update` reads the latest [GitHub Release](https://github.com/jayjongcheolpark/fleetdeck/releases), downloads the archive for the target that the running binary was built for, and checks it against `SHA256SUMS`. +It runs the new binary once with `--version`, then renames it over the running binary. +If any step fails, the old binary stays in place. +It needs no GitHub token. + +To only check whether a newer release exists, run `fleetdeck update --check`. + +`fleetdeck update` works for a binary from the install script or from a release archive. +If you installed with cargo, update with cargo. + +### From a release archive Each [GitHub Release](https://github.com/jayjongcheolpark/fleetdeck/releases) has a `fleetdeck--.tar.gz` archive for these targets: @@ -41,7 +73,7 @@ Each archive holds the `fleetdeck` binary, this README and the license. 1. Set the version and the target: ```sh - VERSION=0.1.0 + VERSION=0.2.0 TARGET=aarch64-apple-darwin ``` @@ -75,7 +107,7 @@ To allow it, run `xattr -d com.apple.quarantine ~/.local/bin/fleetdeck`. You need a Rust toolchain (1.88 or later) to build from source. ```sh -cargo install --git https://github.com/jayjongcheolpark/fleetdeck --tag v0.1.0 fleetdeck +cargo install --git https://github.com/jayjongcheolpark/fleetdeck --tag v0.2.0 fleetdeck ``` Omit `--tag` to build the latest `main`. @@ -131,6 +163,12 @@ Other modes: - `fleetdeck --json` prints the collected snapshots as JSON. - `fleetdeck --frame 160x45 --tab work --select 0` prints one rendered frame as text. +fleetdeck reads every home again after `refresh_secs`. +When the terminal reports that it lost focus, fleetdeck stops the timed reads and shows `paused` in the status bar. +When the terminal gets focus again, fleetdeck reads every home immediately and starts the timer again. +`r` still reads every home while fleetdeck is paused. +A terminal that does not report focus changes never pauses fleetdeck. + ## Screenshots The Work tab shows the tasks, the open decisions and blockers, the captain holds and the backlog of the selected home: diff --git a/crates/fleetdeck/Cargo.toml b/crates/fleetdeck/Cargo.toml index 5abb102..7aa5b7d 100644 --- a/crates/fleetdeck/Cargo.toml +++ b/crates/fleetdeck/Cargo.toml @@ -13,3 +13,7 @@ ratatui.workspace = true anyhow.workspace = true serde_json.workspace = true unicode-width.workspace = true +self_update = { version = "1.3", default-features = false, features = ["ureq", "rustls", "github", "archive-tar", "compression-tar-gz", "checksums"] } + +[dev-dependencies] +sha2 = "0.10" diff --git a/crates/fleetdeck/src/app.rs b/crates/fleetdeck/src/app.rs index 76bdf69..5580eb0 100644 --- a/crates/fleetdeck/src/app.rs +++ b/crates/fleetdeck/src/app.rs @@ -1,6 +1,7 @@ //! TUI state and key handling. Rendering lives in `ui.rs`. use std::collections::{HashMap, HashSet}; +use std::time::Duration; use fleetdeck_core::backlog::Section; use fleetdeck_core::collect::now_epoch; @@ -86,6 +87,9 @@ pub struct App { pub detail: Option, pub help: bool, pub refreshing: bool, + /// The terminal reported that it lost focus, so the timed refresh waits. + /// A terminal that sends no focus events never sets this. + pub paused: bool, pub last_refresh: Option, pub prs: HashMap, pub prs_loading: HashSet, @@ -108,6 +112,7 @@ impl App { detail: None, help: false, refreshing: false, + paused: false, last_refresh: None, prs: HashMap::new(), prs_loading: HashSet::new(), @@ -195,6 +200,26 @@ impl App { } } + /// True when the timed refresh must start: `since_start` has passed + /// since the last refresh started, no refresh runs, and the terminal has focus. + pub fn refresh_due(&self, since_start: Duration) -> bool { + !self.paused + && !self.refreshing + && since_start >= Duration::from_secs(self.config.refresh_secs) + } + + pub fn focus_lost(&mut self) { + self.paused = true; + } + + /// Reads every home now; the timer starts again from this refresh. + pub fn focus_gained(&mut self) { + if self.paused { + self.paused = false; + self.request_refresh(); + } + } + /// PR URLs of the selected home: task PRs, then in-flight backlog links. pub fn pr_urls(&self) -> Vec { let Some(h) = self.home() else { @@ -537,6 +562,36 @@ mod tests { assert_eq!(a.content_sel, 1); } + #[test] + fn focus_loss_pauses_the_timer_and_focus_gain_refreshes() { + let mut a = app(); + let late = Duration::from_secs(a.config.refresh_secs); + assert!(a.refresh_due(late)); + assert!(!a.refresh_due(late - Duration::from_secs(1))); + + a.focus_lost(); + assert!(a.paused); + assert!(!a.refresh_due(late * 10)); + + // The manual refresh key still works while paused. + a.on_key(Key::Char('r'), &[], 10); + assert_eq!(a.requests, [Request::Refresh]); + a.requests.clear(); + a.refresh_finished(); + + a.focus_gained(); + assert!(!a.paused); + assert_eq!(a.requests, [Request::Refresh]); + assert!(!a.refresh_due(late), "the gain refresh is still running"); + a.refresh_finished(); + assert!(a.refresh_due(late)); + + // A second gain, or a gain without a loss, does not refresh again. + a.requests.clear(); + a.focus_gained(); + assert!(a.requests.is_empty()); + } + #[test] fn keys_switch_tabs_and_request_refresh_once() { let mut a = app(); diff --git a/crates/fleetdeck/src/cli.rs b/crates/fleetdeck/src/cli.rs index 73801a0..3d26579 100644 --- a/crates/fleetdeck/src/cli.rs +++ b/crates/fleetdeck/src/cli.rs @@ -9,6 +9,7 @@ fleetdeck - read-only terminal dashboard for firstmate fleets USAGE: fleetdeck [OPTIONS] + fleetdeck update [--check] OPTIONS: -c, --config Config file (default: ~/.config/fleetdeck/config.toml) @@ -22,6 +23,10 @@ OPTIONS: -h, --help Show this help -V, --version Show the version +COMMANDS: + update Install the latest GitHub Release over this binary + update --check Only report whether a newer release exists + With no config file and no --home, fleetdeck shows $FM_HOME, else the current directory when it looks like a firstmate home. "; @@ -41,9 +46,14 @@ pub enum Parsed { Run(Args), Help, Version, + Update { check: bool }, } -pub fn parse(mut it: impl Iterator) -> Result { +pub fn parse(it: impl Iterator) -> Result { + let mut it = it.peekable(); + if it.next_if(|a| a == "update").is_some() { + return parse_update(it); + } let mut a = Args::default(); let value = |it: &mut dyn Iterator, flag: &str| { it.next().ok_or_else(|| format!("{flag} needs a value")) @@ -95,6 +105,18 @@ pub fn parse(mut it: impl Iterator) -> Result { Ok(Parsed::Run(a)) } +fn parse_update(it: impl Iterator) -> Result { + let mut check = false; + for arg in it { + match arg.as_str() { + "--check" => check = true, + "-h" | "--help" => return Ok(Parsed::Help), + other => return Err(format!("unknown argument to update: {other}\n\n{USAGE}")), + } + } + Ok(Parsed::Update { check }) +} + fn basename(p: &str) -> &str { p.trim_end_matches('/').rsplit('/').next().unwrap_or(p) } @@ -172,4 +194,18 @@ mod tests { assert!(args(&["--bogus"]).is_err()); assert!(matches!(args(&["-h"]), Ok(Parsed::Help))); } + + #[test] + fn parses_update() { + assert!(matches!( + args(&["update"]), + Ok(Parsed::Update { check: false }) + )); + assert!(matches!( + args(&["update", "--check"]), + Ok(Parsed::Update { check: true }) + )); + assert!(args(&["update", "--bogus"]).is_err()); + assert!(args(&["--json", "update"]).is_err()); + } } diff --git a/crates/fleetdeck/src/main.rs b/crates/fleetdeck/src/main.rs index f9dd64e..bdab5bf 100644 --- a/crates/fleetdeck/src/main.rs +++ b/crates/fleetdeck/src/main.rs @@ -2,6 +2,7 @@ mod app; mod cli; mod detail; mod ui; +mod update; use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::{Arc, Mutex}; @@ -15,7 +16,10 @@ use fleetdeck_core::model::HomeSnapshot; use fleetdeck_core::pr::{self, PrStatus}; use ratatui::Terminal; use ratatui::backend::TestBackend; -use ratatui::crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; +use ratatui::crossterm::event::{ + self, DisableFocusChange, EnableFocusChange, Event, KeyCode, KeyEventKind, KeyModifiers, +}; +use ratatui::crossterm::execute; use app::{App, Key, Request, Tab}; @@ -51,6 +55,13 @@ fn main() { println!("fleetdeck {}", env!("CARGO_PKG_VERSION")); return; } + cli::Parsed::Update { check } => { + if let Err(e) = update::run(check) { + eprintln!("fleetdeck: {e:#}"); + std::process::exit(1); + } + return; + } cli::Parsed::Run(a) => a, }; let config = match cli::effective_config(&args) { @@ -235,9 +246,10 @@ fn apply(app: &mut App, u: Update) { fn run_tui(config: Config) -> Result<()> { let (tx, rx): (Sender, Receiver) = mpsc::channel(); - let refresh_every = Duration::from_secs(config.refresh_secs); let mut app = App::new(config); let mut terminal = ratatui::init(); + // Terminals that support it report focus changes; the others send nothing. + let _ = execute!(std::io::stdout(), EnableFocusChange); let result = (|| -> Result<()> { let mut last_start = Instant::now(); app.request_refresh(); @@ -245,7 +257,7 @@ fn run_tui(config: Config) -> Result<()> { while let Ok(u) = rx.try_recv() { apply(&mut app, u); } - if !app.refreshing && last_start.elapsed() >= refresh_every { + if app.refresh_due(last_start.elapsed()) { app.request_refresh(); } for req in std::mem::take(&mut app.requests) { @@ -262,18 +274,24 @@ fn run_tui(config: Config) -> Result<()> { let body_w = f.area().width.saturating_sub(24); targets = ui::rows(&app, body_w).targets; })?; - if event::poll(Duration::from_millis(200))? - && let Event::Key(k) = event::read()? - && k.kind == KeyEventKind::Press - && let Some(key) = key_of(k.code, k.modifiers) - { - app.on_key(key, &targets, page); + if event::poll(Duration::from_millis(200))? { + match event::read()? { + Event::Key(k) if k.kind == KeyEventKind::Press => { + if let Some(key) = key_of(k.code, k.modifiers) { + app.on_key(key, &targets, page); + } + } + Event::FocusLost => app.focus_lost(), + Event::FocusGained => app.focus_gained(), + _ => {} + } } if app.quit { return Ok(()); } } })(); + let _ = execute!(std::io::stdout(), DisableFocusChange); ratatui::restore(); result } diff --git a/crates/fleetdeck/src/ui.rs b/crates/fleetdeck/src/ui.rs index f6b7e34..7177a14 100644 --- a/crates/fleetdeck/src/ui.rs +++ b/crates/fleetdeck/src/ui.rs @@ -930,12 +930,20 @@ fn footer_bar(f: &mut Frame, app: &App, area: Rect) { } else { "↑↓ move · Tab pane · 1-4/[ ] tabs · Enter open · r refresh · p PRs · ? help · q quit" }; - let line = Line::from(vec![ + let mut spans = vec![ Span::styled(" fleetdeck ", Style::new().fg(Color::Black).bg(Color::Cyan)), Span::styled( " read-only ", Style::new().fg(Color::Black).bg(Color::Green), ), + ]; + if app.paused { + spans.push(Span::styled( + " paused ", + Style::new().fg(Color::Black).bg(Color::Yellow), + )); + } + spans.extend([ Span::raw(format!( " {refresh} · every {}s · ", app.config.refresh_secs @@ -947,7 +955,7 @@ fn footer_bar(f: &mut Frame, app: &App, area: Rect) { .map_or_else(String::new, |m| format!(" · {m}")), ), ]); - f.render_widget(Paragraph::new(line), area); + f.render_widget(Paragraph::new(Line::from(spans)), area); } fn centered(area: Rect, pw: u16, ph: u16) -> Rect { diff --git a/crates/fleetdeck/src/update.rs b/crates/fleetdeck/src/update.rs new file mode 100644 index 0000000..5087222 --- /dev/null +++ b/crates/fleetdeck/src/update.rs @@ -0,0 +1,207 @@ +//! `fleetdeck update`: replace the running binary with the latest GitHub Release. +//! +//! The release workflow publishes `fleetdeck--.tar.gz` for each target and a +//! `SHA256SUMS` file. The update checks the archive against `SHA256SUMS` before it extracts it, +//! and `self_update` swaps the binary in with a rename, so a failed update keeps the old binary. + +use std::path::Path; +use std::process::Command; + +use anyhow::{Context, Result, bail}; +use self_update::backends::github::Update; +use self_update::{ReleaseAsset, VersionStatus}; + +const OWNER: &str = "jayjongcheolpark"; +const REPO: &str = "fleetdeck"; +const BIN: &str = "fleetdeck"; +const SUMS: &str = "SHA256SUMS"; +const CURRENT: &str = env!("CARGO_PKG_VERSION"); + +/// The archive name that `.github/workflows/release.yml` publishes. +pub fn archive_name(version: &str, target: &str) -> String { + format!("{BIN}-{version}-{target}.tar.gz") +} + +/// Picks the archive for `target` from the asset names of one release. +/// The name must match exactly, so `x86_64-apple-darwin` never matches another target. +pub fn select_asset<'a>(names: &[&'a str], version: &str, target: &str) -> Option<&'a str> { + let want = archive_name(version, target); + names.iter().copied().find(|n| *n == want) +} + +/// True when `latest` (a tag such as `v0.2.0` or a bare version) is newer than `current`. +pub fn is_newer(current: &str, latest: &str) -> Result { + let latest = latest.strip_prefix('v').unwrap_or(latest); + self_update::version::bump_is_greater(current, latest) + .with_context(|| format!("cannot compare versions {current} and {latest}")) +} + +/// Picks the archive for `target` from the assets of one release. +/// The matcher does not get the release version, so it reads it from the archive names. +fn match_archive(assets: &[ReleaseAsset], target: &str) -> Option { + let names: Vec<&str> = assets.iter().map(|a| a.name()).collect(); + let prefix = format!("{BIN}-"); + let suffix = format!("-{target}.tar.gz"); + let version = names + .iter() + .find_map(|n| n.strip_prefix(&prefix)?.strip_suffix(&suffix))?; + let name = select_asset(&names, version, target)?; + assets.iter().find(|a| a.name() == name).cloned() +} + +fn updater(target: &'static str) -> Result { + let built = Update::configure() + .repo_owner(OWNER) + .repo_name(REPO) + .bin_name(BIN) + .target(target) + .current_version(CURRENT) + .bin_path_in_archive("{{ bin }}-{{ version }}-{{ target }}/{{ bin }}") + .asset_matcher(move |assets: &[ReleaseAsset]| match_archive(assets, target)) + .checksum_from_asset(SUMS) + .verify_binary(|new_exe: &Path| check_new_binary(new_exe)) + .show_output(false) + .show_download_progress(false) + .no_confirm(true) + .build()?; + Ok(built) +} + +/// Runs the downloaded binary once before it replaces the installed one. +fn check_new_binary(new_exe: &Path) -> self_update::errors::Result<()> { + let out = Command::new(new_exe) + .arg("--version") + .output() + .map_err(|e| self_update::errors::Error::verification_rejected(e.to_string()))?; + let text = String::from_utf8_lossy(&out.stdout); + if out.status.success() && text.starts_with("fleetdeck ") { + Ok(()) + } else { + Err(self_update::errors::Error::verification_rejected(format!( + "the new binary did not print its version: {}", + text.trim() + ))) + } +} + +/// `fleetdeck update [--check]`. +pub fn run(check: bool) -> Result<()> { + let target = self_update::get_target(); + let updater = updater(target)?; + let latest = updater + .get_latest_release() + .context("cannot read the latest GitHub Release")?; + let Some(release) = latest.all().first() else { + bail!("{OWNER}/{REPO} has no releases"); + }; + let version = release.version().to_owned(); + if !is_newer(CURRENT, &version)? { + println!("fleetdeck {CURRENT} is the latest version."); + return Ok(()); + } + if check { + println!("fleetdeck {version} is available (you have {CURRENT})."); + println!("Run `fleetdeck update` to install it."); + return Ok(()); + } + println!("Updating fleetdeck {CURRENT} to {version} for {target}."); + match updater + .update() + .context("update failed; the installed binary is unchanged")? + { + VersionStatus::Updated(v) => println!("fleetdeck is now {v}."), + other => println!("fleetdeck {} is the latest version.", other.version()), + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use self_update::Checksum; + + const TARGETS: [&str; 4] = [ + "aarch64-apple-darwin", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu", + ]; + + #[test] + fn compares_versions() { + assert!(is_newer("0.1.0", "v0.2.0").unwrap()); + assert!(is_newer("0.1.0", "0.1.1").unwrap()); + assert!(!is_newer("0.2.0", "v0.2.0").unwrap()); + assert!(!is_newer("0.2.0", "v0.1.9").unwrap()); + assert!(is_newer("0.9.0", "0.10.0").unwrap()); + assert!(is_newer("0.1.0", "not-a-version").is_err()); + } + + #[test] + fn selects_the_archive_for_each_target() { + let names: Vec = TARGETS + .iter() + .map(|t| archive_name("0.2.0", t)) + .chain([SUMS.to_string()]) + .collect(); + let names: Vec<&str> = names.iter().map(String::as_str).collect(); + for t in TARGETS { + assert_eq!( + select_asset(&names, "0.2.0", t), + Some(format!("fleetdeck-0.2.0-{t}.tar.gz").as_str()) + ); + } + assert_eq!( + select_asset(&names, "0.2.0", "x86_64-unknown-linux-musl"), + None + ); + assert_eq!(select_asset(&names, "0.1.0", TARGETS[0]), None); + } + + #[test] + fn matcher_picks_the_running_target() { + let assets: Vec = TARGETS + .iter() + .map(|t| archive_name("0.2.0", t)) + .chain([SUMS.to_string()]) + .map(|n| ReleaseAsset::new(n.clone(), format!("https://example.invalid/{n}"))) + .collect(); + for t in TARGETS { + let chosen = match_archive(&assets, t).expect("an archive matches"); + assert_eq!(chosen.name(), format!("fleetdeck-0.2.0-{t}.tar.gz")); + } + assert!(match_archive(&assets, "x86_64-unknown-linux-musl").is_none()); + } + + fn hex_sha256(body: &[u8]) -> String { + use sha2::{Digest, Sha256}; + Sha256::digest(body) + .iter() + .map(|b| format!("{b:02x}")) + .collect() + } + + /// The `SHA256SUMS` shape that `sha256sum -- *.tar.gz` writes in the release workflow. + fn sums_for(name: &str, body: &[u8]) -> String { + let hex = hex_sha256(body); + format!( + "{} fleetdeck-0.2.0-x86_64-unknown-linux-gnu.tar.gz\n{hex} {name}\n", + "0".repeat(64) + ) + } + + #[test] + fn verifies_the_archive_against_sha256sums() { + let name = archive_name("0.2.0", "aarch64-apple-darwin"); + + let sums = sums_for(&name, b"archive bytes"); + let Checksum::Sha256(want) = Checksum::from_sums_file(&sums, &name).unwrap() else { + panic!("expected a SHA-256 entry"); + }; + assert_eq!(want, hex_sha256(b"archive bytes")); + assert_ne!(want, hex_sha256(b"tampered bytes")); + + let missing = archive_name("0.2.0", "x86_64-apple-darwin"); + assert!(Checksum::from_sums_file(&sums, &missing).is_err()); + } +} diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..385e6d0 --- /dev/null +++ b/install.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# Install the latest fleetdeck release. +# +# curl -fsSL https://raw.githubusercontent.com/jayjongcheolpark/fleetdeck/main/install.sh | sh +# +# The script downloads the archive for this OS and CPU, checks it against the +# release's SHA256SUMS, and installs the binary to ~/.local/bin, or to +# $FLEETDECK_INSTALL_DIR when it is set. Set FLEETDECK_VERSION (for example +# 0.2.0) to install that version in place of the latest one. + +set -eu + +REPO=jayjongcheolpark/fleetdeck + +say() { printf 'fleetdeck: %s\n' "$*"; } +die() { + printf 'fleetdeck: %s\n' "$*" >&2 + exit 1 +} + +need() { command -v "$1" >/dev/null 2>&1 || die "this script needs $1"; } + +need curl +need tar +need uname + +case "$(uname -s)" in +Darwin) os=apple-darwin ;; +Linux) os=unknown-linux-gnu ;; +*) die "no release for $(uname -s); build from source with cargo" ;; +esac +case "$(uname -m)" in +arm64 | aarch64) arch=aarch64 ;; +x86_64 | amd64) arch=x86_64 ;; +*) die "no release for $(uname -m); build from source with cargo" ;; +esac +target="$arch-$os" + +if [ -n "${FLEETDECK_VERSION:-}" ]; then + version=${FLEETDECK_VERSION#v} +else + # /releases/latest redirects to /releases/tag/v. + url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' "https://github.com/$REPO/releases/latest") || + die "cannot reach GitHub" + version=${url##*/v} + case "$version" in + '' | */*) die "cannot find the latest release from $url" ;; + esac +fi + +if command -v sha256sum >/dev/null 2>&1; then + sha256() { sha256sum "$1" | cut -d ' ' -f 1; } +elif command -v shasum >/dev/null 2>&1; then + sha256() { shasum -a 256 "$1" | cut -d ' ' -f 1; } +else + die "this script needs sha256sum or shasum" +fi + +dir="${FLEETDECK_INSTALL_DIR:-$HOME/.local/bin}" +name="fleetdeck-$version-$target" +base="https://github.com/$REPO/releases/download/v$version" + +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT INT TERM + +say "downloading $name.tar.gz" +curl -fsSL -o "$tmp/$name.tar.gz" "$base/$name.tar.gz" || die "cannot download $base/$name.tar.gz" +curl -fsSL -o "$tmp/SHA256SUMS" "$base/SHA256SUMS" || die "cannot download $base/SHA256SUMS" + +want=$(awk -v f="$name.tar.gz" '$2 == f || $2 == "*" f { print $1 }' "$tmp/SHA256SUMS") +[ -n "$want" ] || die "SHA256SUMS has no entry for $name.tar.gz" +got=$(sha256 "$tmp/$name.tar.gz") +[ "$want" = "$got" ] || die "checksum mismatch for $name.tar.gz (expected $want, got $got)" +say "checksum OK" + +tar -xzf "$tmp/$name.tar.gz" -C "$tmp" +mkdir -p "$dir" +# Copy next to the old binary, then rename over it, so a failed copy keeps the old one. +cp "$tmp/$name/fleetdeck" "$dir/.fleetdeck.new" +chmod 755 "$dir/.fleetdeck.new" +mv -f "$dir/.fleetdeck.new" "$dir/fleetdeck" +say "installed $("$dir/fleetdeck" --version) to $dir/fleetdeck" + +case ":$PATH:" in +*":$dir:"*) ;; +*) + say "$dir is not on your PATH. Add this line to your shell profile:" + # shellcheck disable=SC2016 # print $PATH literally + printf '\n export PATH="%s:$PATH"\n\n' "$dir" + ;; +esac +say "run 'fleetdeck update' later to install a newer release"