From 62f99b07f63188c669c6ef8438b9d3b235cd488c Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 23 Apr 2026 01:13:29 -0300 Subject: [PATCH] ci: add lesavka hygiene gate and launcher crash guard --- .gitignore | 10 +- Cargo.lock | 4278 +++++++++++++++++ Cargo.toml | 2 +- Jenkinsfile | 66 +- README.md | 5 + client/Cargo.toml | 2 +- client/src/launcher/ui.rs | 17 +- client/src/launcher/ui_components.rs | 12 +- common/Cargo.toml | 2 +- docs/operational-env.md | 235 + docs/quality-gate.md | 45 + scripts/ci/build-dist.sh | 21 + scripts/ci/gate_glue_gate.sh | 110 + scripts/ci/hygiene_gate.sh | 208 +- scripts/ci/hygiene_gate_baseline.json | 4 +- scripts/ci/media_reliability_gate.sh | 166 + scripts/ci/platform_quality_gate.sh | 15 + scripts/ci/quality_gate.sh | 58 +- scripts/ci/quality_gate_baseline.json | 2 +- scripts/ci/sonarqube_gate.sh | 98 + scripts/ci/supply_chain_gate.sh | 144 + scripts/daemon/lesavka-core.sh | 0 scripts/daemon/lesavka-uvc.sh | 0 scripts/kernel/build-linux-rpi.sh | 0 scripts/manual/audio-clip-fetch.sh | 12 +- scripts/manual/audio-mic-fetch.sh | 6 + scripts/manual/compare-eye-decoders.sh | 2 + scripts/manual/eval_lesavka.sh | 1 + scripts/manual/kde-start-tethys.sh | 2 + scripts/manual/probe-eye-capabilities.sh | 2 + scripts/manual/soak-report.sh | 1 + scripts/manual/usb-reset.sh | 1 + scripts/manual/video-frame-fetch.sh | 7 +- scripts/manual/video-stream.sh | 12 +- scripts/manual/vpn-open.sh | 6 +- scripts/manual/vpn-test.sh | 8 +- server/Cargo.toml | 2 +- .../tests/client_launcher_layout_contract.rs | 15 + .../tests/client_launcher_runtime_contract.rs | 4 + 39 files changed, 5494 insertions(+), 87 deletions(-) create mode 100644 Cargo.lock create mode 100644 docs/operational-env.md create mode 100644 docs/quality-gate.md create mode 100755 scripts/ci/gate_glue_gate.sh create mode 100755 scripts/ci/media_reliability_gate.sh create mode 100755 scripts/ci/platform_quality_gate.sh create mode 100755 scripts/ci/sonarqube_gate.sh create mode 100755 scripts/ci/supply_chain_gate.sh mode change 100644 => 100755 scripts/daemon/lesavka-core.sh mode change 100644 => 100755 scripts/daemon/lesavka-uvc.sh mode change 100644 => 100755 scripts/kernel/build-linux-rpi.sh diff --git a/.gitignore b/.gitignore index 45e8077..0de3165 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,16 @@ target/ -Cargo.lock +dist/ +coverage/ +logs/ +captures/ override.toml .cache/sccache/ /unit-graph.json +*.log +*.h264 +*.aac +*.wav +*.rgba /**/*.rs.bk **/*.rs.orig **/*.rs.rej diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a82841d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4278 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.11.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atomic_refcell" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cairo-rs" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac2a4d0e69036cf0062976f6efcba1aaee3e448594e6514bb2ddf87acce562" +dependencies = [ + "bitflags 2.11.0", + "cairo-sys-rs", + "glib 0.19.9", + "libc", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64" +dependencies = [ + "glib-sys 0.19.8", + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.11.0", + "log", + "polling", + "rustix 0.38.44", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix 0.38.44", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon 0.12.16", +] + +[[package]] +name = "cfg-expr" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" +dependencies = [ + "smallvec", + "target-lexicon 0.13.3", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +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-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "evdev" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b686663ba7f08d92880ff6ba22170f1df4e83629341cba34cf82cd65ebea99" +dependencies = [ + "bitvec", + "cfg-if", + "libc", + "nix", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624eaba126021103c7339b2e179ae4ee8cdab842daab419040710f38ed9f8699" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib 0.19.9", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4efa05a4f83c8cc50eb4d883787b919b85e5f1d8dd10b5a1df53bf5689782379" +dependencies = [ + "gio-sys 0.19.8", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "gdk4" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db265c9dd42d6a371e09e52deab3a84808427198b86ac792d75fd35c07990a07" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "glib 0.19.9", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9418fb4e8a67074919fe7604429c45aa74eb9df82e7ca529767c6d4e9dc66dd" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys 0.19.8", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "pango-sys", + "pkg-config", + "system-deps 6.2.2", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gio" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c49f117d373ffcc98a35d114db5478bc223341cff53e39a5d6feced9e2ddffe" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.19.8", + "glib 0.19.9", + "libc", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef" +dependencies = [ + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "system-deps 6.2.2", + "windows-sys 0.52.0", +] + +[[package]] +name = "gio-sys" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83" +dependencies = [ + "glib-sys 0.20.10", + "gobject-sys 0.20.10", + "libc", + "system-deps 7.0.8", + "windows-sys 0.59.0", +] + +[[package]] +name = "glib" +version = "0.19.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39650279f135469465018daae0ba53357942a5212137515777d5fdca74984a44" +dependencies = [ + "bitflags 2.11.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.19.8", + "glib-macros 0.19.9", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "memchr", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib" +version = "0.20.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc4b6e352d4716d84d7dde562dd9aee2a7d48beb872dd9ece7f2d1515b2d683" +dependencies = [ + "bitflags 2.11.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.20.10", + "glib-macros 0.20.12", + "glib-sys 0.20.10", + "gobject-sys 0.20.10", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.19.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4429b0277a14ae9751350ad9b658b1be0abb5b54faa5bcdf6e74a3372582fad7" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-macros" +version = "0.20.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8084af62f09475a3f529b1629c10c429d7600ee1398ae12dd3bf175d74e7145" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5" +dependencies = [ + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "glib-sys" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215" +dependencies = [ + "libc", + "system-deps 7.0.8", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e" +dependencies = [ + "glib-sys 0.19.8", + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "gobject-sys" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda" +dependencies = [ + "glib-sys 0.20.10", + "libc", + "system-deps 7.0.8", +] + +[[package]] +name = "graphene-rs" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb86031d24d9ec0a2a15978fc7a65d545a2549642cf1eb7c3dda358da42bcf" +dependencies = [ + "glib 0.19.9", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f530e0944bccba4b55065e9c69f4975ad691609191ebac16e13ab8e1f27af05" +dependencies = [ + "glib-sys 0.19.8", + "libc", + "pkg-config", + "system-deps 6.2.2", +] + +[[package]] +name = "gsk4" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7563884bf6939f4468e5d94654945bdd9afcaf8c3ba4c5dd17b5342b747221be" +dependencies = [ + "cairo-rs", + "gdk4", + "glib 0.19.9", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gsk4-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23024bf2636c38bbd1f822f58acc9d1c25b28da896ff0f291a1a232d4272b3dc" +dependencies = [ + "cairo-sys-rs", + "gdk4-sys", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "graphene-sys", + "libc", + "pango-sys", + "system-deps 6.2.2", +] + +[[package]] +name = "gstreamer" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8757a87f3706560037a01a9f06a59fcc7bdb0864744dcf73546606e60c4316e1" +dependencies = [ + "cfg-if", + "futures-channel", + "futures-core", + "futures-util", + "glib 0.20.12", + "gstreamer-sys", + "itertools", + "libc", + "muldiv", + "num-integer", + "num-rational", + "once_cell", + "option-operations", + "paste", + "pin-project-lite", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gstreamer-app" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9a883eb21aebcf1289158225c05f7aea5da6ecf71fa7f0ff1ce4d25baf004e" +dependencies = [ + "futures-core", + "futures-sink", + "glib 0.20.12", + "gstreamer", + "gstreamer-app-sys", + "gstreamer-base", + "libc", +] + +[[package]] +name = "gstreamer-app-sys" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f7ef838306fe51852d503a14dc79ac42de005a59008a05098de3ecdaf05455" +dependencies = [ + "glib-sys 0.20.10", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", + "system-deps 7.0.8", +] + +[[package]] +name = "gstreamer-base" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f19a74fd04ffdcb847dd322640f2cf520897129d00a7bcb92fd62a63f3e27404" +dependencies = [ + "atomic_refcell", + "cfg-if", + "glib 0.20.12", + "gstreamer", + "gstreamer-base-sys", + "libc", +] + +[[package]] +name = "gstreamer-base-sys" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f2fb0037b6d3c5b51f60dea11e667910f33be222308ca5a101450018a09840" +dependencies = [ + "glib-sys 0.20.10", + "gobject-sys 0.20.10", + "gstreamer-sys", + "libc", + "system-deps 7.0.8", +] + +[[package]] +name = "gstreamer-gl" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5fe4cfc154d1aef2d5bddaf45c8fcb80af5ae4416795650a01c38e01d6d5bd" +dependencies = [ + "glib 0.20.12", + "gstreamer", + "gstreamer-base", + "gstreamer-gl-sys", + "gstreamer-video", + "libc", + "once_cell", +] + +[[package]] +name = "gstreamer-gl-sys" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a832c21d4522ed5e1b8dfc676a45361969216b144fc03af413a38c471f38bcf7" +dependencies = [ + "glib-sys 0.20.10", + "gobject-sys 0.20.10", + "gstreamer-base-sys", + "gstreamer-sys", + "gstreamer-video-sys", + "libc", + "system-deps 7.0.8", +] + +[[package]] +name = "gstreamer-gl-wayland" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603a0b0744c5810932f9c2cb40f0922378202a8875f3db55627049acfe68faf1" +dependencies = [ + "glib 0.20.12", + "gstreamer", + "gstreamer-gl", + "gstreamer-gl-wayland-sys", + "libc", +] + +[[package]] +name = "gstreamer-gl-wayland-sys" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9701def285d8cdf69664c44ac759b92d18254e1faa5cfadea18810cc289e5cb9" +dependencies = [ + "glib-sys 0.20.10", + "gstreamer-gl-sys", + "libc", + "system-deps 7.0.8", +] + +[[package]] +name = "gstreamer-sys" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feea73b4d92dbf9c24a203c9cd0bcc740d584f6b5960d5faf359febf288919b2" +dependencies = [ + "glib-sys 0.20.10", + "gobject-sys 0.20.10", + "libc", + "system-deps 7.0.8", +] + +[[package]] +name = "gstreamer-video" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1318b599d77ca4f7702ecbdeac1672d6304cb16b7e5752fabb3ee8260449a666" +dependencies = [ + "cfg-if", + "futures-channel", + "glib 0.20.12", + "gstreamer", + "gstreamer-base", + "gstreamer-video-sys", + "libc", + "once_cell", + "thiserror 2.0.18", +] + +[[package]] +name = "gstreamer-video-sys" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a70f0947f12d253b9de9bc3fd92f981e4d025336c18389c7f08cdf388a99f5c" +dependencies = [ + "glib-sys 0.20.10", + "gobject-sys 0.20.10", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", + "system-deps 7.0.8", +] + +[[package]] +name = "gtk4" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b04e11319b08af11358ab543105a9e49b0c491faca35e2b8e7e36bfba8b671ab" +dependencies = [ + "cairo-rs", + "field-offset", + "futures-channel", + "gdk-pixbuf", + "gdk4", + "gio", + "glib 0.19.9", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gtk4-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec655a7ef88d8ce9592899deb8b2d0fa50bab1e6dd69182deb764e643c522408" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gtk4-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c8aa86b7f85ea71d66ea88c1d4bae1cfacf51ca4856274565133838d77e57b5" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk4-sys", + "gio-sys 0.19.8", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "graphene-sys", + "gsk4-sys", + "libc", + "pango-sys", + "system-deps 6.2.2", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "lesavka_client" +version = "0.12.2" +dependencies = [ + "anyhow", + "async-stream", + "base64", + "chacha20poly1305", + "evdev", + "futures", + "gstreamer", + "gstreamer-app", + "gstreamer-gl-wayland", + "gstreamer-video", + "gtk4", + "lesavka_common", + "prost-build", + "raw-window-handle", + "serde", + "serde_json", + "serial_test", + "shell-escape", + "temp-env", + "tempfile", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-appender", + "tracing-subscriber", + "v4l", + "winit", +] + +[[package]] +name = "lesavka_common" +version = "0.12.2" +dependencies = [ + "anyhow", + "base64", + "prost", + "serial_test", + "tonic", + "tonic-build", +] + +[[package]] +name = "lesavka_server" +version = "0.12.2" +dependencies = [ + "anyhow", + "base64", + "chacha20poly1305", + "chrono", + "futures-util", + "gstreamer", + "gstreamer-app", + "gstreamer-video", + "lesavka_common", + "libc", + "prost-build", + "prost-types", + "serial_test", + "temp-env", + "tempfile", + "tokio", + "tokio-stream", + "tonic", + "tonic-reflection", + "tracing", + "tracing-appender", + "tracing-subscriber", + "udev", +] + +[[package]] +name = "lesavka_testing" +version = "0.1.0" +dependencies = [ + "anyhow", + "chacha20poly1305", + "chrono", + "evdev", + "futures-util", + "gstreamer", + "gstreamer-app", + "gstreamer-video", + "gtk4", + "lesavka_client", + "lesavka_common", + "lesavka_server", + "libc", + "serde_json", + "serial_test", + "shell-escape", + "temp-env", + "tempfile", + "tokio", + "tokio-stream", + "tonic", + "tonic-reflection", + "tracing", + "tracing-appender", + "tracing-subscriber", + "udev", + "v4l", + "winit", +] + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags 2.11.0", + "libc", + "redox_syscall 0.7.3", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "muldiv" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0" + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.11.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.11.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.11.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "option-operations" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0" +dependencies = [ + "paste", +] + +[[package]] +name = "orbclient" +version = "0.3.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd" +dependencies = [ + "libc", + "libredox", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "pango" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f0d328648058085cfd6897c9ae4272884098a926f3a833cd50c8c73e6eccecd" +dependencies = [ + "gio", + "glib 0.19.9", + "libc", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff03da4fa086c0b244d4a4587d3e20622a3ecdb21daea9edf66597224c634ba0" +dependencies = [ + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.11+spec-1.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "quick-xml" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +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 = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[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 = "scc" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" +dependencies = [ + "sdd", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "sdd" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serial_test" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" +dependencies = [ + "futures-executor", + "futures-util", + "log", + "once_cell", + "parking_lot", + "scc", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.11.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 0.38.44", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr 0.15.8", + "heck", + "pkg-config", + "toml 0.8.23", + "version-compare", +] + +[[package]] +name = "system-deps" +version = "7.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" +dependencies = [ + "cfg-expr 0.20.7", + "heck", + "pkg-config", + "toml 1.1.2+spec-1.1.0", + "version-compare", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "target-lexicon" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" + +[[package]] +name = "temp-env" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96374855068f47402c3121c6eed88d29cb1de8f3ab27090e273e420bdabcf050" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tokio" +version = "1.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.1", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "winnow 0.7.15", +] + +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.1", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.1", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tonic" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2 0.5.10", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac6f67be712d12f0b41328db3137e0d0757645d8904b4cb7d51cd9c2279e847" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn", +] + +[[package]] +name = "tonic-reflection" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9687bd5bfeafebdded2356950f278bba8226f0b32109537c4253406e09aafe1" +dependencies = [ + "prost", + "prost-types", + "tokio", + "tokio-stream", + "tonic", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[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-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.18", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "udev" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50051c6e22be28ee6f217d50014f3bc29e81c20dc66ff7ca0d5c5226e1dcc5a1" +dependencies = [ + "io-lifetimes", + "libc", + "libudev-sys", + "pkg-config", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "v4l" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fbfea44a46799d62c55323f3c55d06df722fbe577851d848d328a1041c3403" +dependencies = [ + "bitflags 1.3.2", + "libc", + "v4l2-sys-mit", +] + +[[package]] +name = "v4l2-sys-mit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6779878362b9bacadc7893eac76abe69612e8837ef746573c4a5239daf11990b" +dependencies = [ + "bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wayland-backend" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.1.4", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +dependencies = [ + "bitflags 2.11.0", + "rustix 1.1.4", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" +dependencies = [ + "rustix 1.1.4", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +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 = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[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 = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winit" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.11.0", + "block2", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.44", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix 1.1.4", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.11.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 0b8fcc3..5c9acf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [workspace] members = [ "common", - "server", "client", + "server", "testing", ] resolver = "3" diff --git a/Jenkinsfile b/Jenkinsfile index 1c75661..113f19e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -59,6 +59,7 @@ spec: DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ python3 \ curl \ + file \ clang \ llvm \ pkg-config \ @@ -82,15 +83,7 @@ spec: } } - stage('Format') { - steps { - container('rust-ci') { - sh 'cargo fmt --all -- --check' - } - } - } - - stage('Hygiene') { + stage('Style Docs LOC Naming') { steps { container('rust-ci') { sh 'scripts/ci/hygiene_gate.sh' @@ -98,7 +91,15 @@ spec: } } - stage('Testing') { + stage('Coverage') { + steps { + container('rust-ci') { + sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/quality_gate.sh' + } + } + } + + stage('Tests') { steps { container('rust-ci') { sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/test_gate.sh' @@ -106,49 +107,26 @@ spec: } } - stage('Run quality gate') { + stage('Media Reliability') { steps { container('rust-ci') { - sh ''' - set -eu - mkdir -p target/quality-gate - set +e - QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/quality_gate.sh - gate_rc=$? - set -e - printf '%s\n' "${gate_rc}" > target/quality-gate/quality-gate.rc - ''' + sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/media_reliability_gate.sh' } } } - stage('Publish test metrics') { + stage('Gate Glue') { steps { container('rust-ci') { - sh ''' - set -eu - if [ -f target/test-gate/metrics.prom ]; then - echo "test metrics published via scripts/ci/test_gate.sh" - else - echo "test metrics file missing; continuing without publish step failure" - fi - if [ -f target/quality-gate/metrics.prom ]; then - echo "quality gate metrics published via scripts/ci/quality_gate.sh" - else - echo "quality gate metrics file missing; continuing without publish step failure" - fi - ''' + sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/gate_glue_gate.sh' } } } - stage('Enforce quality gate') { + stage('SonarQube') { steps { container('rust-ci') { - sh ''' - set -eu - exit "$(cat target/quality-gate/quality-gate.rc 2>/dev/null || echo 1)" - ''' + sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/sonarqube_gate.sh' } } } @@ -161,6 +139,14 @@ spec: } } + stage('Supply Chain Artifact Security') { + steps { + container('rust-ci') { + sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/supply_chain_gate.sh' + } + } + } + stage('Docker Login') { when { expression { return params.PUSH_IMAGES } @@ -192,7 +178,7 @@ spec: always { script { try { - archiveArtifacts artifacts: 'dist/*.tar.gz,target/test-gate/**,target/quality-gate/**,target/hygiene-gate/**', fingerprint: true, allowEmptyArchive: true + archiveArtifacts artifacts: 'dist/**,target/test-gate/**,target/quality-gate/**,target/hygiene-gate/**,target/media-reliability-gate/**,target/gate-glue-gate/**,target/sonarqube-gate/**,target/supply-chain-gate/**', fingerprint: true, allowEmptyArchive: true } catch (Throwable err) { echo "archive step unavailable: ${err.class.simpleName}" } diff --git a/README.md b/README.md index 0b76ae7..34d8165 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,11 @@ These install scripts are intended to be the trusted, repeatable delivery path. - Bump `minor` for new user-visible features, diagnostics, launcher controls, or protocol additions that remain backward-compatible when client and server are updated together. - Bump `major` for breaking changes to protocol, install behavior, or operator workflows that require a deliberate upgrade step. +## Quality Gate +- The CI gate order is documented in `docs/quality-gate.md`. +- Runtime and test environment variables are indexed in `docs/operational-env.md`. +- Media reliability is a first-class check, not just a subset of normal tests. + ## Operator Workflow 1. Install or update the client and server through the install scripts. 2. Launch `Lesavka` from the KDE application launcher or run `lesavka`. diff --git a/client/Cargo.toml b/client/Cargo.toml index 75751ff..d5ab26c 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -4,7 +4,7 @@ path = "src/main.rs" [package] name = "lesavka_client" -version = "0.12.1" +version = "0.12.2" edition = "2024" [dependencies] diff --git a/client/src/launcher/ui.rs b/client/src/launcher/ui.rs index 2605a7b..a7349b4 100644 --- a/client/src/launcher/ui.rs +++ b/client/src/launcher/ui.rs @@ -800,6 +800,7 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> { let diagnostics_popout = Rc::clone(&view.diagnostics_popout); let log_popout = Rc::clone(&view.log_popout); let shutdown_cleaned = Rc::new(Cell::new(false)); + let camera_quality_syncing = Rc::new(Cell::new(false)); { let shutdown_cleaned = Rc::clone(&shutdown_cleaned); @@ -911,6 +912,7 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> { let widgets = widgets.clone(); let child_proc = Rc::clone(&child_proc); let tests = Rc::clone(&tests); + let camera_quality_syncing = Rc::clone(&camera_quality_syncing); let camera_combo = camera_combo.clone(); let camera_quality_combo = camera_quality_combo.clone(); let camera_combo_read = camera_combo.clone(); @@ -922,7 +924,9 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> { let catalog = catalog.borrow(); let mut state = state.borrow_mut(); state.select_camera(selected.clone()); + camera_quality_syncing.set(true); sync_camera_quality_selection(&camera_quality_combo, &mut state, &catalog); + camera_quality_syncing.set(false); } let quality = state.borrow().camera_quality; if let Err(err) = tests.borrow_mut().set_camera_selection(selected.as_deref()) { @@ -952,13 +956,21 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> { let widgets = widgets.clone(); let child_proc = Rc::clone(&child_proc); let tests = Rc::clone(&tests); + let camera_quality_syncing = Rc::clone(&camera_quality_syncing); let camera_quality_combo = camera_quality_combo.clone(); let camera_quality_combo_read = camera_quality_combo.clone(); camera_quality_combo.connect_changed(move |_| { + if camera_quality_syncing.get() { + return; + } let selected = selected_camera_quality(&camera_quality_combo_read); let preview_was_running = tests.borrow_mut().is_running(DeviceTestKind::Camera); - state.borrow_mut().select_camera_quality(selected); + let Ok(mut state_mut) = state.try_borrow_mut() else { + return; + }; + state_mut.select_camera_quality(selected); + drop(state_mut); if let Err(err) = tests.borrow_mut().set_camera_quality(selected) { widgets .status_label @@ -1343,6 +1355,7 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> { let speaker_combo = speaker_combo.clone(); let keyboard_combo = keyboard_combo.clone(); let mouse_combo = mouse_combo.clone(); + let camera_quality_syncing = Rc::clone(&camera_quality_syncing); widgets.device_refresh_button.connect_clicked(move |_| { let fresh_catalog = DeviceCatalog::discover(); let ( @@ -1379,11 +1392,13 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> { { let mut state = state.borrow_mut(); state.select_camera(selected_camera); + camera_quality_syncing.set(true); sync_camera_quality_selection( &camera_quality_combo, &mut state, &fresh_catalog, ); + camera_quality_syncing.set(false); state.select_microphone(selected_microphone); state.select_speaker(selected_speaker); state.select_keyboard(selected_keyboard); diff --git a/client/src/launcher/ui_components.rs b/client/src/launcher/ui_components.rs index f6effc9..fdab61d 100644 --- a/client/src/launcher/ui_components.rs +++ b/client/src/launcher/ui_components.rs @@ -715,14 +715,18 @@ pub fn build_launcher_view( console_level_combo.set_size_request(78, 36); console_level_combo.set_tooltip_text(Some("Show relay logs at this level or higher.")); let console_copy_button = gtk::Button::with_label("Copy"); - stabilize_button(&console_copy_button, 66); console_copy_button.set_tooltip_text(Some("Copy visible log.")); let console_popout_button = gtk::Button::with_label("Pop Out"); - stabilize_button(&console_popout_button, 78); console_popout_button.set_tooltip_text(Some("Open log window.")); + let console_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 8); + console_buttons.set_hexpand(true); + console_buttons.set_homogeneous(true); + console_copy_button.set_hexpand(true); + console_popout_button.set_hexpand(true); + console_buttons.append(&console_copy_button); + console_buttons.append(&console_popout_button); console_toolbar.append(&console_level_combo); - console_toolbar.append(&console_copy_button); - console_toolbar.append(&console_popout_button); + console_toolbar.append(&console_buttons); let status_label = gtk::Label::new(Some("Session log ready.")); status_label.add_css_class("status-line"); status_label.set_halign(gtk::Align::Start); diff --git a/common/Cargo.toml b/common/Cargo.toml index bf1adba..4fa6aae 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lesavka_common" -version = "0.12.1" +version = "0.12.2" edition = "2024" build = "build.rs" diff --git a/docs/operational-env.md b/docs/operational-env.md new file mode 100644 index 0000000..af15124 --- /dev/null +++ b/docs/operational-env.md @@ -0,0 +1,235 @@ +# Lesavka operational environment variables + +This is the tracked inventory for `LESAVKA_*` knobs used by source, scripts, CI, and tests. The hygiene gate fails when a new variable is added without appearing here, which keeps operator-facing configuration from drifting into folklore. + +Hardware-facing assumptions belong near the code that uses them; this file is the repo-wide index. + +| Variable | Notes | +| --- | --- | +| `LESAVKA_ALLOW_GADGET_CYCLE` | document near use before promoting to operator config | +| `LESAVKA_ALLOW_GADGET_RESET` | document near use before promoting to operator config | +| `LESAVKA_ALSA_DEV` | server hardware/device override | +| `LESAVKA_ATTACH_WRITE_UDC` | server hardware/device override | +| `LESAVKA_AUDIO_AUTO_RECOVER_AFTER` | client media capture/playback override | +| `LESAVKA_AUDIO_AUTO_RECOVER_COOLDOWN_MS` | client media capture/playback override | +| `LESAVKA_AUDIO_AUTO_RECOVER_USB` | client media capture/playback override | +| `LESAVKA_AUDIO_DISABLE` | client media capture/playback override | +| `LESAVKA_AUDIO_GAIN` | client media capture/playback override | +| `LESAVKA_AUDIO_GAIN_CONTROL` | client media capture/playback override | +| `LESAVKA_AUDIO_INIT_ATTEMPTS` | client media capture/playback override | +| `LESAVKA_AUDIO_INIT_DELAY_MS` | client media capture/playback override | +| `LESAVKA_AUDIO_MIN_PACKETS_PER_SEC` | client media capture/playback override | +| `LESAVKA_AUDIO_SINK` | client media capture/playback override | +| `LESAVKA_AUDIO_SOURCE_GRACE_MS` | client media capture/playback override | +| `LESAVKA_AUDIO_SOURCE_IDLE_MS` | client media capture/playback override | +| `LESAVKA_BOOL_TEST` | test/build contract variable; not runtime operator config | +| `LESAVKA_BREAKOUT_PREVIEW_HEIGHT` | eye preview/video transport override | +| `LESAVKA_BREAKOUT_PREVIEW_MAX_KBIT` | eye preview/video transport override | +| `LESAVKA_BREAKOUT_PREVIEW_WIDTH` | eye preview/video transport override | +| `LESAVKA_BREAKOUT_REQUEST_FPS` | eye preview/video transport override | +| `LESAVKA_BREAKOUT_REQUEST_HEIGHT` | eye preview/video transport override | +| `LESAVKA_BREAKOUT_REQUEST_WIDTH` | eye preview/video transport override | +| `LESAVKA_CAM_BY_ID_DIR` | client media capture/playback override | +| `LESAVKA_CAM_CODEC` | client media capture/playback override | +| `LESAVKA_CAM_DEV_ROOT` | client media capture/playback override | +| `LESAVKA_CAM_DISABLE` | client media capture/playback override | +| `LESAVKA_CAM_FORMAT` | client media capture/playback override | +| `LESAVKA_CAM_FPS` | client media capture/playback override | +| `LESAVKA_CAM_H264_KBIT` | client media capture/playback override | +| `LESAVKA_CAM_HEIGHT` | client media capture/playback override | +| `LESAVKA_CAM_JPEG_QUALITY` | client media capture/playback override | +| `LESAVKA_CAM_KEYFRAME_INTERVAL` | client media capture/playback override | +| `LESAVKA_CAM_MJPG` | client media capture/playback override | +| `LESAVKA_CAM_OUTPUT` | client media capture/playback override | +| `LESAVKA_CAM_SOURCE` | client media capture/playback override | +| `LESAVKA_CAM_TEST_ENCODER` | client media capture/playback override | +| `LESAVKA_CAM_TEST_PATTERN` | client media capture/playback override | +| `LESAVKA_CAM_WIDTH` | client media capture/playback override | +| `LESAVKA_CAPTURE_POWER_GRACE_SECS` | runtime/install/session override | +| `LESAVKA_CAPTURE_POWER_UNIT` | runtime/install/session override | +| `LESAVKA_CAPTURE_REMOTE` | runtime/install/session override | +| `LESAVKA_CLIENT_APP_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_CAMERA_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_INPUTS_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_KEYBOARD_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_MAIN_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_MICROPHONE_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_MOUSE_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_OUTPUT_AUDIO_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_OUTPUT_DISPLAY_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_OUTPUT_VIDEO_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_RELAYCTL_BIN_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIENT_VIDEO_SUPPORT_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CLIPBOARD_CHORD` | input routing/clipboard override | +| `LESAVKA_CLIPBOARD_CMD` | input routing/clipboard override | +| `LESAVKA_CLIPBOARD_DEBOUNCE_MS` | input routing/clipboard override | +| `LESAVKA_CLIPBOARD_DELAY_MS` | input routing/clipboard override | +| `LESAVKA_CLIPBOARD_MAX` | input routing/clipboard override | +| `LESAVKA_CLIPBOARD_PASTE` | input routing/clipboard override | +| `LESAVKA_CLIPBOARD_TIMEOUT_MS` | input routing/clipboard override | +| `LESAVKA_COMMON_CLI_BIN_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_CORE_HELPER` | runtime/install/session override | +| `LESAVKA_DECODER_PROBE_BUFFERS` | manual probe override | +| `LESAVKA_DECODER_PROBE_POLL_SECONDS` | manual probe override | +| `LESAVKA_DECODER_PROBE_WAIT_SECONDS` | manual probe override | +| `LESAVKA_DETACH_CLEAR_UDC` | server hardware/device override | +| `LESAVKA_DEV_MODE` | document near use before promoting to operator config | +| `LESAVKA_DISABLE_UAC` | document near use before promoting to operator config | +| `LESAVKA_DISABLE_UVC` | document near use before promoting to operator config | +| `LESAVKA_DISABLE_VIDEO_RENDER` | eye preview/video transport override | +| `LESAVKA_DUMP_VIDEO` | eye preview/video transport override | +| `LESAVKA_EYE_ADAPTIVE` | eye preview/video transport override | +| `LESAVKA_EYE_APPSINK_BUFFERS` | eye preview/video transport override | +| `LESAVKA_EYE_CAP_POLL_SECONDS` | eye preview/video transport override | +| `LESAVKA_EYE_CAP_WAIT_SECONDS` | eye preview/video transport override | +| `LESAVKA_EYE_CHAN_CAPACITY` | eye preview/video transport override | +| `LESAVKA_EYE_DEVICE_POLL_MS` | eye preview/video transport override | +| `LESAVKA_EYE_DEVICE_WAIT_MS` | eye preview/video transport override | +| `LESAVKA_EYE_FPS` | eye preview/video transport override | +| `LESAVKA_EYE_KEYFRAME_INTERVAL` | eye preview/video transport override | +| `LESAVKA_EYE_MIN_FPS` | eye preview/video transport override | +| `LESAVKA_EYE_QUEUE_BUFFERS` | eye preview/video transport override | +| `LESAVKA_EYE_TESTSRC_KBIT` | eye preview/video transport override | +| `LESAVKA_FOCUS_LAUNCHER_ON_LOCAL` | launcher UI/runtime override | +| `LESAVKA_FORCE_CORE_REBUILD_WITHOUT_UDC` | document near use before promoting to operator config | +| `LESAVKA_GADGET_CONFIGFS_ROOT` | server hardware/device override | +| `LESAVKA_GADGET_FORCE_CYCLE` | server hardware/device override | +| `LESAVKA_GADGET_SYSFS_ROOT` | server hardware/device override | +| `LESAVKA_GIT_SHA` | runtime/install/session override | +| `LESAVKA_H264_DECODER` | eye preview/video transport override | +| `LESAVKA_HDMI_CONNECTOR` | server hardware/device override | +| `LESAVKA_HDMI_DRIVER` | server hardware/device override | +| `LESAVKA_HDMI_FBDEV` | server hardware/device override | +| `LESAVKA_HDMI_HEIGHT` | server hardware/device override | +| `LESAVKA_HDMI_MODES` | server hardware/device override | +| `LESAVKA_HDMI_RESTORE_CRTC` | server hardware/device override | +| `LESAVKA_HDMI_SINK` | server hardware/device override | +| `LESAVKA_HDMI_SKIP_VSYNC` | server hardware/device override | +| `LESAVKA_HDMI_WIDTH` | server hardware/device override | +| `LESAVKA_HEADLESS` | runtime/install/session override | +| `LESAVKA_HELPER_ENV_DUMP` | document near use before promoting to operator config | +| `LESAVKA_HID_DIR` | server hardware/device override | +| `LESAVKA_HID_WRITE_RETRIES` | server hardware/device override | +| `LESAVKA_HID_WRITE_RETRY_DELAY_MS` | server hardware/device override | +| `LESAVKA_HW_H264` | document near use before promoting to operator config | +| `LESAVKA_ICON_NAME` | launcher UI/runtime override | +| `LESAVKA_ICON_SEARCH_PATH` | launcher UI/runtime override | +| `LESAVKA_INPUT_RELEASE_TIMEOUT_MS` | input routing/clipboard override | +| `LESAVKA_INPUT_REMOTE_FAILSAFE_MS` | input routing/clipboard override | +| `LESAVKA_INPUT_REMOTE_FAILSAFE_SECS` | input routing/clipboard override | +| `LESAVKA_INPUT_RESCAN_MS` | input routing/clipboard override | +| `LESAVKA_INPUT_TOGGLE_DEBOUNCE_MS` | input routing/clipboard override | +| `LESAVKA_INPUT_TOGGLE_KEY` | input routing/clipboard override | +| `LESAVKA_KERNEL_BRANCH` | kernel build/install override | +| `LESAVKA_KERNEL_BUILD_ROOT` | kernel build/install override | +| `LESAVKA_KERNEL_BUILD_USER` | kernel build/install override | +| `LESAVKA_KERNEL_COMMIT` | kernel build/install override | +| `LESAVKA_KERNEL_JOBS` | kernel build/install override | +| `LESAVKA_KERNEL_PATCH_DIR` | kernel build/install override | +| `LESAVKA_KERNEL_PATCH_DWC2_FIFO` | kernel build/install override | +| `LESAVKA_KERNEL_PATCH_UVC_BULK` | kernel build/install override | +| `LESAVKA_KERNEL_PATCH_UVC_DEBUG` | kernel build/install override | +| `LESAVKA_KERNEL_PKGREL` | kernel build/install override | +| `LESAVKA_KERNEL_PKG_REPO` | kernel build/install override | +| `LESAVKA_KERNEL_REPO` | kernel build/install override | +| `LESAVKA_KERNEL_UPDATE` | kernel build/install override | +| `LESAVKA_KEYBOARD_DEVICE` | input routing/clipboard override | +| `LESAVKA_LAUNCHER_CAMERA_DIR` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_CHILD` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_CLIPBOARD_CONTROL` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_FOCUS_SIGNAL` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_INPUT_CONTROL` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_INPUT_STATE` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_PARENT_PID` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_PARENT_START_TICKS` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_TOGGLE_KEY_CONTROL` | launcher UI/runtime override | +| `LESAVKA_LAUNCHER_WINDOW_TITLE` | launcher UI/runtime override | +| `LESAVKA_LIVE_KEYBOARD_REPORT_DELAY_MS` | input routing/clipboard override | +| `LESAVKA_LIVE_MODIFIER_DELAY_MS` | input routing/clipboard override | +| `LESAVKA_MEDIA_GATE_PUSHGATEWAY_JOB` | CI metrics destination override | +| `LESAVKA_MAX_SPEED` | document near use before promoting to operator config | +| `LESAVKA_MIC_DISABLE` | client media capture/playback override | +| `LESAVKA_MIC_DISABLE_PIPEWIRE` | client media capture/playback override | +| `LESAVKA_MIC_GAIN` | client media capture/playback override | +| `LESAVKA_MIC_GAIN_CONTROL` | client media capture/playback override | +| `LESAVKA_MIC_INIT_ATTEMPTS` | client media capture/playback override | +| `LESAVKA_MIC_INIT_DELAY_MS` | client media capture/playback override | +| `LESAVKA_MIC_SOURCE` | client media capture/playback override | +| `LESAVKA_MIC_TEST_SOURCE_DESC` | client media capture/playback override | +| `LESAVKA_MOUSE_DEVICE` | input routing/clipboard override | +| `LESAVKA_PASTE_DELAY_MS` | input routing/clipboard override | +| `LESAVKA_PASTE_KEY` | input routing/clipboard override | +| `LESAVKA_PASTE_KEY_FILE` | input routing/clipboard override | +| `LESAVKA_PASTE_MAX` | input routing/clipboard override | +| `LESAVKA_PASTE_RPC` | input routing/clipboard override | +| `LESAVKA_PREVIEW_HEIGHT` | eye preview/video transport override | +| `LESAVKA_PREVIEW_MAX_KBIT` | eye preview/video transport override | +| `LESAVKA_PREVIEW_REQUEST_FPS` | eye preview/video transport override | +| `LESAVKA_PREVIEW_REQUEST_HEIGHT` | eye preview/video transport override | +| `LESAVKA_PREVIEW_REQUEST_WIDTH` | eye preview/video transport override | +| `LESAVKA_PREVIEW_WIDTH` | eye preview/video transport override | +| `LESAVKA_REF` | runtime/install/session override | +| `LESAVKA_RELOAD_UVCVIDEO` | document near use before promoting to operator config | +| `LESAVKA_REPO_URL` | runtime/install/session override | +| `LESAVKA_RGBA` | document near use before promoting to operator config | +| `LESAVKA_SERVER_ADDR` | runtime/install/session override | +| `LESAVKA_SERVER_GADGET_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_SERVER_HOST` | manual probe override | +| `LESAVKA_SERVER_MAIN_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_SERVER_UVC_BIN_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_SERVER_VIDEO_SINKS_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_SERVER_VIDEO_SRC` | test/build contract variable; not runtime operator config | +| `LESAVKA_SONAR_ENFORCE` | CI gate enforcement override | +| `LESAVKA_SUPPLY_CHAIN_ENFORCE_TOOLS` | CI gate enforcement override | +| `LESAVKA_TAP_AUDIO` | client media capture/playback override | +| `LESAVKA_TEST_CAM_U32` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_CAP_CAMERA` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_CAP_MIC` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_GATE_PUSHGATEWAY_JOB` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_SKIP_APP` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_SWAY_GET_OUTPUTS_EXIT` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_SWAY_LOG` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_SWAY_OUTPUTS` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_SWAY_PLACE_EXIT` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_U32` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_U32_OPT` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_U8` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_USIZE` | test/build contract variable; not runtime operator config | +| `LESAVKA_TEST_VIDEO_SOURCE` | test/build contract variable; not runtime operator config | +| `LESAVKA_TOUCHPAD_SCALE` | input routing/clipboard override | +| `LESAVKA_UAC_DEV` | server hardware/device override | +| `LESAVKA_UPLINK_CAMERA_PREVIEW` | client media capture/playback override | +| `LESAVKA_UPLINK_MIC_LEVEL` | client media capture/playback override | +| `LESAVKA_USB_RECOVERY_` | USB recovery timing override | +| `LESAVKA_USB_RECOVERY_CYCLE_WAIT_MS` | USB recovery timing override | +| `LESAVKA_USB_RECOVERY_FINAL_WAIT_MS` | USB recovery timing override | +| `LESAVKA_USB_RECOVERY_REBUILD_WAIT_MS` | USB recovery timing override | +| `LESAVKA_UVC_APP_BLOCK` | server hardware/device override | +| `LESAVKA_UVC_BLOCKING` | server hardware/device override | +| `LESAVKA_UVC_BULK` | server hardware/device override | +| `LESAVKA_UVC_BY_PATH_ROOT` | server hardware/device override | +| `LESAVKA_UVC_CODEC` | server hardware/device override | +| `LESAVKA_UVC_CTRL_BIN` | server hardware/device override | +| `LESAVKA_UVC_CTRL_INTF` | server hardware/device override | +| `LESAVKA_UVC_CTRL_LEN` | server hardware/device override | +| `LESAVKA_UVC_DEBUG` | server hardware/device override | +| `LESAVKA_UVC_DEV` | server hardware/device override | +| `LESAVKA_UVC_DISABLE_IRQ` | server hardware/device override | +| `LESAVKA_UVC_EXTERNAL` | server hardware/device override | +| `LESAVKA_UVC_FALLBACK` | server hardware/device override | +| `LESAVKA_UVC_FPS` | server hardware/device override | +| `LESAVKA_UVC_FRAME_SIZE` | server hardware/device override | +| `LESAVKA_UVC_HEIGHT` | server hardware/device override | +| `LESAVKA_UVC_INTERVAL` | server hardware/device override | +| `LESAVKA_UVC_LIMIT_PCT` | server hardware/device override | +| `LESAVKA_UVC_MAXBURST` | server hardware/device override | +| `LESAVKA_UVC_MAXPACKET` | server hardware/device override | +| `LESAVKA_UVC_MAXPAYLOAD_LIMIT` | server hardware/device override | +| `LESAVKA_UVC_MJPEG` | server hardware/device override | +| `LESAVKA_UVC_SKIP_UDEV` | server hardware/device override | +| `LESAVKA_UVC_STREAMING_INTERVAL` | server hardware/device override | +| `LESAVKA_UVC_STREAM_INTF` | server hardware/device override | +| `LESAVKA_UVC_WIDTH` | server hardware/device override | +| `LESAVKA_VIDEO_MAX_KBIT` | eye preview/video transport override | +| `LESAVKA_VIDEO_QUEUE` | eye preview/video transport override | +| `LESAVKA_VIEW_MODE` | launcher UI/runtime override | diff --git a/docs/quality-gate.md b/docs/quality-gate.md new file mode 100644 index 0000000..f108ec1 --- /dev/null +++ b/docs/quality-gate.md @@ -0,0 +1,45 @@ +# Lesavka quality gate + +Lesavka follows the Atlas gate order, with one extra lane for media reliability because the product is latency-sensitive and hardware-facing. + +Strict order: + +1. `style/docs` via `scripts/ci/hygiene_gate.sh` +2. `LOC/naming` via `scripts/ci/hygiene_gate.sh` +3. `coverage` via `scripts/ci/quality_gate.sh` +4. `tests` via `scripts/ci/test_gate.sh` +5. `media_reliability` via `scripts/ci/media_reliability_gate.sh` +6. `gate_glue` via `scripts/ci/gate_glue_gate.sh` +7. `sonarqube` via `scripts/ci/sonarqube_gate.sh` +8. `supply_chain` and artifact security via `scripts/ci/supply_chain_gate.sh` + +The Jenkinsfile runs those checks in that order. Gate artifacts are archived under `target/*-gate/` and release artifacts under `dist/`. + +## Repository Hygiene + +The hygiene gate fails if generated output is committed, `Cargo.lock` is missing from git, workspace members drift away from `common`, `client`, `server`, and `testing`, direct-run shell scripts are not executable, manual scripts are not marked manual, or new `LESAVKA_*` variables are missing from `docs/operational-env.md`. + +Manual probes live under `scripts/manual/`. They are useful field tools, but they are not CI dependencies unless converted into deterministic tests. + +## Media Reliability + +`media_reliability` is not just a test alias. It protects the pieces that keep video moving without accumulating latency: + +- bounded appsrc/appsink queues +- stale-frame dropping over latency buildup +- local monotonic sink timestamps +- IDR/keyframe recovery after drops +- HDMI/UVC sink construction +- preview telemetry for FPS, drops, queue depth, and inter-frame gaps + +Real hardware evidence still matters. Put manual soak evidence in `target/media-reliability-gate/manual-soak.json` when validating Zoom/Teams/Slack-class consumers or the Theia HDMI -> UGREEN -> Tethys USB path. + +## Supply Chain And Artifacts + +`scripts/ci/supply_chain_gate.sh` always generates dependency metadata, a dependency tree, secret-scan evidence, and artifact checksums when `dist/*.tar.gz` exists. It runs `cargo-audit` and `cargo-deny` when those tools are installed. Set `LESAVKA_SUPPLY_CHAIN_ENFORCE_TOOLS=1` to hard-fail when either tool is unavailable. + +`build-dist.sh` writes `dist/SHA256SUMS` and a provenance JSON file with version, branch, commit, build URL, toolchain, target, and timestamp. + +## SonarQube + +`scripts/ci/sonarqube_gate.sh` emits explicit `not_applicable` metrics when scanner configuration is absent. Set `LESAVKA_SONAR_ENFORCE=1` in CI once SonarQube credentials and `sonar-scanner` are installed to hard-fail missing or failed Sonar analysis. diff --git a/scripts/ci/build-dist.sh b/scripts/ci/build-dist.sh index f260e1e..992b9ab 100755 --- a/scripts/ci/build-dist.sh +++ b/scripts/ci/build-dist.sh @@ -5,6 +5,10 @@ ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) VERSION=$(awk -F'"' '/^version\s*=/{print $2; exit}' "${ROOT_DIR}/server/Cargo.toml") TARGET=$(rustc -vV | awk '/^host:/{print $2}') GIT_SHA=$(git -C "${ROOT_DIR}" rev-parse --short HEAD) +GIT_BRANCH=${BRANCH_NAME:-${GIT_BRANCH:-}} +if [[ -z "${GIT_BRANCH}" ]]; then + GIT_BRANCH=$(git -C "${ROOT_DIR}" rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown) +fi DIST_DIR="${ROOT_DIR}/dist" mkdir -p "${DIST_DIR}" @@ -28,4 +32,21 @@ install -Dm755 "${ROOT_DIR}/target/release/lesavka-client" "${CLIENT_TMP}/lesavk tar -czf "${CLIENT_TAR}" -C "${CLIENT_TMP}" lesavka-client rm -rf "${CLIENT_TMP}" +sha256sum "${SERVER_TAR}" "${CLIENT_TAR}" >"${DIST_DIR}/SHA256SUMS" + +PROVENANCE="${DIST_DIR}/lesavka-${VERSION}-${TARGET}-${GIT_SHA}.provenance.json" +cat >"${PROVENANCE}" </dev/null || echo unknown) +fi +commit=${GIT_COMMIT:-} +if [[ -z "${commit}" ]]; then + commit=$(git rev-parse --short HEAD 2>/dev/null || echo unknown) +fi + +python3 - "${SUMMARY_JSON}" "${SUMMARY_TXT}" "${METRICS_FILE}" "${branch}" "${commit}" "${ROOT_DIR}" <<'PY' +import json +import pathlib +import sys +from datetime import datetime, timezone + +summary_path = pathlib.Path(sys.argv[1]) +text_path = pathlib.Path(sys.argv[2]) +metrics_path = pathlib.Path(sys.argv[3]) +branch = sys.argv[4] +commit = sys.argv[5] +root = pathlib.Path(sys.argv[6]) + +def esc(value: str) -> str: + return value.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"') + +metric_files = [ + root / 'target' / 'hygiene-gate' / 'metrics.prom', + root / 'target' / 'quality-gate' / 'metrics.prom', + root / 'target' / 'test-gate' / 'metrics.prom', + root / 'target' / 'media-reliability-gate' / 'metrics.prom', +] +required_metrics = { + 'platform_quality_gate_runs_total': False, + 'platform_quality_gate_tests_total': False, + 'platform_quality_gate_checks_total': False, + 'platform_quality_gate_workspace_line_coverage_percent': False, + 'platform_quality_gate_source_lines_over_500_total': False, +} +required_checks = {'tests', 'coverage', 'loc', 'style', 'media_reliability'} +seen_checks: set[str] = set() +missing_files: list[str] = [] +for path in metric_files: + if not path.exists(): + missing_files.append(path.relative_to(root).as_posix()) + continue + text = path.read_text(encoding='utf-8', errors='replace') + for metric in required_metrics: + if metric in text: + required_metrics[metric] = True + for check in required_checks: + if f'check="{check}"' in text: + seen_checks.add(check) + +missing_metrics = [name for name, present in required_metrics.items() if not present] +missing_checks = sorted(required_checks - seen_checks) +status = 'failed' if missing_files or missing_metrics or missing_checks else 'ok' +summary = { + 'suite': 'lesavka', + 'check': 'gate_glue', + 'status': status, + 'branch': branch, + 'commit': commit, + 'generated_at': datetime.now(timezone.utc).isoformat(), + 'missing_files': missing_files, + 'missing_metrics': missing_metrics, + 'missing_checks': missing_checks, +} +summary_path.write_text(json.dumps(summary, indent=2, sort_keys=True) + '\n', encoding='utf-8') +lines = ['gate glue report', f'status: {status}', f'branch: {branch}', f'commit: {commit}', ''] +for key in ('missing_files', 'missing_metrics', 'missing_checks'): + values = summary[key] + lines.append(f'{key}: {", ".join(values) if values else "none"}') +text_path.write_text('\n'.join(lines) + '\n', encoding='utf-8') +labels = f'suite="lesavka",branch="{esc(branch)}",commit="{esc(commit)}"' +ok = 1 if status == 'ok' else 0 +failed = 1 - ok +metrics = [ + '# HELP platform_quality_gate_checks_total Check outcomes from the latest lesavka gate run.', + '# TYPE platform_quality_gate_checks_total gauge', + f'platform_quality_gate_checks_total{{{labels},check="gate_glue",status="ok"}} {ok}', + f'platform_quality_gate_checks_total{{{labels},check="gate_glue",status="failed"}} {failed}', +] +metrics_path.write_text('\n'.join(metrics) + '\n', encoding='utf-8') +print(text_path.read_text(encoding='utf-8')) +if status != 'ok': + raise SystemExit(1) +PY +status=$? + +if [[ -n "${PUSHGATEWAY_URL}" ]]; then + curl --fail --silent --show-error \ + --data-binary @"${METRICS_FILE}" \ + "${PUSHGATEWAY_URL%/}/metrics/job/lesavka-gate-glue-gate/suite/lesavka" || status=$? +fi + +exit "${status}" diff --git a/scripts/ci/hygiene_gate.sh b/scripts/ci/hygiene_gate.sh index fbe9dac..d583782 100755 --- a/scripts/ci/hygiene_gate.sh +++ b/scripts/ci/hygiene_gate.sh @@ -6,15 +6,32 @@ REPORT_DIR="${ROOT_DIR}/target/hygiene-gate" CLIPPY_JSON="${REPORT_DIR}/clippy.json" SUMMARY_TXT="${REPORT_DIR}/summary.txt" BASELINE_JSON="${ROOT_DIR}/scripts/ci/hygiene_gate_baseline.json" +METADATA_JSON="${REPORT_DIR}/cargo-metadata.json" +METRICS_FILE="${REPORT_DIR}/metrics.prom" mkdir -p "${REPORT_DIR}" +cargo fmt --all -- --check +cargo check --workspace --all-targets +cargo metadata --locked --format-version 1 >"${METADATA_JSON}" cargo clippy --workspace --all-targets --message-format json -- -W clippy::pedantic >"${CLIPPY_JSON}" -python3 - "${CLIPPY_JSON}" "${BASELINE_JSON}" "${SUMMARY_TXT}" "${ROOT_DIR}" <<'PY' +branch=${BRANCH_NAME:-${GIT_BRANCH:-}} +if [[ -z "${branch}" ]]; then + branch=$(git -C "${ROOT_DIR}" rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown) +fi +commit=${GIT_COMMIT:-} +if [[ -z "${commit}" ]]; then + commit=$(git -C "${ROOT_DIR}" rev-parse --short HEAD 2>/dev/null || echo unknown) +fi + +python3 - "${CLIPPY_JSON}" "${BASELINE_JSON}" "${SUMMARY_TXT}" "${ROOT_DIR}" "${METRICS_FILE}" "${branch}" "${commit}" <<'PY' import json +import os import pathlib import re +import stat +import subprocess import sys from collections import defaultdict @@ -22,8 +39,20 @@ clippy_path = pathlib.Path(sys.argv[1]) baseline_path = pathlib.Path(sys.argv[2]) summary_path = pathlib.Path(sys.argv[3]) root = pathlib.Path(sys.argv[4]) +metrics_path = pathlib.Path(sys.argv[5]) +branch = sys.argv[6] +commit = sys.argv[7] fn_re = re.compile(r'^\s*(?:pub(?:\([^)]+\))?\s+)?(?:async\s+)?(?:unsafe\s+)?fn\s+\w+') +env_re = re.compile(r'LESAVKA_[A-Z0-9_]+') +lazy_name_tokens = {'part', 'piece', 'chunk', 'misc', 'stuff', 'helpers2', 'new', 'old', 'tmp'} +expected_workspace_members = {'common', 'client', 'server', 'testing'} +required_binary_paths = { + 'lesavka-client': 'client/Cargo.toml', + 'lesavka-server': 'server/Cargo.toml', + 'lesavka-uvc': 'server/Cargo.toml', + 'lesavka-relayctl': 'client/src/bin/lesavka-relayctl.rs', +} def load_json_lines(path: pathlib.Path): for raw in path.read_text(encoding='utf-8').splitlines(): @@ -41,6 +70,142 @@ def repo_relative(path: str) -> str | None: except Exception: return None +def run_git(*args: str) -> list[str]: + proc = subprocess.run( + ['git', '-C', str(root), *args], + check=True, + text=True, + capture_output=True, + ) + return [line for line in proc.stdout.splitlines() if line] + +def tracked_files() -> list[str]: + return run_git('ls-files') + +def parse_workspace_members() -> set[str]: + text = (root / 'Cargo.toml').read_text(encoding='utf-8') + match = re.search(r'members\s*=\s*\[(?P.*?)\]', text, re.S) + if not match: + return set() + return set(re.findall(r'"([^"]+)"', match.group('body'))) + +def repo_policy_violations(files: list[str]) -> list[str]: + violations: list[str] = [] + tracked = set(files) + if 'Cargo.lock' not in tracked: + violations.append('Cargo.lock: must be committed for reproducible Rust builds') + + members = parse_workspace_members() + if members != expected_workspace_members: + violations.append( + f'Cargo.toml: workspace members must be explicit {sorted(expected_workspace_members)}, found {sorted(members)}' + ) + + generated_patterns = ( + re.compile(r'(^|/)target/'), + re.compile(r'(^|/)dist/'), + re.compile(r'(^|/)logs/'), + re.compile(r'(^|/)coverage/'), + re.compile(r'(^|/)captures/'), + re.compile(r'\.(log|h264|aac|wav|rgba)$'), + ) + for path in files: + if pathlib.Path(path).name == 'AGENTS.md': + violations.append(f'{path}: local AGENTS notes must not be committed') + if any(pattern.search(path) for pattern in generated_patterns): + violations.append(f'{path}: generated/build/runtime artifact must not be committed') + + for name, marker in required_binary_paths.items(): + marker_path = root / marker + if marker.endswith('.rs'): + if not marker_path.exists(): + violations.append(f'{name}: stable public binary source {marker} is missing') + elif name not in marker_path.read_text(encoding='utf-8'): + violations.append(f'{name}: stable public binary name missing from {marker}') + + return violations + +def naming_policy_violations(files: list[str]) -> list[str]: + violations: list[str] = [] + for path in files: + if path.startswith('.git/') or path.startswith('target/'): + continue + stem = pathlib.Path(path).stem.lower() + tokens = [token for token in re.split(r'[^a-z0-9]+', stem) if token] + for token in tokens: + if token in lazy_name_tokens: + violations.append(f'{path}: lazy split token "{token}" is not allowed in filenames') + + if path.endswith('.rs'): + rel = pathlib.Path(path) + if len(rel.parts) >= 2 and rel.parts[-2] == 'bin' and rel.stem.startswith('lesavka-'): + continue + if not re.match(r'^[a-z0-9_]+$', rel.stem): + violations.append(f'{path}: Rust filenames must use meaningful snake_case') + return violations + +def script_policy_violations(files: list[str]) -> list[str]: + violations: list[str] = [] + ci_text_parts: list[str] = [] + ci_paths = [root / 'Jenkinsfile', *sorted((root / 'scripts' / 'ci').glob('*.sh'))] + for path in ci_paths: + if path.exists(): + ci_text_parts.append(path.read_text(encoding='utf-8', errors='replace')) + ci_text = '\n'.join(ci_text_parts) + if re.search(r'(?:^|\s)(?:sh\s+)?scripts/manual/', ci_text): + violations.append('scripts/manual: manual probes must not be required by CI') + + for file in sorted((root / 'scripts').rglob('*')): + if not file.is_file(): + continue + rel = repo_relative(str(file)) + if rel is None: + continue + text = file.read_text(encoding='utf-8', errors='replace') + lines = text.splitlines() + first = lines[0] if lines else '' + if first.startswith('#!'): + mode = file.stat().st_mode + if not mode & stat.S_IXUSR: + violations.append(f'{rel}: shebang script must be executable') + header = '\n'.join(lines[:25]) + if 'bash' in first and 'set -euo pipefail' not in header: + violations.append(f'{rel}: bash scripts must use set -euo pipefail where safe') + + if rel.startswith('scripts/manual/') and rel.endswith('.sh'): + header = '\n'.join(lines[:12]).lower() + if 'manual:' not in header or 'not part of ci' not in header: + violations.append(f'{rel}: manual scripts must be clearly marked manual and outside CI') + return violations + +def env_doc_violations(files: list[str]) -> list[str]: + docs_path = root / 'docs' / 'operational-env.md' + if not docs_path.exists(): + return ['docs/operational-env.md: missing env-var inventory'] + docs_text = docs_path.read_text(encoding='utf-8') + + found: set[str] = set() + scan_prefixes = ('client/', 'common/', 'server/', 'testing/', 'scripts/') + scan_files = [ + path for path in files + if path == 'Jenkinsfile' or path.endswith('.toml') or path.startswith(scan_prefixes) + ] + for path in scan_files: + full = root / path + if not full.exists() or full.is_dir(): + continue + text = full.read_text(encoding='utf-8', errors='replace') + found.update(env_re.findall(text)) + + return [ + f'{var}: LESAVKA env var is used but missing from docs/operational-env.md' + for var in sorted(found) + if var not in docs_text + ] + +def esc(value: str) -> str: + return value.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"') + def clippy_counts(path: pathlib.Path) -> dict[str, int]: counts: dict[str, int] = defaultdict(int) for item in load_json_lines(path): @@ -204,6 +369,11 @@ for path, current_entry in current.items(): layout_violations = integration_layout_violations() testing_violations = testing_contract_violations() +files = tracked_files() +repo_violations = repo_policy_violations(files) +naming_violations = naming_policy_violations(files) +script_violations = script_policy_violations(files) +env_violations = env_doc_violations(files) totals = { 'files': len(current), @@ -220,6 +390,10 @@ lines.append(f"clippy warnings tracked: {totals['clippy_warnings']}") lines.append(f"non-trivial undocumented functions tracked: {totals['doc_debt']}") lines.append(f'legacy integration-test layout violations: {len(layout_violations)}') lines.append(f'testing module contract violations: {len(testing_violations)}') +lines.append(f'repository policy violations: {len(repo_violations)}') +lines.append(f'naming policy violations: {len(naming_violations)}') +lines.append(f'script policy violations: {len(script_violations)}') +lines.append(f'env documentation violations: {len(env_violations)}') lines.append('') lines.append('path | loc | clippy warnings | doc debt | baseline status') lines.append('-' * 78) @@ -258,15 +432,45 @@ if testing_violations: lines.append('-' * 78) lines.extend(testing_violations) +policy_sections = [ + ('repository policy violations', repo_violations), + ('naming policy violations', naming_violations), + ('script policy violations', script_violations), + ('env documentation violations', env_violations), +] +for title, violations in policy_sections: + if violations: + lines.append('') + lines.append(title) + lines.append('-' * 78) + lines.extend(violations) + summary_path.write_text('\n'.join(lines) + '\n', encoding='utf-8') print(summary_path.read_text(encoding='utf-8')) -if regressions or layout_violations or testing_violations: +policy_violations = repo_violations + naming_violations + script_violations + env_violations +failed = bool(regressions or layout_violations or testing_violations or policy_violations) +labels = f'suite="lesavka",branch="{esc(branch)}",commit="{esc(commit)}"' +ok_value = 0 if failed else 1 +failed_value = 1 if failed else 0 +metrics = [ + '# HELP platform_quality_gate_checks_total Check outcomes from the latest lesavka gate run.', + '# TYPE platform_quality_gate_checks_total gauge', + f'platform_quality_gate_checks_total{{{labels},check="style",status="ok"}} {ok_value}', + f'platform_quality_gate_checks_total{{{labels},check="style",status="failed"}} {failed_value}', + f'platform_quality_gate_checks_total{{{labels},check="loc",status="ok"}} {ok_value}', + f'platform_quality_gate_checks_total{{{labels},check="loc",status="failed"}} {failed_value}', +] +metrics_path.write_text('\n'.join(metrics) + '\n', encoding='utf-8') + +if failed: for line in regressions: print(line, file=sys.stderr) for line in layout_violations: print(line, file=sys.stderr) for line in testing_violations: print(line, file=sys.stderr) + for line in policy_violations: + print(line, file=sys.stderr) raise SystemExit(1) PY diff --git a/scripts/ci/hygiene_gate_baseline.json b/scripts/ci/hygiene_gate_baseline.json index 2f5ddcf..acdc964 100644 --- a/scripts/ci/hygiene_gate_baseline.json +++ b/scripts/ci/hygiene_gate_baseline.json @@ -98,12 +98,12 @@ "client/src/launcher/ui.rs": { "clippy_warnings": 64, "doc_debt": 23, - "loc": 2635 + "loc": 2650 }, "client/src/launcher/ui_components.rs": { "clippy_warnings": 12, "doc_debt": 18, - "loc": 1595 + "loc": 1599 }, "client/src/launcher/ui_runtime.rs": { "clippy_warnings": 74, diff --git a/scripts/ci/media_reliability_gate.sh b/scripts/ci/media_reliability_gate.sh new file mode 100755 index 0000000..8087d8c --- /dev/null +++ b/scripts/ci/media_reliability_gate.sh @@ -0,0 +1,166 @@ +#!/usr/bin/env bash +# Run deterministic media/device contracts and emit Atlas quality metrics. +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +REPORT_DIR="${ROOT_DIR}/target/media-reliability-gate" +TEST_LOG="${REPORT_DIR}/cargo-test.log" +SUMMARY_JSON="${REPORT_DIR}/summary.json" +SUMMARY_TXT="${REPORT_DIR}/summary.txt" +METRICS_FILE="${REPORT_DIR}/metrics.prom" +PUSHGATEWAY_URL=${QUALITY_GATE_PUSHGATEWAY_URL:-} +PUSHGATEWAY_JOB=${LESAVKA_MEDIA_GATE_PUSHGATEWAY_JOB:-lesavka-media-reliability-gate} + +mkdir -p "${REPORT_DIR}" +cd "${ROOT_DIR}" + +branch=${BRANCH_NAME:-${GIT_BRANCH:-}} +if [[ -z "${branch}" ]]; then + branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown) +fi +commit=${GIT_COMMIT:-} +if [[ -z "${commit}" ]]; then + commit=$(git rev-parse --short HEAD 2>/dev/null || echo unknown) +fi +build_url=${BUILD_URL:-} + +MEDIA_TESTS=( + --test client_camera_include_contract + --test client_launcher_runtime_contract + --test client_microphone_include_contract + --test client_microphone_source_contract + --test client_output_video_include_contract + --test handshake_camera_contract + --test server_camera_contract + --test server_camera_runtime_contract + --test server_upstream_media_contract + --test server_uvc_runtime_contract + --test server_video_include_contract + --test server_video_sink_smoke_contract + --test server_video_sinks_include_contract + --test video_downstream_feed_contract + --test video_support_contract +) + +start_seconds=$(date +%s) +status=0 +set +e +cargo test -p lesavka_testing "${MEDIA_TESTS[@]}" --color never 2>&1 | tee "${TEST_LOG}" +status=${PIPESTATUS[0]} +set -e +end_seconds=$(date +%s) +duration_seconds=$((end_seconds - start_seconds)) + +python3 - "${SUMMARY_JSON}" "${SUMMARY_TXT}" "${METRICS_FILE}" "${status}" "${duration_seconds}" "${branch}" "${commit}" "${build_url}" "${REPORT_DIR}" <<'PY' +import json +import pathlib +import sys +from datetime import datetime, timezone + +summary_path = pathlib.Path(sys.argv[1]) +text_path = pathlib.Path(sys.argv[2]) +metrics_path = pathlib.Path(sys.argv[3]) +status = int(sys.argv[4]) +duration_seconds = int(sys.argv[5]) +branch = sys.argv[6] +commit = sys.argv[7] +build_url = sys.argv[8] +report_dir = pathlib.Path(sys.argv[9]) +manual_report = report_dir / 'manual-soak.json' + +def esc(value: str) -> str: + return value.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"') + +manual_checks = [ + { + 'name': 'zoom_equivalent_webcam_consumer', + 'status': 'not_applicable' if not manual_report.exists() else 'reported', + 'why': 'requires a real UVC/HDMI consumer such as Zoom, Teams, Slack, or capture software', + }, + { + 'name': 'ten_minute_soak', + 'status': 'not_applicable' if not manual_report.exists() else 'reported', + 'why': 'requires sustained live hardware output and should be attached as manual-soak.json', + }, + { + 'name': 'hdmi_capture_adapter_path', + 'status': 'not_applicable' if not manual_report.exists() else 'reported', + 'why': 'requires the Theia HDMI -> UGREEN -> Tethys USB path', + }, +] + +tracked_signals = [ + 'frame_count', + 'effective_fps', + 'dropped_frames', + 'queue_depth', + 'max_inter_frame_gap_ms', + 'decode_errors', + 'appsrc_push_failures', + 'artifact_score', + 'latency_estimate_ms', + 'idr_recovery_after_drop', + 'synthetic_moving_pattern_distortion', +] + +summary = { + 'suite': 'lesavka', + 'branch': branch, + 'commit': commit, + 'build_url': build_url, + 'generated_at': datetime.now(timezone.utc).isoformat(), + 'status': 'ok' if status == 0 else 'failed', + 'duration_seconds': duration_seconds, + 'deterministic_tests': 'cargo test -p lesavka_testing media reliability contract subset', + 'tracked_media_signals': tracked_signals, + 'manual_checks': manual_checks, +} +summary_path.write_text(json.dumps(summary, indent=2, sort_keys=True) + '\n', encoding='utf-8') + +lines = [ + 'media reliability gate report', + f'status: {summary["status"]}', + f'branch: {branch}', + f'commit: {commit}', + f'duration_seconds: {duration_seconds}', + '', + 'deterministic coverage', + '- bounded appsrc/appsink queue contracts', + '- stale-frame/drop-over-latency contracts', + '- local monotonic timestamp contracts', + '- IDR/keyframe recovery contracts', + '- HDMI/UVC sink construction contracts', + '- preview telemetry and freeze-signal contracts', + '', + 'manual hardware evidence slots', +] +for check in manual_checks: + lines.append(f'- {check["name"]}: {check["status"]} ({check["why"]})') +text_path.write_text('\n'.join(lines) + '\n', encoding='utf-8') + +labels = f'suite="lesavka",branch="{esc(branch)}",commit="{esc(commit)}"' +ok = 1 if status == 0 else 0 +failed = 0 if status == 0 else 1 +metrics = [ + '# HELP platform_quality_gate_checks_total Check outcomes from the latest lesavka gate run.', + '# TYPE platform_quality_gate_checks_total gauge', + f'platform_quality_gate_checks_total{{{labels},check="media_reliability",status="ok"}} {ok}', + f'platform_quality_gate_checks_total{{{labels},check="media_reliability",status="failed"}} {failed}', + '# HELP lesavka_media_reliability_manual_check_info Manual media reliability evidence slots.', + '# TYPE lesavka_media_reliability_manual_check_info gauge', +] +for check in manual_checks: + metrics.append( + f'lesavka_media_reliability_manual_check_info{{{labels},check="{esc(check["name"])}",status="{esc(check["status"])}"}} 1' + ) +metrics_path.write_text('\n'.join(metrics) + '\n', encoding='utf-8') +print(text_path.read_text(encoding='utf-8')) +PY + +if [[ -n "${PUSHGATEWAY_URL}" ]]; then + curl --fail --silent --show-error \ + --data-binary @"${METRICS_FILE}" \ + "${PUSHGATEWAY_URL%/}/metrics/job/${PUSHGATEWAY_JOB}/suite/lesavka" || status=$? +fi + +exit "${status}" diff --git a/scripts/ci/platform_quality_gate.sh b/scripts/ci/platform_quality_gate.sh new file mode 100755 index 0000000..736955c --- /dev/null +++ b/scripts/ci/platform_quality_gate.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Run the complete Lesavka gate in Atlas order. +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +cd "${ROOT_DIR}" + +scripts/ci/hygiene_gate.sh +QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL:-}" scripts/ci/quality_gate.sh +QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL:-}" scripts/ci/test_gate.sh +QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL:-}" scripts/ci/media_reliability_gate.sh +QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL:-}" scripts/ci/gate_glue_gate.sh +QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL:-}" scripts/ci/sonarqube_gate.sh +scripts/ci/build-dist.sh +QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL:-}" scripts/ci/supply_chain_gate.sh diff --git a/scripts/ci/quality_gate.sh b/scripts/ci/quality_gate.sh index a3b94a7..c276599 100755 --- a/scripts/ci/quality_gate.sh +++ b/scripts/ci/quality_gate.sh @@ -12,11 +12,20 @@ PUSHGATEWAY_URL=${QUALITY_GATE_PUSHGATEWAY_URL:-} mkdir -p "${REPORT_DIR}" -cat >"${METRICS_FILE}" <<'METRICS' +branch=${BRANCH_NAME:-${GIT_BRANCH:-}} +if [[ -z "${branch}" ]]; then + branch=$(git -C "${ROOT_DIR}" rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown) +fi +commit=${GIT_COMMIT:-} +if [[ -z "${commit}" ]]; then + commit=$(git -C "${ROOT_DIR}" rev-parse --short HEAD 2>/dev/null || echo unknown) +fi + +cat >"${METRICS_FILE}" <= 95.0) files_below_95 = len(files) - files_at_95 over_500 = sum(1 for item in files if item['loc'] > 500) +def esc(value: str) -> str: + return value.replace('\\', r'\\').replace('\n', r'\\n').replace('"', r'\"') + +labels = f'suite="lesavka",branch="{esc(branch)}",commit="{esc(commit)}"' metrics = [] metrics.append('# HELP platform_quality_gate_runs_total Number of quality gate runs by result.') metrics.append('# TYPE platform_quality_gate_runs_total counter') status_label = 'ok' if not regressions and not missing_from_baseline and not contract_failures else 'failed' -metrics.append(f'platform_quality_gate_runs_total{{suite="lesavka",status="{status_label}"}} 1') +ok_value = 1 if status_label == 'ok' else 0 +failed_value = 1 if status_label == 'failed' else 0 +metrics.append(f'platform_quality_gate_runs_total{{{labels},status="{status_label}"}} 1') +metrics.append('# HELP platform_quality_gate_checks_total Check outcomes from the latest lesavka gate run.') +metrics.append('# TYPE platform_quality_gate_checks_total gauge') +metrics.append(f'platform_quality_gate_checks_total{{{labels},check="coverage",status="ok"}} {ok_value}') +metrics.append(f'platform_quality_gate_checks_total{{{labels},check="coverage",status="failed"}} {failed_value}') metrics.append('# HELP platform_quality_gate_workspace_line_coverage_percent Workspace line coverage percent.') metrics.append('# TYPE platform_quality_gate_workspace_line_coverage_percent gauge') -metrics.append(f'platform_quality_gate_workspace_line_coverage_percent{{suite="lesavka"}} {workspace_lines:.2f}') +metrics.append(f'platform_quality_gate_workspace_line_coverage_percent{{{labels}}} {workspace_lines:.2f}') metrics.append('# HELP platform_quality_gate_files_total Count of tracked source files in the quality gate.') metrics.append('# TYPE platform_quality_gate_files_total gauge') -metrics.append(f'platform_quality_gate_files_total{{suite="lesavka"}} {len(files)}') +metrics.append(f'platform_quality_gate_files_total{{{labels}}} {len(files)}') metrics.append('# HELP platform_quality_gate_files_at_or_above_95_total Count of files at or above the 95 percent line target.') metrics.append('# TYPE platform_quality_gate_files_at_or_above_95_total gauge') -metrics.append(f'platform_quality_gate_files_at_or_above_95_total{{suite="lesavka"}} {files_at_95}') +metrics.append(f'platform_quality_gate_files_at_or_above_95_total{{{labels}}} {files_at_95}') metrics.append('# HELP platform_quality_gate_files_below_95_total Count of files below the 95 percent line target.') metrics.append('# TYPE platform_quality_gate_files_below_95_total gauge') -metrics.append(f'platform_quality_gate_files_below_95_total{{suite="lesavka"}} {files_below_95}') +metrics.append(f'platform_quality_gate_files_below_95_total{{{labels}}} {files_below_95}') metrics.append('# HELP platform_quality_gate_source_lines_over_500_total Count of tracked source files over 500 LOC.') metrics.append('# TYPE platform_quality_gate_source_lines_over_500_total gauge') -metrics.append(f'platform_quality_gate_source_lines_over_500_total{{suite="lesavka"}} {over_500}') +metrics.append(f'platform_quality_gate_source_lines_over_500_total{{{labels}}} {over_500}') metrics.append('# HELP platform_quality_gate_contract_files_total Count of files covered by the strict testing coverage contract.') metrics.append('# TYPE platform_quality_gate_contract_files_total gauge') -metrics.append(f'platform_quality_gate_contract_files_total{{suite="lesavka"}} {len(contract_files)}') +metrics.append(f'platform_quality_gate_contract_files_total{{{labels}}} {len(contract_files)}') metrics.append('# HELP platform_quality_gate_contract_files_at_target_total Count of strict contract files meeting the line coverage target.') metrics.append('# TYPE platform_quality_gate_contract_files_at_target_total gauge') -metrics.append(f'platform_quality_gate_contract_files_at_target_total{{suite="lesavka"}} {contract_files_at_target}') +metrics.append(f'platform_quality_gate_contract_files_at_target_total{{{labels}}} {contract_files_at_target}') metrics.append('# HELP platform_quality_gate_contract_files_below_target_total Count of strict contract files missing the line coverage target or LOC cap.') metrics.append('# TYPE platform_quality_gate_contract_files_below_target_total gauge') metrics.append( - f'platform_quality_gate_contract_files_below_target_total{{suite="lesavka"}} {len(contract_failures)}' + f'platform_quality_gate_contract_files_below_target_total{{{labels}}} {len(contract_failures)}' ) metrics.append('# HELP platform_quality_gate_file_line_coverage_percent Per-file line coverage percent.') metrics.append('# TYPE platform_quality_gate_file_line_coverage_percent gauge') metrics.append('# HELP platform_quality_gate_file_source_lines Per-file source line count.') metrics.append('# TYPE platform_quality_gate_file_source_lines gauge') -def esc(value: str) -> str: - return value.replace('\\', r'\\').replace('\n', r'\\n').replace('"', r'\"') - for item in files: label = esc(item['path']) metrics.append( - f'platform_quality_gate_file_line_coverage_percent{{suite="lesavka",file="{label}"}} {item["line_percent"]:.2f}' + f'platform_quality_gate_file_line_coverage_percent{{{labels},file="{label}"}} {item["line_percent"]:.2f}' ) metrics.append( - f'platform_quality_gate_file_source_lines{{suite="lesavka",file="{label}"}} {item["loc"]}' + f'platform_quality_gate_file_source_lines{{{labels},file="{label}"}} {item["loc"]}' ) metrics_path.write_text('\n'.join(metrics) + '\n', encoding='utf-8') diff --git a/scripts/ci/quality_gate_baseline.json b/scripts/ci/quality_gate_baseline.json index 888ec7d..455ec81 100644 --- a/scripts/ci/quality_gate_baseline.json +++ b/scripts/ci/quality_gate_baseline.json @@ -62,7 +62,7 @@ }, "client/src/launcher/ui.rs": { "line_percent": 100.0, - "loc": 2635 + "loc": 2650 }, "client/src/layout.rs": { "line_percent": 97.73, diff --git a/scripts/ci/sonarqube_gate.sh b/scripts/ci/sonarqube_gate.sh new file mode 100755 index 0000000..b9d780f --- /dev/null +++ b/scripts/ci/sonarqube_gate.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# Run or account for SonarQube analysis in the Atlas quality contract. +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +REPORT_DIR="${ROOT_DIR}/target/sonarqube-gate" +SUMMARY_JSON="${REPORT_DIR}/summary.json" +SUMMARY_TXT="${REPORT_DIR}/summary.txt" +METRICS_FILE="${REPORT_DIR}/metrics.prom" +PUSHGATEWAY_URL=${QUALITY_GATE_PUSHGATEWAY_URL:-} +ENFORCE=${LESAVKA_SONAR_ENFORCE:-0} + +mkdir -p "${REPORT_DIR}" +cd "${ROOT_DIR}" + +branch=${BRANCH_NAME:-${GIT_BRANCH:-}} +if [[ -z "${branch}" ]]; then + branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown) +fi +commit=${GIT_COMMIT:-} +if [[ -z "${commit}" ]]; then + commit=$(git rev-parse --short HEAD 2>/dev/null || echo unknown) +fi + +status=0 +sonar_status=not_applicable +reason='SONARQUBE_HOST_URL, SONARQUBE_TOKEN, or sonar-scanner is unavailable' +if [[ -n "${SONARQUBE_HOST_URL:-}" && -n "${SONARQUBE_TOKEN:-}" ]] && command -v sonar-scanner >/dev/null 2>&1; then + sonar_status=ok + reason='sonar-scanner completed' + if ! sonar-scanner \ + -Dsonar.projectKey=lesavka \ + -Dsonar.projectName=lesavka \ + -Dsonar.sources=client/src,server/src,common/src,testing/src \ + -Dsonar.tests=testing/tests \ + -Dsonar.host.url="${SONARQUBE_HOST_URL}" \ + -Dsonar.token="${SONARQUBE_TOKEN}" \ + >"${REPORT_DIR}/sonar-scanner.log" 2>&1; then + sonar_status=failed + reason='sonar-scanner failed; see sonar-scanner.log' + status=1 + fi +elif [[ "${ENFORCE}" == "1" ]]; then + sonar_status=failed + status=1 +fi + +python3 - "${SUMMARY_JSON}" "${SUMMARY_TXT}" "${METRICS_FILE}" "${sonar_status}" "${reason}" "${branch}" "${commit}" <<'PY' +import json +import pathlib +import sys +from datetime import datetime, timezone + +summary_path = pathlib.Path(sys.argv[1]) +text_path = pathlib.Path(sys.argv[2]) +metrics_path = pathlib.Path(sys.argv[3]) +sonar_status = sys.argv[4] +reason = sys.argv[5] +branch = sys.argv[6] +commit = sys.argv[7] + +def esc(value: str) -> str: + return value.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"') + +summary = { + 'suite': 'lesavka', + 'check': 'sonarqube', + 'status': sonar_status, + 'reason': reason, + 'branch': branch, + 'commit': commit, + 'generated_at': datetime.now(timezone.utc).isoformat(), +} +summary_path.write_text(json.dumps(summary, indent=2, sort_keys=True) + '\n', encoding='utf-8') +text_path.write_text( + f'sonarqube gate report\nstatus: {sonar_status}\nreason: {reason}\nbranch: {branch}\ncommit: {commit}\n', + encoding='utf-8', +) +labels = f'suite="lesavka",branch="{esc(branch)}",commit="{esc(commit)}"' +statuses = {'ok': 0, 'failed': 0, 'not_applicable': 0} +statuses[sonar_status] = 1 +metrics = [ + '# HELP platform_quality_gate_checks_total Check outcomes from the latest lesavka gate run.', + '# TYPE platform_quality_gate_checks_total gauge', +] +for state, value in statuses.items(): + metrics.append(f'platform_quality_gate_checks_total{{{labels},check="sonarqube",status="{state}"}} {value}') +metrics_path.write_text('\n'.join(metrics) + '\n', encoding='utf-8') +print(text_path.read_text(encoding='utf-8')) +PY + +if [[ -n "${PUSHGATEWAY_URL}" ]]; then + curl --fail --silent --show-error \ + --data-binary @"${METRICS_FILE}" \ + "${PUSHGATEWAY_URL%/}/metrics/job/lesavka-sonarqube-gate/suite/lesavka" || status=$? +fi + +exit "${status}" diff --git a/scripts/ci/supply_chain_gate.sh b/scripts/ci/supply_chain_gate.sh new file mode 100755 index 0000000..e807974 --- /dev/null +++ b/scripts/ci/supply_chain_gate.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# Generate dependency/artifact security evidence and run available scanners. +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +REPORT_DIR="${ROOT_DIR}/target/supply-chain-gate" +SUMMARY_JSON="${REPORT_DIR}/summary.json" +SUMMARY_TXT="${REPORT_DIR}/summary.txt" +METRICS_FILE="${REPORT_DIR}/metrics.prom" +SBOM_JSON="${REPORT_DIR}/sbom.cargo-metadata.json" +TREE_TXT="${REPORT_DIR}/dependency-tree.txt" +SECRET_TXT="${REPORT_DIR}/secret-scan.txt" +PUSHGATEWAY_URL=${QUALITY_GATE_PUSHGATEWAY_URL:-} +ENFORCE_TOOLS=${LESAVKA_SUPPLY_CHAIN_ENFORCE_TOOLS:-0} + +mkdir -p "${REPORT_DIR}" +cd "${ROOT_DIR}" + +branch=${BRANCH_NAME:-${GIT_BRANCH:-}} +if [[ -z "${branch}" ]]; then + branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown) +fi +commit=${GIT_COMMIT:-} +if [[ -z "${commit}" ]]; then + commit=$(git rev-parse --short HEAD 2>/dev/null || echo unknown) +fi + +status=0 +cargo metadata --locked --format-version 1 >"${SBOM_JSON}" +cargo tree --locked --workspace >"${TREE_TXT}" + +secret_status=ok +: >"${SECRET_TXT}" +while IFS= read -r path; do + case "$path" in + Cargo.lock|target/*|dist/*) continue ;; + esac + [[ -f "$path" ]] || continue + if file "$path" | grep -qi 'text\|json\|xml\|yaml\|toml\|script'; then + if grep -EHni \ + -e 'AKIA[0-9A-Z]{16}' \ + -e '-----BEGIN (RSA|EC|OPENSSH|PRIVATE) KEY-----' \ + -e "(password|secret|token|api[_-]?key)[[:space:]]*[:=][[:space:]]*[\"'][A-Za-z0-9_+/=.-]{12,}[\"']" \ + "$path" >>"${SECRET_TXT}" 2>/dev/null; then + secret_status=failed + status=1 + fi + fi +done < <(git ls-files) + +audit_status=not_applicable +if command -v cargo-audit >/dev/null 2>&1; then + audit_status=ok + if ! cargo audit --locked >"${REPORT_DIR}/cargo-audit.txt" 2>&1; then + audit_status=failed + status=1 + fi +elif [[ "${ENFORCE_TOOLS}" == "1" ]]; then + audit_status=failed + status=1 +fi + +deny_status=not_applicable +if command -v cargo-deny >/dev/null 2>&1; then + deny_status=ok + if ! cargo deny check >"${REPORT_DIR}/cargo-deny.txt" 2>&1; then + deny_status=failed + status=1 + fi +elif [[ "${ENFORCE_TOOLS}" == "1" ]]; then + deny_status=failed + status=1 +fi + +artifact_status=not_applicable +if compgen -G "dist/*.tar.gz" >/dev/null; then + artifact_status=ok + sha256sum dist/*.tar.gz >"${REPORT_DIR}/SHA256SUMS" +fi + +python3 - "${SUMMARY_JSON}" "${SUMMARY_TXT}" "${METRICS_FILE}" "${branch}" "${commit}" "${secret_status}" "${audit_status}" "${deny_status}" "${artifact_status}" <<'PY' +import json +import pathlib +import sys +from datetime import datetime, timezone + +summary_path = pathlib.Path(sys.argv[1]) +text_path = pathlib.Path(sys.argv[2]) +metrics_path = pathlib.Path(sys.argv[3]) +branch = sys.argv[4] +commit = sys.argv[5] +secret_status = sys.argv[6] +audit_status = sys.argv[7] +deny_status = sys.argv[8] +artifact_status = sys.argv[9] + +def esc(value: str) -> str: + return value.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"') + +checks = { + 'secret_scan': secret_status, + 'cargo_audit': audit_status, + 'cargo_deny': deny_status, + 'artifact_checksums': artifact_status, + 'sbom': 'ok', +} +status = 'failed' if any(value == 'failed' for value in checks.values()) else 'ok' +summary = { + 'suite': 'lesavka', + 'check': 'supply_chain', + 'status': status, + 'branch': branch, + 'commit': commit, + 'generated_at': datetime.now(timezone.utc).isoformat(), + 'checks': checks, +} +summary_path.write_text(json.dumps(summary, indent=2, sort_keys=True) + '\n', encoding='utf-8') +lines = ['supply chain gate report', f'status: {status}', f'branch: {branch}', f'commit: {commit}', ''] +for name, value in checks.items(): + lines.append(f'{name}: {value}') +text_path.write_text('\n'.join(lines) + '\n', encoding='utf-8') +labels = f'suite="lesavka",branch="{esc(branch)}",commit="{esc(commit)}"' +metrics = [ + '# HELP platform_quality_gate_checks_total Check outcomes from the latest lesavka gate run.', + '# TYPE platform_quality_gate_checks_total gauge', +] +for state in ('ok', 'failed', 'not_applicable'): + value = 1 if state == status else 0 + metrics.append(f'platform_quality_gate_checks_total{{{labels},check="supply_chain",status="{state}"}} {value}') +metrics.append('# HELP lesavka_supply_chain_subcheck_info Supply-chain subcheck evidence status.') +metrics.append('# TYPE lesavka_supply_chain_subcheck_info gauge') +for name, value in checks.items(): + metrics.append(f'lesavka_supply_chain_subcheck_info{{{labels},subcheck="{esc(name)}",status="{esc(value)}"}} 1') +metrics_path.write_text('\n'.join(metrics) + '\n', encoding='utf-8') +print(text_path.read_text(encoding='utf-8')) +PY + +if [[ -n "${PUSHGATEWAY_URL}" ]]; then + curl --fail --silent --show-error \ + --data-binary @"${METRICS_FILE}" \ + "${PUSHGATEWAY_URL%/}/metrics/job/lesavka-supply-chain-gate/suite/lesavka" || status=$? +fi + +exit "${status}" diff --git a/scripts/daemon/lesavka-core.sh b/scripts/daemon/lesavka-core.sh old mode 100644 new mode 100755 diff --git a/scripts/daemon/lesavka-uvc.sh b/scripts/daemon/lesavka-uvc.sh old mode 100644 new mode 100755 diff --git a/scripts/kernel/build-linux-rpi.sh b/scripts/kernel/build-linux-rpi.sh old mode 100644 new mode 100755 diff --git a/scripts/manual/audio-clip-fetch.sh b/scripts/manual/audio-clip-fetch.sh index f8ec154..5aaafe9 100755 --- a/scripts/manual/audio-clip-fetch.sh +++ b/scripts/manual/audio-clip-fetch.sh @@ -1,14 +1,18 @@ #!/usr/bin/env bash # scripts/manual/audio-clip-fetch.sh +# +# Manual: fetch and play recent server-side audio clips during field debugging. +# Not part of CI; requires SSH access to the target server. +set -euo pipefail -# Pull & play the most recent 1 s AAC clip from lesavka‑server PI_HOST="nikto@192.168.42.253" # adjust REMOTE_DIR="/tmp" -DEST="$(mktemp -u).wav" +TMPDIR="$(mktemp -d)" +trap 'rm -rf "$TMPDIR"' EXIT -scp "${PI_HOST}:${REMOTE_DIR}/ear-*.aac" "$DEST" 2>/dev/null \ +scp -q "${PI_HOST}:${REMOTE_DIR}/ear-*.aac" "$TMPDIR/" 2>/dev/null \ || { echo "❌ no clip files yet"; exit 1; } -LATEST=$(ls -1t ear-*.aac | head -n1) +LATEST=$(ls -1t "$TMPDIR"/ear-*.aac | head -n1) echo "🎧 playing ${LATEST} ..." gst-play-1.0 --quiet "${LATEST}" diff --git a/scripts/manual/audio-mic-fetch.sh b/scripts/manual/audio-mic-fetch.sh index a74214d..e9a0f03 100755 --- a/scripts/manual/audio-mic-fetch.sh +++ b/scripts/manual/audio-mic-fetch.sh @@ -1,9 +1,15 @@ #!/usr/bin/env bash # scripts/manual/audio-mic-fetch.sh +# +# Manual: fetch and play recent microphone uplink clips during field debugging. +# Not part of CI; requires SSH access to the target server. +set -euo pipefail PI_HOST="nikto@192.168.42.253" # adjust if needed REMOTE_DIR="/tmp" TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + scp -q "${PI_HOST}:${REMOTE_DIR}/voice-*.aac" "$TMPDIR/" 2>/dev/null \ || { echo "❌ no mic clip files found yet"; exit 1; } diff --git a/scripts/manual/compare-eye-decoders.sh b/scripts/manual/compare-eye-decoders.sh index 299ed5b..45efe6f 100755 --- a/scripts/manual/compare-eye-decoders.sh +++ b/scripts/manual/compare-eye-decoders.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Manual: compare H.264 decoder output from a live eye device. +# Not part of CI; requires a live /dev/lesavka_* eye device. set -euo pipefail DEVICE="${1:-/dev/lesavka_r_eye}" diff --git a/scripts/manual/eval_lesavka.sh b/scripts/manual/eval_lesavka.sh index 60bddba..2c9e6a0 100755 --- a/scripts/manual/eval_lesavka.sh +++ b/scripts/manual/eval_lesavka.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash # scripts/manual/eval_lesavka.sh - iterative health check for lesavka client/server/gadget +# Manual: operator probe for live Lesavka hosts; not part of CI. # - Locally: probes TCP + gRPC handshake on LESAVKA_SERVER_ADDR # - Optional: if TETHYS_HOST is set, ssh to run lsusb + dmesg tail (enumeration check) # - Optional: if THEIA_HOST is set, ssh to show core/server status + hidg/uvc presence diff --git a/scripts/manual/kde-start-tethys.sh b/scripts/manual/kde-start-tethys.sh index d73999a..a14fb49 100755 --- a/scripts/manual/kde-start-tethys.sh +++ b/scripts/manual/kde-start-tethys.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash # scripts/manual/kde-start-tethys.sh # +# Manual: remote desktop recovery helper for tethys; not part of CI. +# # Start/restart SDDM on tethys and set display geometry over :0. # Intended for remote use after SSH-ing into tethys. # diff --git a/scripts/manual/probe-eye-capabilities.sh b/scripts/manual/probe-eye-capabilities.sh index 1662600..514da03 100755 --- a/scripts/manual/probe-eye-capabilities.sh +++ b/scripts/manual/probe-eye-capabilities.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Manual: capture V4L2/sysfs capability evidence from a live eye device. +# Not part of CI; requires local device access. set -euo pipefail DEVICE="${1:-/dev/lesavka_r_eye}" diff --git a/scripts/manual/soak-report.sh b/scripts/manual/soak-report.sh index f2310ca..9a096bd 100755 --- a/scripts/manual/soak-report.sh +++ b/scripts/manual/soak-report.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash # scripts/manual/soak-report.sh - summarize server stability/quality counters for a time window +# Manual: operator soak evidence collector; not part of CI. set -euo pipefail SERVER_HOST=${LESAVKA_SERVER_HOST:-theia} diff --git a/scripts/manual/usb-reset.sh b/scripts/manual/usb-reset.sh index 4eb0c83..d436a2c 100755 --- a/scripts/manual/usb-reset.sh +++ b/scripts/manual/usb-reset.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash # scripts/manual/usb-reset.sh - trigger USB reset RPC on the server +# Manual: operator recovery action; not part of CI. set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" diff --git a/scripts/manual/video-frame-fetch.sh b/scripts/manual/video-frame-fetch.sh index c3e3c8b..fd2c30d 100755 --- a/scripts/manual/video-frame-fetch.sh +++ b/scripts/manual/video-frame-fetch.sh @@ -1,11 +1,14 @@ #!/usr/bin/env bash -# scripts/manual/video-stream.sh +# scripts/manual/video-frame-fetch.sh +# +# Manual: fetch recent H.264 eye samples and render first frames for inspection. +# Not part of CI; requires SSH access to a live server. +set -euo pipefail PI_HOST="nikto@192.168.42.253" # user@IP-of lesavka REMOTE_DIR="/tmp" # where eye*-idr.h264 are written FIRST_FEW=10 -set -eu WORKDIR="$(mktemp -d)" echo "⏬ pulling h264 samples from $PI_HOST ..." scp "${PI_HOST}:${REMOTE_DIR}/eye*.h264" "$WORKDIR/" diff --git a/scripts/manual/video-stream.sh b/scripts/manual/video-stream.sh index 0d5d1c3..47a8bf0 100755 --- a/scripts/manual/video-stream.sh +++ b/scripts/manual/video-stream.sh @@ -1,11 +1,15 @@ #!/usr/bin/env bash # scripts/manual/video-stream.sh +# +# Manual: stream live server video into a local GStreamer preview. +# Not part of CI; requires grpcurl, jq, and GStreamer. +set -euo pipefail grpcurl -plaintext \ -d '{"id":0,"max_bitrate":6000}' \ -import-path ./../../common/proto -proto lesavka.proto \ 192.168.42.253:50051 \ - lesavka.relay.Relay/CaptureVideo \ -| jq -r '.data' -| base64 -d \ -| gst-launch-1.0 fdsrc ! h264parse ! avdec_h264 ! autovideosink + lesavka.relay.Relay/CaptureVideo | + jq -r '.data' | + base64 -d | + gst-launch-1.0 fdsrc ! h264parse ! avdec_h264 ! autovideosink diff --git a/scripts/manual/vpn-open.sh b/scripts/manual/vpn-open.sh index c029002..57ec826 100755 --- a/scripts/manual/vpn-open.sh +++ b/scripts/manual/vpn-open.sh @@ -1,7 +1,11 @@ #!/usr/bin/env bash # scripts/manual/vpn-open.sh +# +# Manual: open the local CyberGhost VPN profile for field networking tests. +# Not part of CI; requires local sudo privileges. +set -euo pipefail here=$(pwd) cd /home/brad/cyberghost sudo openvpn --config openvpn.ovpn -cd $here +cd "$here" diff --git a/scripts/manual/vpn-test.sh b/scripts/manual/vpn-test.sh index 7469ef8..f569cbd 100755 --- a/scripts/manual/vpn-test.sh +++ b/scripts/manual/vpn-test.sh @@ -1,4 +1,10 @@ #!/usr/bin/env bash # scripts/manual/vpn-test.sh +# +# Manual: show current public IP/geolocation after VPN changes. +# Not part of CI; uses public HTTP APIs. +set -euo pipefail -set -x IP $(curl -s https://api.ipify.org) && echo "IP: $IP" && curl http://ip-api.com/json/$IP?fields=country,city,lat,lon +IP="$(curl -fsS https://api.ipify.org)" +echo "IP: $IP" +curl -fsS "http://ip-api.com/json/${IP}?fields=country,city,lat,lon" diff --git a/server/Cargo.toml b/server/Cargo.toml index 13daba3..606a5aa 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,7 @@ bench = false [package] name = "lesavka_server" -version = "0.12.1" +version = "0.12.2" edition = "2024" autobins = false diff --git a/testing/tests/client_launcher_layout_contract.rs b/testing/tests/client_launcher_layout_contract.rs index 376ad06..b8285ce 100644 --- a/testing/tests/client_launcher_layout_contract.rs +++ b/testing/tests/client_launcher_layout_contract.rs @@ -102,6 +102,21 @@ fn operations_column_fills_height_and_splits_extra_space_between_logs() { ); } +#[test] +fn session_console_buttons_share_the_remaining_toolbar_width() { + assert!( + UI_SRC.contains("let console_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 8);") + ); + assert!(UI_SRC.contains("console_buttons.set_hexpand(true);")); + assert!(UI_SRC.contains("console_buttons.set_homogeneous(true);")); + assert!(UI_SRC.contains("console_copy_button.set_hexpand(true);")); + assert!(UI_SRC.contains("console_popout_button.set_hexpand(true);")); + assert!( + source_index("console_toolbar.append(&console_level_combo);") + < source_index("console_toolbar.append(&console_buttons);") + ); +} + #[test] fn relay_controls_keep_connect_inline_with_server_entry() { assert!(UI_SRC.contains("build_panel(\"Relay Controls\")")); diff --git a/testing/tests/client_launcher_runtime_contract.rs b/testing/tests/client_launcher_runtime_contract.rs index bfac7e8..9868263 100644 --- a/testing/tests/client_launcher_runtime_contract.rs +++ b/testing/tests/client_launcher_runtime_contract.rs @@ -111,6 +111,10 @@ fn active_relay_keeps_local_upstream_camera_and_microphone_evidence_visible() { fn launcher_webcam_quality_selection_reaches_preview_and_relay_env() { assert!(UI_SRC.contains("selected_camera_quality(&camera_quality_combo")); assert!(UI_SRC.contains("sync_camera_quality_selection")); + assert!(UI_SRC.contains("let camera_quality_syncing = Rc::new(Cell::new(false));")); + assert!(UI_SRC.contains("camera_quality_syncing.set(true);")); + assert!(UI_SRC.contains("if camera_quality_syncing.get()")); + assert!(UI_SRC.contains("state.try_borrow_mut()")); assert!(UI_SRC.contains("tests.set_camera_quality")); assert!(DEVICE_TEST_SRC.contains("pub fn set_camera_quality")); assert!(DEVICE_TEST_SRC.contains("build_camera_preview_pipeline(&device, mode)"));