From cef11af4ac81fb94df9515fade675211600097e1 Mon Sep 17 00:00:00 2001 From: Yann Prono Date: Wed, 19 Nov 2025 20:28:39 +0100 Subject: [PATCH 1/5] fix: integration tests with testcontainers --- .github/workflows/build.yml | 36 +- Cargo.lock | 792 ++++++++++++++++++++--- crates/bin/Cargo.toml | 2 + crates/bin/src/main.rs | 4 +- crates/bin/tests/integration_tests.rs | 49 ++ crates/bin/tests/yozefu_testcontainer.rs | 151 +++++ crates/tui/src/component/ui.rs | 2 +- 7 files changed, 934 insertions(+), 102 deletions(-) create mode 100644 crates/bin/tests/integration_tests.rs create mode 100644 crates/bin/tests/yozefu_testcontainer.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 746e858d..60e76660 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,25 +22,10 @@ concurrency: jobs: check: - name: Check ${{ matrix.package.name }} + name: Check runs-on: ubuntu-latest permissions: contents: read - strategy: - matrix: - package: - - name: yozefu-lib - command: cargo check --locked --package yozefu-lib - - name: yozefu-app - command: cargo check --locked --package yozefu-app - - name: yozefu-command - command: cargo check --locked --package yozefu-command - - name: yozefu-tui - command: cargo check --locked --package yozefu-tui - - name: yozefu-wasm-types - command: cargo check --locked --package yozefu-wasm-types - - name: yozefu - command: cargo run --locked -- --version steps: - uses: actions/checkout@v5 with: @@ -52,7 +37,24 @@ jobs: with: cache-all-crates: true - name: Run cargo check - run: ${{ matrix.package.command }} + run: cargo check --locked + + hack: + name: Cargo hack + needs: check + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@cargo-hack + - run: cargo hack check --feature-powerset --no-dev-deps build: name: Build w/o features diff --git a/Cargo.lock b/Cargo.lock index 5ded2d63..ebefcd48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,6 +142,43 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +[[package]] +name = "assert_cmd" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcbb6924530aa9e0432442af08bbcafdad182db80d2e560da42a6d442535bf85" +dependencies = [ + "anstyle", + "bstr", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[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" @@ -249,7 +286,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -261,6 +298,12 @@ dependencies = [ "syn", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.10.0" @@ -288,6 +331,82 @@ dependencies = [ "objc2", ] +[[package]] +name = "bollard" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899ca34eb6924d6ec2a77c6f7f5c7339e60fd68235eaf91edd5a15f12958bb06" +dependencies = [ + "async-stream", + "base64 0.22.1", + "bitflags 2.10.0", + "bollard-buildkit-proto", + "bollard-stubs", + "bytes", + "chrono", + "futures-core", + "futures-util", + "hex", + "home", + "http", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-rustls", + "hyper-util", + "hyperlocal", + "log", + "num", + "pin-project-lite", + "rand 0.9.2", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tokio-util", + "tonic 0.13.1", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-buildkit-proto" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b3e79f8bd0f25f32660e3402afca46fd91bebaf135af017326d905651f8107" +dependencies = [ + "prost 0.13.5", + "prost-types 0.13.5", + "tonic 0.13.1", + "ureq 2.12.1", +] + +[[package]] +name = "bollard-stubs" +version = "1.48.3-rc.28.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ea257e555d16a2c01e5593f40b73865cdf12efbceda33c6d14a2d8d1490368" +dependencies = [ + "base64 0.22.1", + "bollard-buildkit-proto", + "bytes", + "chrono", + "prost 0.13.5", + "serde", + "serde_json", + "serde_repr", + "serde_with", +] + [[package]] name = "bon" version = "3.8.1" @@ -320,6 +439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", + "regex-automata", "serde", ] @@ -362,7 +482,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb9f6e1368bd4621d2c86baa7e37de77a938adf5221e5dd3d6133340101b309e" dependencies = [ - "bitflags", + "bitflags 2.10.0", "polling", "rustix 1.1.2", "slab", @@ -469,7 +589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "befbfd072a8e81c02f8c507aefce431fe5e7d051f83d48a23ffc9b9fe5a11799" dependencies = [ "heck", - "indexmap", + "indexmap 2.12.1", "log", "proc-macro2", "quote", @@ -667,9 +787,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8599749b6667e2f0c910c1d0dff6901163ff698a52d5a39720f61b5be4b20d3" dependencies = [ "futures-core", - "prost", - "prost-types", - "tonic", + "prost 0.14.1", + "prost-types 0.14.1", + "tonic 0.14.2", "tonic-prost", "tracing-core", ] @@ -687,14 +807,14 @@ dependencies = [ "hdrhistogram", "humantime", "hyper-util", - "prost", - "prost-types", + "prost 0.14.1", + "prost-types 0.14.1", "serde", "serde_json", "thread_local", "tokio", "tokio-stream", - "tonic", + "tonic 0.14.2", "tracing", "tracing-core", "tracing-subscriber", @@ -754,6 +874,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -953,7 +1083,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags", + "bitflags 2.10.0", "crossterm_winapi", "mio", "parking_lot", @@ -969,7 +1099,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags", + "bitflags 2.10.0", "crossterm_winapi", "derive_more", "document-features", @@ -1114,6 +1244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", + "serde_core", ] [[package]] @@ -1137,6 +1268,12 @@ dependencies = [ "syn", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -1229,6 +1366,17 @@ dependencies = [ "libloading", ] +[[package]] +name = "docker_credential" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d89dfcba45b4afad7450a99b39e751590463e45c04728cf555d36bb66940de8" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "document-features" version = "0.2.12" @@ -1327,6 +1475,17 @@ version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +[[package]] +name = "etcetera" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c7b13d0780cb82722fd59f6f57f925e143427e4a75313a6c77243bf5326ae6" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.59.0", +] + [[package]] name = "extism" version = "1.12.0" @@ -1345,7 +1504,7 @@ dependencies = [ "toml 0.9.8", "tracing", "tracing-subscriber", - "ureq", + "ureq 3.1.4", "url", "uuid", "wasi-common", @@ -1363,7 +1522,7 @@ dependencies = [ "base64 0.22.1", "bytemuck", "extism-convert-macros", - "prost", + "prost 0.14.1", "rmp-serde", "serde", "serde_json", @@ -1416,6 +1575,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "filetime" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.60.2", +] + [[package]] name = "find-msvc-tools" version = "0.1.5" @@ -1611,7 +1782,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" dependencies = [ - "bitflags", + "bitflags 2.10.0", "debugid", "fxhash", "serde", @@ -1668,7 +1839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 2.12.1", "stable_deref_trait", ] @@ -1703,13 +1874,19 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1753,6 +1930,21 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "http" version = "1.3.1" @@ -1828,6 +2020,21 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + [[package]] name = "hyper-rustls" version = "0.27.7" @@ -1891,7 +2098,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.1", "system-configuration", "tokio", "tower-service", @@ -1899,6 +2106,21 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "hyperlocal" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" +dependencies = [ + "hex", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -2037,6 +2259,17 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.12.1" @@ -2308,8 +2541,9 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags", + "bitflags 2.10.0", "libc", + "redox_syscall 0.5.18", ] [[package]] @@ -2511,7 +2745,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -2544,6 +2778,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2555,6 +2803,15 @@ dependencies = [ "serde", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2570,6 +2827,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2623,7 +2902,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags", + "bitflags 2.10.0", "block2", "libc", "objc2", @@ -2639,7 +2918,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags", + "bitflags 2.10.0", "block2", "objc2", "objc2-foundation", @@ -2669,7 +2948,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags", + "bitflags 2.10.0", "block2", "libc", "objc2", @@ -2681,7 +2960,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags", + "bitflags 2.10.0", "block2", "objc2", "objc2-foundation", @@ -2693,7 +2972,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags", + "bitflags 2.10.0", "block2", "objc2", "objc2-foundation", @@ -2717,7 +2996,7 @@ checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.12.1", "memchr", ] @@ -2739,7 +3018,7 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags", + "bitflags 2.10.0", "libc", "once_cell", "onig_sys", @@ -2772,7 +3051,7 @@ version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -2854,11 +3133,36 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link", ] +[[package]] +name = "parse-display" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +dependencies = [ + "parse-display-derive", + "regex", + "regex-syntax", +] + +[[package]] +name = "parse-display-derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "regex-syntax", + "structmeta", + "syn", +] + [[package]] name = "paste" version = "1.0.15" @@ -2940,7 +3244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap", + "indexmap 2.12.1", "quick-xml 0.38.4", "serde", "time", @@ -3002,6 +3306,33 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "difflib", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -3041,6 +3372,16 @@ 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 0.13.5", +] + [[package]] name = "prost" version = "0.14.1" @@ -3048,7 +3389,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.14.1", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -3064,13 +3418,22 @@ dependencies = [ "syn", ] +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost 0.13.5", +] + [[package]] name = "prost-types" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" dependencies = [ - "prost", + "prost 0.14.1", ] [[package]] @@ -3240,7 +3603,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cassowary", "compact_str", "crossterm 0.28.1", @@ -3312,13 +3675,22 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +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", + "bitflags 2.10.0", ] [[package]] @@ -3515,7 +3887,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3528,7 +3900,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", @@ -3560,6 +3932,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.5.1", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.13.0" @@ -3623,6 +4016,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars" version = "1.1.0" @@ -3631,7 +4036,7 @@ checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "chrono", "dyn-clone", - "indexmap", + "indexmap 2.12.1", "ref-cast", "schemars_derive", "serde", @@ -3669,8 +4074,21 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", - "core-foundation", + "bitflags 2.10.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3753,7 +4171,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap", + "indexmap 2.12.1", "itoa", "memchr", "ryu", @@ -3761,6 +4179,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -3791,6 +4220,37 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.12.1", + "schemars 0.9.0", + "schemars 1.1.0", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha2" version = "0.10.9" @@ -3871,9 +4331,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -3917,7 +4377,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags", + "bitflags 2.10.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -3949,6 +4409,16 @@ dependencies = [ "wayland-backend", ] +[[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.1" @@ -3983,6 +4453,29 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "structmeta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn", +] + +[[package]] +name = "structmeta-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "strum" version = "0.26.3" @@ -4034,9 +4527,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.110" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -4090,8 +4583,8 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags", - "core-foundation", + "bitflags 2.10.0", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4111,7 +4604,7 @@ version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4592f674ce18521c2a81483873a49596655b179f71c5e05d10c1fe66c78745" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cap-fs-ext", "cap-std", "fd-lock", @@ -4149,6 +4642,42 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "testcontainers" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b92bce247dc9260a19808321e11b51ea6a0293d02b48ab1c6578960610cfa2a7" +dependencies = [ + "async-trait", + "bollard", + "bollard-stubs", + "bytes", + "docker_credential", + "either", + "etcetera", + "futures", + "log", + "memchr", + "parse-display", + "pin-project-lite", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tokio-tar", + "tokio-util", + "ulid", + "url", +] + [[package]] name = "testing_logger" version = "0.1.1" @@ -4285,7 +4814,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.1", "tokio-macros", "tracing", "windows-sys 0.61.2", @@ -4333,6 +4862,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tar" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75" +dependencies = [ + "filetime", + "futures-core", + "libc", + "redox_syscall 0.3.5", + "tokio", + "tokio-stream", + "xattr", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -4364,7 +4908,7 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "indexmap", + "indexmap 2.12.1", "serde_core", "serde_spanned 1.0.3", "toml_datetime 0.7.3", @@ -4397,7 +4941,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.12.1", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -4411,7 +4955,7 @@ version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap", + "indexmap 2.12.1", "toml_datetime 0.7.3", "toml_parser", "winnow", @@ -4438,6 +4982,35 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +[[package]] +name = "tonic" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +dependencies = [ + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost 0.13.5", + "socket2 0.5.10", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tonic" version = "0.14.2" @@ -4457,7 +5030,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2", + "socket2 0.6.1", "sync_wrapper", "tokio", "tokio-stream", @@ -4474,8 +5047,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ "bytes", - "prost", - "tonic", + "prost 0.14.1", + "tonic 0.14.2", ] [[package]] @@ -4486,7 +5059,7 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "indexmap", + "indexmap 2.12.1", "pin-project-lite", "slab", "sync_wrapper", @@ -4503,7 +5076,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags", + "bitflags 2.10.0", "bytes", "futures-util", "http", @@ -4622,6 +5195,16 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "ulid" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" +dependencies = [ + "rand 0.9.2", + "web-time", +] + [[package]] name = "unicode-ident" version = "1.0.22" @@ -4675,6 +5258,21 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots 0.26.11", +] + [[package]] name = "ureq" version = "3.1.4" @@ -4689,14 +5287,14 @@ dependencies = [ "rustls-pki-types", "ureq-proto", "utf-8", - "webpki-roots", + "webpki-roots 1.0.4", ] [[package]] name = "ureq-proto" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" +checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" dependencies = [ "base64 0.22.1", "http", @@ -4764,6 +5362,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -4796,7 +5403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f6fd843e80e63252198c08122b0a5889e384f5b0a0c172f3436017bc27c1915" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.10.0", "cap-fs-ext", "cap-rand", "cap-std", @@ -4907,9 +5514,9 @@ version = "0.224.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04f17a5917c2ddd3819e84c661fae0d6ba29d7b9c1f0e96c708c65a9c4188e11" dependencies = [ - "bitflags", + "bitflags 2.10.0", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.12.1", "semver", "serde", ] @@ -4920,8 +5527,8 @@ version = "0.241.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46d90019b1afd4b808c263e428de644f3003691f243387d30d673211ee0cb8e8" dependencies = [ - "bitflags", - "indexmap", + "bitflags 2.10.0", + "indexmap 2.12.1", "semver", ] @@ -4945,7 +5552,7 @@ dependencies = [ "addr2line", "anyhow", "async-trait", - "bitflags", + "bitflags 2.10.0", "bumpalo", "cc", "cfg-if", @@ -4953,7 +5560,7 @@ dependencies = [ "fxprof-processed-profile", "gimli", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.12.1", "ittapi", "libc", "log", @@ -5081,7 +5688,7 @@ dependencies = [ "cranelift-bitset", "cranelift-entity", "gimli", - "indexmap", + "indexmap 2.12.1", "log", "object 0.36.7", "postcard", @@ -5187,7 +5794,7 @@ checksum = "9a5531455e2c55994a1540355140369bb7ec0e46d2699731c5ee9f4cf9c3f7d4" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.12.1", "wit-parser", ] @@ -5242,7 +5849,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags", + "bitflags 2.10.0", "rustix 1.1.2", "wayland-backend", "wayland-scanner", @@ -5254,7 +5861,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cursor-icon", "wayland-backend", ] @@ -5276,7 +5883,7 @@ version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5288,7 +5895,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5301,7 +5908,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dfe33d551eb8bffd03ff067a8b44bb963919157841a99957151299a6307d19c" dependencies = [ - "bitflags", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5314,7 +5921,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5364,6 +5971,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.4", +] + [[package]] name = "webpki-roots" version = "1.0.4" @@ -5381,7 +5997,7 @@ checksum = "c5a4ea7722c042a659dc70caab0b56d7f45220e8bae1241cf5ebc7ab7efb0dfb" dependencies = [ "anyhow", "async-trait", - "bitflags", + "bitflags 2.10.0", "thiserror 1.0.69", "tracing", "wasmtime", @@ -5715,7 +6331,7 @@ version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" dependencies = [ - "bitflags", + "bitflags 2.10.0", "windows-sys 0.59.0", ] @@ -5733,7 +6349,7 @@ checksum = "e3477d8d0acb530d76beaa8becbdb1e3face08929db275f39934963eb4f716f8" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.12.1", "log", "semver", "serde", @@ -5788,6 +6404,16 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.2", +] + [[package]] name = "xcursor" version = "0.3.10" @@ -5836,7 +6462,9 @@ dependencies = [ name = "yozefu" version = "0.0.20" dependencies = [ + "assert_cmd", "console-subscriber", + "testcontainers", "tokio", "yozefu-command", ] @@ -5848,11 +6476,11 @@ dependencies = [ "chrono", "directories", "extism", - "indexmap", + "indexmap 2.12.1", "itertools 0.14.0", "rdkafka", "resolve-path", - "schemars", + "schemars 1.1.0", "serde", "serde_json", "tempfile", @@ -5874,7 +6502,7 @@ dependencies = [ "extism", "futures", "futures-batch", - "indexmap", + "indexmap 2.12.1", "indicatif", "itertools 0.14.0", "rdkafka", @@ -5906,7 +6534,7 @@ dependencies = [ "protobuf", "rdkafka", "reqwest", - "schemars", + "schemars 1.1.0", "serde", "serde_json", "strum 0.27.2", @@ -5925,7 +6553,7 @@ dependencies = [ "crossterm 0.29.0", "futures", "futures-batch", - "indexmap", + "indexmap 2.12.1", "insta", "itertools 0.14.0", "nom 8.0.0", diff --git a/crates/bin/Cargo.toml b/crates/bin/Cargo.toml index 5df9e41a..deb347dc 100644 --- a/crates/bin/Cargo.toml +++ b/crates/bin/Cargo.toml @@ -25,6 +25,8 @@ path = "src/main.rs" tokio = { version = "1", features = ["full", "tracing"] } command = { workspace = true } console-subscriber = "0.5.0" +assert_cmd = "2.1.1" +testcontainers = { version = "0.25.0", features = ["blocking"] } [features] default = ["ssl-vendored", "gssapi-vendored", "libz-static"] diff --git a/crates/bin/src/main.rs b/crates/bin/src/main.rs index b256c6fb..79b2281b 100644 --- a/crates/bin/src/main.rs +++ b/crates/bin/src/main.rs @@ -5,8 +5,8 @@ use command::{Cli, Parser, TuiError}; #[tokio::main] async fn main() -> Result<(), TuiError> { // Needed to use `tokio-console` in debug mode - // #[cfg(debug_assertions)] - // console_subscriber::init(); + #[cfg(debug_assertions)] + console_subscriber::init(); let parsed = Cli::::parse(); parsed.execute().await } diff --git a/crates/bin/tests/integration_tests.rs b/crates/bin/tests/integration_tests.rs new file mode 100644 index 00000000..1d7efcb4 --- /dev/null +++ b/crates/bin/tests/integration_tests.rs @@ -0,0 +1,49 @@ +/* + +use crate::yozefu_testcontainer::YozefuTestContainer; +use std::{path::PathBuf, sync::LazyLock}; +use testcontainers::ImageExt; +pub mod yozefu_testcontainer; + +static CONTAINER: LazyLock = LazyLock::new(YozefuTestContainer::default); + +#[test] +fn success() -> Result<(), Box> { + let container = CONTAINER.run(|image| image.with_cmd(["config"]))?; + assert!(container.exit_code() == 0); + Ok(()) +} + +#[test] +fn read_only_root_fs() -> Result<(), Box> { + let container = CONTAINER.run(|image| { + image + .with_cmd(["config"]) + .with_readonly_rootfs(true) + .with_user("ubuntu") + })?; + + assert!(container.exit_code() != 0); + assert!(container.stderr().contains("Read-only file system")); + Ok(()) +} + +#[test] +fn log_file_defined_with_env_variable() -> Result<(), Box> { + let log_file = PathBuf::from("/tmp/yozefu.log"); + let container = CONTAINER.run(|image| { + image + .with_cmd(["config"]) + .with_env_var("YOZEFU_LOG_FILE", log_file.display().to_string()) + })?; + + assert_eq!( + 0, + container.exit_code(), + "Container stderr: {}", + container.stderr() + ); + assert!(container.file_exist(log_file)); + Ok(()) +} + */ diff --git a/crates/bin/tests/yozefu_testcontainer.rs b/crates/bin/tests/yozefu_testcontainer.rs new file mode 100644 index 00000000..2f0196c6 --- /dev/null +++ b/crates/bin/tests/yozefu_testcontainer.rs @@ -0,0 +1,151 @@ +use assert_cmd::cargo; +use std::hash::Hasher; +use std::{fs, hash::DefaultHasher, path::PathBuf}; +use testcontainers::ContainerRequest; +use testcontainers::core::ExecCommand; +use testcontainers::{ + GenericBuildableImage, GenericImage, ImageExt, + core::{Mount, wait::ExitWaitStrategy}, + runners::{SyncBuilder, SyncRunner}, +}; + +const DOCKERFILE: &str = r#" +FROM rust:1-slim-trixie AS builder +WORKDIR /app +ENV RUST_BACKTRACE="full" +RUN apt-get update && apt-get install --no-install-recommends -y git build-essential cmake libclang-dev +RUN --mount=type=bind,source=crates,target=crates \ + --mount=type=bind,source=.cargo/,target=.cargo/ \ + --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ + --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ + < Self { + let binary_path = Self::build().expect("Cannot build Yozefu binary"); + Self { binary_path } + } +} + +impl YozefuTestContainer { + fn image(&self) -> ContainerRequest { + GenericImage::new("ubuntu", "latest") + .with_entrypoint("/tmp/app") + .with_wait_for(testcontainers::core::WaitFor::Exit( + ExitWaitStrategy::default(), + )) + .with_env_var("RUST_BACKTRACE", "full") + .with_mount(Mount::bind_mount( + self.binary_path.to_str().unwrap(), + "/tmp/app", + )) + } + + fn build() -> Result> { + let cmd = cargo::cargo_bin_cmd!("yozf"); + let mut hasher = DefaultHasher::new(); + hasher.write(&fs::read(cmd.get_program())?); + let hash: u64 = hasher.finish(); + + let root_repo = env!("CARGO_MANIFEST_DIR"); + let root_repo = PathBuf::from(root_repo) + .parent() + .unwrap() + .parent() + .unwrap() + .to_path_buf(); + + let output_dir = root_repo + .join("target") + .join("debug") + .join("integration-test"); + let linux_binary = output_dir.join(format!("yozf-{}", hash)); + + if fs::metadata(&linux_binary).is_ok() { + return Ok(linux_binary); + } + + let _ = fs::remove_dir_all(&output_dir); + fs::create_dir_all(&output_dir)?; + + println!("Building Yozefu with docker..."); + let docker_image = GenericBuildableImage::new("yozefu-integration-tests", "latest") + .with_dockerfile_string(DOCKERFILE) + .with_file(root_repo.join("crates"), "./crates") + .with_file(root_repo.join(".cargo"), "./.cargo") + .with_file(root_repo.join("Cargo.toml"), "./Cargo.toml") + .with_file(root_repo.join("Cargo.lock"), "./Cargo.lock") + .build_image() + .expect("Cannot build the docker image"); + + let container = docker_image + .with_entrypoint("sleep") + .with_wait_for(testcontainers::core::WaitFor::Exit( + ExitWaitStrategy::default(), + )) + .with_cmd(["3"]) + .start()?; + + let _ = std::process::Command::new("docker") + .args([ + "cp", + &format!("{}:/app/target/debug/yozf", container.id()), + linux_binary.to_str().unwrap(), + ]) + .status()?; + + Ok(linux_binary) + } + + pub fn run( + &self, + fnn: impl FnOnce(ContainerRequest) -> ContainerRequest, + ) -> Result> { + let container = self.image(); + let container = fnn(container); + let running = container.start()?; + + Ok(Container::new(running)) + } +} + +pub struct Container { + container: testcontainers::Container, +} + +impl Container { + pub fn new(container: testcontainers::Container) -> Self { + Self { container } + } + pub fn stdout(&self) -> String { + String::from_utf8_lossy(&self.container.stdout_to_vec().unwrap()).to_string() + } + pub fn stderr(&self) -> String { + String::from_utf8_lossy(&self.container.stderr_to_vec().unwrap()).to_string() + } + + pub fn exit_code(&self) -> i64 { + self.container.exit_code().unwrap().unwrap() + } + + pub fn file_exist(&self, path: PathBuf) -> bool { + self.container + .exec(ExecCommand::new(vec![ + "test", + "-f", + &path.display().to_string(), + ])) + .unwrap() + .exit_code() + .unwrap() + == Some(0) + } +} diff --git a/crates/tui/src/component/ui.rs b/crates/tui/src/component/ui.rs index a4797595..eb53f638 100644 --- a/crates/tui/src/component/ui.rs +++ b/crates/tui/src/component/ui.rs @@ -300,7 +300,7 @@ impl Ui { let records_channel = mpsc::unbounded_channel::(); self.records_sender = Some(records_channel.0); self.load_topics(action_tx.clone()); - let mut tui = tui::Tui::new()?; + let mut tui: tui::Tui = tui::Tui::new()?; tui.enter()?; self.root.register_action_handler(action_tx.clone()); self.root.init()?; From 0d5bf398d9bf0c6ecbbbb1b056b0a5cf3f02458f Mon Sep 17 00:00:00 2001 From: Yann Prono Date: Sun, 23 Nov 2025 13:02:39 +0100 Subject: [PATCH 2/5] build: some default features are not compatible with windows. I removed from the default enabled features #177 --- .github/workflows/build.yml | 10 +++++----- .github/workflows/publish.yml | 8 ++++---- crates/bin/Cargo.toml | 2 +- deny.toml | 3 ++- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 60e76660..985c97e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -91,22 +91,22 @@ jobs: platforms: - os: macos-15 target: aarch64-apple-darwin - features: "" + features: "--features gssapi-vendored,libz-static,ssl-vendored" - os: macos-15-intel target: x86_64-apple-darwin - features: "" + features: "--features gssapi-vendored,libz-static,ssl-vendored" - os: ubuntu-latest target: x86_64-unknown-linux-gnu - features: "" + features: "--features gssapi-vendored,libz-static,ssl-vendored" - os: ubuntu-24.04-arm target: aarch64-unknown-linux-gnu - features: "" + features: "--features gssapi-vendored,libz-static,ssl-vendored" - os: windows-latest target: x86_64-pc-windows-gnu features: "--no-default-features" - os: windows-latest target: x86_64-pc-windows-msvc - features: "--no-default-features --features ssl-vendored" + features: "--no-default-features --features ssl-vendored,libz-static" steps: - uses: actions/checkout@v5 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d00d7987..7fb1c36b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -70,16 +70,16 @@ jobs: platforms: - os: macos-15 target: aarch64-apple-darwin - features: "" + features: "--features gssapi-vendored,libz-static,ssl-vendored" - os: macos-15-intel target: x86_64-apple-darwin - features: "" + features: "--features gssapi-vendored,libz-static,ssl-vendored" - os: ubuntu-latest target: x86_64-unknown-linux-gnu - features: "" + features: "--features gssapi-vendored,libz-static,ssl-vendored" - os: ubuntu-24.04-arm target: aarch64-unknown-linux-gnu - features: "" + features: "--features gssapi-vendored,libz-static,ssl-vendored" - os: windows-latest target: x86_64-pc-windows-gnu features: "--no-default-features" diff --git a/crates/bin/Cargo.toml b/crates/bin/Cargo.toml index deb347dc..5f599fa0 100644 --- a/crates/bin/Cargo.toml +++ b/crates/bin/Cargo.toml @@ -29,7 +29,7 @@ assert_cmd = "2.1.1" testcontainers = { version = "0.25.0", features = ["blocking"] } [features] -default = ["ssl-vendored", "gssapi-vendored", "libz-static"] +default = ["ssl-vendored", "libz-static"] ssl-vendored = [ "command/ssl-vendored" ] diff --git a/deny.toml b/deny.toml index 2e7e94e3..09147253 100644 --- a/deny.toml +++ b/deny.toml @@ -73,7 +73,8 @@ ignore = [ "RUSTSEC-2024-0436", # paste crate "RUSTSEC-2025-0046", # Host panic with fd_renumber WASIp1 function "RUSTSEC-2024-0320", # yaml-rust is unmaintained. - "RUSTSEC-2025-0118" # Unsound API access to a WebAssembly shared linear memory + "RUSTSEC-2025-0118", # Unsound API access to a WebAssembly shared linear memory + "RUSTSEC-2025-0111" # tokio-tar parses PAX extended headers incorrectly, allows file smuggling #"RUSTSEC-0000-0000", #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" }, #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish From 84dd8e1afd260dc76e9ea4fa711fdd360c8d1984 Mon Sep 17 00:00:00 2001 From: Yann Prono Date: Sun, 23 Nov 2025 18:32:13 +0100 Subject: [PATCH 3/5] feat: show topic configuration in the TopicDetailComponent --- .cargo/audit.toml | 2 +- crates/app/src/admin/mod.rs | 38 +++ crates/app/src/app.rs | 11 +- crates/app/src/configuration/mod.rs | 7 +- crates/app/src/lib.rs | 2 + crates/bin/src/main.rs | 4 +- crates/lib/src/kafka/topic.rs | 43 ++- ...ponent__topic_details_component__draw.snap | 12 +- .../src/component/topic_details_component.rs | 274 ++++++++---------- crates/tui/src/component/ui.rs | 7 +- 10 files changed, 228 insertions(+), 172 deletions(-) create mode 100644 crates/app/src/admin/mod.rs diff --git a/.cargo/audit.toml b/.cargo/audit.toml index 2bc50cc5..f5032a78 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -1,4 +1,4 @@ [advisories] -ignore = ["RUSTSEC-2024-0320", "RUSTSEC-2024-0436", "RUSTSEC-2025-0057", "RUSTSEC-2025-0046", "RUSTSEC-2025-0118"] +ignore = ["RUSTSEC-2024-0320", "RUSTSEC-2024-0436", "RUSTSEC-2025-0057", "RUSTSEC-2025-0046", "RUSTSEC-2025-0118", "RUSTSEC-2025-0057"] informational_warnings = ["unmaintained"] # warn for categories of informational advisories severity_threshold = "low" # CVSS severity ("none", "low", "medium", "high", "critical") diff --git a/crates/app/src/admin/mod.rs b/crates/app/src/admin/mod.rs new file mode 100644 index 00000000..c906a4af --- /dev/null +++ b/crates/app/src/admin/mod.rs @@ -0,0 +1,38 @@ +use lib::{Error, TopicConfig}; +use rdkafka::{ + ClientConfig, + admin::{AdminClient as RDAdminClient, AdminOptions, ResourceSpecifier}, + client::DefaultClientContext, + config::FromClientConfigAndContext, +}; + +pub struct AdminClient { + client: RDAdminClient, + options: AdminOptions, +} + +impl AdminClient { + pub fn new(config: ClientConfig) -> Result { + let client = RDAdminClient::from_config_and_context(&config, DefaultClientContext)?; + let options = AdminOptions::new(); + Ok(Self { client, options }) + } + /// Loads the configuration details for the specified topic from the Kafka cluster. + pub async fn topic_config(&self, topic: &str) -> Result, Error> { + let resource = ResourceSpecifier::Topic(topic); + let result = self + .client + .describe_configs(&[resource], &self.options) + .await?; + + if result.is_empty() { + return Ok(None); + } + + match result.first() { + Some(Ok(c)) => Ok(Some(c.into())), + Some(Err(e)) => Err(Error::Error(e.to_string())), + None => Ok(None), + } + } +} diff --git a/crates/app/src/app.rs b/crates/app/src/app.rs index ade6c93f..1fc8610f 100644 --- a/crates/app/src/app.rs +++ b/crates/app/src/app.rs @@ -1,6 +1,6 @@ //! This app is both a kafka consumer and a kafka admin client. use lib::{ - ConsumerGroupDetail, Error, ExportedKafkaRecord, KafkaRecord, TopicDetail, + ConsumerGroupDetail, Error, ExportedKafkaRecord, KafkaRecord, TopicConfig, TopicDetail, kafka::SchemaRegistryClient, search::offset::FromOffset, }; use rdkafka::{ @@ -15,6 +15,7 @@ use std::{collections::HashSet, fs, time::Duration}; use itertools::Itertools; use crate::{ + AdminClient, configuration::{Configuration, ConsumerConfig, InternalConfig, YozefuConfig}, search::{Search, ValidSearchQuery}, }; @@ -201,6 +202,7 @@ impl App { partitions: metadata.partitions().len(), consumer_groups: vec![], count: self.count_records_in_topic(&topic)?, + config: None, }; let mut consumer_groups = vec![]; let metadata = consumer.fetch_group_list(None, Duration::from_secs(10))?; @@ -211,7 +213,6 @@ impl App { state: g.state().parse()?, }); } - detail.consumer_groups = consumer_groups; results.push(detail); } @@ -219,6 +220,12 @@ impl App { Ok(results) } + pub async fn topic_config_of(&self, topic: &str) -> Result, Error> { + AdminClient::new(self.config.client_config())? + .topic_config(topic) + .await + } + pub fn count_records_in_topic(&self, topic: &str) -> Result { let mut count = 0; let consumer: BaseConsumer = self.config.create_kafka_consumer()?; diff --git a/crates/app/src/configuration/mod.rs b/crates/app/src/configuration/mod.rs index a5144b71..10bd2176 100644 --- a/crates/app/src/configuration/mod.rs +++ b/crates/app/src/configuration/mod.rs @@ -31,11 +31,16 @@ pub trait Configuration { where T: FromClientConfig, { - Self::kafka_client_config_from_properties(self.kafka_config_map().clone()) + self.client_config() .create() .map_err(std::convert::Into::into) } + /// Properties you can set for the kafka consumer: + fn client_config(&self) -> ClientConfig { + Self::kafka_client_config_from_properties(self.kafka_config_map().clone()) + } + fn kafka_client_config_from_properties( kafka_properties: HashMap, ) -> ClientConfig { diff --git a/crates/app/src/lib.rs b/crates/app/src/lib.rs index b384e65b..c2b3ee00 100644 --- a/crates/app/src/lib.rs +++ b/crates/app/src/lib.rs @@ -2,10 +2,12 @@ //! - List topics, and consume records, //! - Fetch information about a given topic, //! - Consume records. +pub mod admin; mod app; pub mod configuration; pub mod search; +pub use admin::*; pub use app::App; /// Name of the application pub const APPLICATION_NAME: &str = "yozefu"; diff --git a/crates/bin/src/main.rs b/crates/bin/src/main.rs index 79b2281b..b256c6fb 100644 --- a/crates/bin/src/main.rs +++ b/crates/bin/src/main.rs @@ -5,8 +5,8 @@ use command::{Cli, Parser, TuiError}; #[tokio::main] async fn main() -> Result<(), TuiError> { // Needed to use `tokio-console` in debug mode - #[cfg(debug_assertions)] - console_subscriber::init(); + // #[cfg(debug_assertions)] + // console_subscriber::init(); let parsed = Cli::::parse(); parsed.execute().await } diff --git a/crates/lib/src/kafka/topic.rs b/crates/lib/src/kafka/topic.rs index bb01759f..c71ce796 100644 --- a/crates/lib/src/kafka/topic.rs +++ b/crates/lib/src/kafka/topic.rs @@ -3,17 +3,21 @@ //! - Number of partitions //! - Number of replicas +use std::collections::HashMap; + +use rdkafka::admin::ConfigResource; use serde::{Deserialize, Serialize}; use strum::{Display, EnumIter, EnumString}; /// Information regarding a given topic, their consumers, the number of partitions... -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Default, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct TopicDetail { pub name: String, pub partitions: usize, pub replicas: usize, pub consumer_groups: Vec, pub count: i64, + pub config: Option, } /// Information regarding a given consumer @@ -82,3 +86,40 @@ pub struct MemberAssignment { pub topic: String, pub partitions: Vec, } + +/// A configurable resource and its current configuration values. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct TopicConfig { + pub name: String, + pub entries: HashMap, +} + +impl From<&ConfigResource> for TopicConfig { + fn from(resource: &ConfigResource) -> Self { + let entries = resource + .entries + .iter() + .filter_map(|entry| { + entry + .value + .as_ref() + .map(|value| (entry.name.clone(), value.clone())) + }) + .collect(); + + TopicConfig { + name: match &resource.specifier { + rdkafka::admin::OwnedResourceSpecifier::Topic(s) => s.to_string(), + rdkafka::admin::OwnedResourceSpecifier::Group(s) => { + panic!("unexpected group specifier '{}'", s) + } + rdkafka::admin::OwnedResourceSpecifier::Broker(s) => { + panic!("unexpected broker specifier '{}'", s) + } + }, + entries, + } + } +} + +impl TopicConfig {} diff --git a/crates/tui/src/component/snapshots/yozefu_tui__component__topic_details_component__draw.snap b/crates/tui/src/component/snapshots/yozefu_tui__component__topic_details_component__draw.snap index 529c85c1..1106782c 100644 --- a/crates/tui/src/component/snapshots/yozefu_tui__component__topic_details_component__draw.snap +++ b/crates/tui/src/component/snapshots/yozefu_tui__component__topic_details_component__draw.snap @@ -5,16 +5,16 @@ expression: terminal.backend() "╭ Topic details ───────────────────────────────────────────────────────────────────────────────────────────────────────╮" "│ │" "│ │" -"│ travel-stories │" -"│ 4 partitions, 6 replicas │" -"│ 0 records, 0 consumer groups │" +"│ travel-stories │" +"│ 4 partitions, 6 replicas │" +"│ 0 records, 0 consumer groups │" "│ │" +"│ 🔬 The following list of consumer members is experimental, use it with caution. │" Hidden by multi-width symbols: [(9, " ")] +"│ │" +"│ Name State Partitions │" "│ │" "│ │" "│ │" -"│ ╭Name─────────────────────────State─────────────────────Partitions───────────────────────────Members─────Lag╮ │" -"│ │ 🔬 The following list of consumer members is experimental, use it with caution. │ │" Hidden by multi-width symbols: [(10, " ")] -"│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │" "│ │" "│ │" "│ │" diff --git a/crates/tui/src/component/topic_details_component.rs b/crates/tui/src/component/topic_details_component.rs index d253e74b..98ec5870 100644 --- a/crates/tui/src/component/topic_details_component.rs +++ b/crates/tui/src/component/topic_details_component.rs @@ -7,17 +7,17 @@ use itertools::Itertools; use lib::{ConsumerGroupDetail, ConsumerGroupState, TopicDetail}; use ratatui::{ Frame, - layout::{Alignment, Constraint, Margin, Rect}, + layout::{Margin, Rect}, style::{Modifier, Style, Stylize}, text::{Line, Span, Text}, - widgets::{ - Block, BorderType, Borders, Cell, Clear, Padding, Paragraph, Row, Table, TableState, - }, + widgets::{Block, BorderType, Borders, Clear, Padding, Paragraph, TableState}, }; use thousands::Separable; use tokio::sync::mpsc::UnboundedSender; -use crate::{Action, Notification, action::Level, error::TuiError}; +use crate::{ + Action, Notification, action::Level, component::scroll_state::ScrollState, error::TuiError, +}; use super::{Component, ComponentName, State, WithHeight}; @@ -26,6 +26,7 @@ pub(crate) struct TopicDetailsComponent { details: Vec, action_tx: Option>, state: TableState, + scroll: ScrollState, refreshing_data: bool, throbber_state: throbber_widgets_tui::ThrobberState, } @@ -34,7 +35,11 @@ impl WithHeight for TopicDetailsComponent { fn content_height(&self) -> usize { self.details .iter() - .map(|e| e.consumer_groups.len()) + .map(|e| { + e.consumer_groups.len() + + 50 + + e.config.as_ref().map(|e| e.entries.len()).unwrap_or(0) + }) .sum::() } } @@ -52,11 +57,11 @@ impl Component for TopicDetailsComponent { match key.code { KeyCode::Char('j') | KeyCode::Down => { self.next(); - //self.scroll.scroll_to_next_line(); + self.scroll.scroll_to_next_line(); } KeyCode::Char('k') | KeyCode::Up => { self.previous(); - //self.scroll.scroll_to_previous_line(); + self.scroll.scroll_to_previous_line(); } KeyCode::Char('[') => { self.first(); @@ -109,7 +114,7 @@ impl Component for TopicDetailsComponent { .borders(Borders::ALL) .border_style(Style::default()) .title(" Topic details ") - .padding(Padding::proportional(2)) + .padding(Padding::horizontal(2)) .border_type(BorderType::Rounded); let block = self.make_block_focused_with_state(state, block); @@ -130,6 +135,7 @@ impl Component for TopicDetailsComponent { return Ok(()); } + let mut lines: Vec> = vec![]; if self.refreshing_data { let full = throbber_widgets_tui::Throbber::default() .label("Refreshing data...") @@ -145,184 +151,134 @@ impl Component for TopicDetailsComponent { ); } + let detail = self.details.first().unwrap(); + + lines.extend(vec![ + Line::from(""), + Line::from(""), + Line::from(detail.name.clone()).style(Style::default().bold()), + Line::from(format!( + "{} partitions, {} replicas", + detail.partitions, detail.replicas + )) + .style(Style::default()), + Line::from(format!( + "{} records, {} consumer groups", + detail.count.separate_with_underscores(), + detail.consumer_groups.len() + )), + Line::from(""), + ]); + if !self.details.is_empty() { - let header_cells = vec![ - Cell::new(Text::from("")), - Cell::new(Text::from("Name")), - Cell::new(Text::from("State")), - Cell::new(Text::from("Partitions").alignment(Alignment::Right)), - Cell::new(Text::from("Members").alignment(Alignment::Right)), - Cell::new(Text::from("Lag").alignment(Alignment::Right)), - ]; - - let header = Row::new(header_cells).bold().height(1); - let mut rows = vec![]; + lines.push(Line::from( + " 🔬 The following list of consumer members is experimental, use it with caution.", + )); + lines.push(Line::from("")); + lines.push( + Line::from(vec![ + Span::from(" "), + Span::from(format!(" {:<42}", "Name")), + Span::from(format!(" {:<23}", "State")), + Span::from(format!(" {:>10}", "Partitions")), + Span::from(format!(" {:>32}", "Members")), + Span::from(format!(" {:>6}", "Lag")), + ]) + .bold(), + ); for detail in &self.details { let consumers_groups = detail.consumer_groups.clone(); - rows.extend( + lines.extend( consumers_groups .into_iter() .sorted_by(|a, b| a.name.cmp(&b.name)) .enumerate() .map(|item| { - Row::new(vec![ - Cell::new( - match item.1.state { - ConsumerGroupState::Unknown => { - Span::styled("⊘", Style::default().fg(state.theme.red)) - } - ConsumerGroupState::Empty => { - Span::styled("◯", Style::default().fg(state.theme.red)) - } - ConsumerGroupState::Dead => { - Span::styled("⊗", Style::default().fg(state.theme.red)) - } - ConsumerGroupState::Stable => Span::styled( - "⏺︎", - Style::default().fg(state.theme.green), - ), - ConsumerGroupState::PreparingRebalance => Span::styled( - "⦿", - Style::default().fg(state.theme.yellow), - ), - ConsumerGroupState::CompletingRebalance => Span::styled( - "⦿", - Style::default().fg(state.theme.yellow), - ), - ConsumerGroupState::Rebalancing => Span::styled( - "⦿", - Style::default().fg(state.theme.yellow), - ), - ConsumerGroupState::UnknownRebalance => Span::styled( - "⊘", - Style::default().fg(state.theme.black), - ), + Line::from(vec![ + (match item.1.state { + ConsumerGroupState::Unknown => { + Span::styled("⊘", Style::default().fg(state.theme.red)) } - .into_right_aligned_line(), - ), - Cell::new(Span::styled(item.1.name.clone(), Style::default())), - Cell::new(Span::styled(item.1.state.to_string(), Style::default())), - Cell::new( - Span::styled( - item.1.members.len().to_string(), - Style::default(), - ) - .into_right_aligned_line(), + ConsumerGroupState::Empty => { + Span::styled("◯", Style::default().fg(state.theme.red)) + } + ConsumerGroupState::Dead => { + Span::styled("⊗", Style::default().fg(state.theme.red)) + } + ConsumerGroupState::Stable => { + Span::styled("⏺︎", Style::default().fg(state.theme.green)) + } + ConsumerGroupState::PreparingRebalance => { + Span::styled("⦿", Style::default().fg(state.theme.yellow)) + } + ConsumerGroupState::CompletingRebalance => { + Span::styled("⦿", Style::default().fg(state.theme.yellow)) + } + ConsumerGroupState::Rebalancing => { + Span::styled("⦿", Style::default().fg(state.theme.yellow)) + } + ConsumerGroupState::UnknownRebalance => { + Span::styled("⊘", Style::default().fg(state.theme.black)) + } + }), + Span::styled( + format!(" {:<42}", item.1.name.clone()), + Style::default(), ), - Cell::new( - Span::styled("1", Style::default()).into_right_aligned_line(), + Span::styled( + format!(" {:<23}", item.1.state.to_string()), + Style::default(), ), - Cell::new( - Span::styled("?", Style::default()).into_right_aligned_line(), + Span::styled( + format!(" {:>10}", item.1.members.len().to_string()), + Style::default(), ), + Span::styled(format!(" {:>32}", "1"), Style::default()), + Span::styled(format!(" {:>6}", "?"), Style::default()), ]) - .height(1_u16) }), ); } - let focused = state.is_focused(&self.id()); - let table = Table::new( - rows, - [ - Constraint::Length(1), - Constraint::Length(42), - Constraint::Length(24), - Constraint::Length(10), - Constraint::Length(32), - Constraint::Length(6), - ], - ) - .column_spacing(2) - .header(header.clone()) - .row_highlight_style(match focused { - true => Style::default() - .bg(state.theme.bg_focused_selected) - .fg(state.theme.fg_focused_selected) - .bold(), - false => Style::default() - .bg(state.theme.bg_unfocused_selected) - .fg(state.theme.fg_unfocused_selected), - }); - - let table_area = block.inner(rect); - let detail = self.details.first().unwrap(); - let text = vec![ - Line::from(detail.name.clone()).style(Style::default().bold()), - Line::from(format!( - "{} partitions, {} replicas", - detail.partitions, detail.replicas - )) - .style(Style::default()), - Line::from(format!( - "{} records, {} consumer groups", - detail.count.separate_with_underscores(), - detail.consumer_groups.len() - )), - Line::from(""), - ]; - - let block_experimental = Block::default() - .borders(Borders::ALL) - .border_style(Style::default()) - .padding(Padding::horizontal(1)) - .border_type(BorderType::Rounded); + // f.render_widget( + // Paragraph::new( + // "🔬 The following list of consumer members is experimental, use it with caution.", + // ) + // .block(block_experimental), + // Rect { + // x: 0, + // y: 10.min(rect.height), // to avoid panicking with 'index outside of buffer' + // width: rect.width + 3, + // height: 3.min(rect.height), + // } + // .inner(Margin::new(7, 0)), + // ); + + if let Some(config) = &detail.config { + lines.push(Line::from("")); + lines.push( + Line::from(format!("{:>42} {}", "Topic configuration", "Value")) + .style(Style::default().bold()), + ); - f.render_widget( - Paragraph::new( - "🔬 The following list of consumer members is experimental, use it with caution.", - ) - .block(block_experimental), - Rect { - x: 0, - y: 10.min(rect.height), // to avoid panicking with 'index outside of buffer' - width: rect.width + 3, - height: 3.min(rect.height), + for (key, value) in config.entries.iter().sorted_by(|a, b| a.0.cmp(b.0)) { + lines.push(Line::from(format!("{:>42} {}", key, value))); } - .inner(Margin::new(7, 0)), - ); - - f.render_stateful_widget( - table, - Rect { - x: table_area.x, - y: table_area.y + 7, - width: table_area.width, - height: table_area.height.saturating_sub(5), - }, - &mut self.state, - ); + } + let number_of_lines = lines.len(); f.render_widget( - Paragraph::new(text) + Paragraph::new(Text::from(lines)) + .scroll((self.scroll.value(), 0)) .style(Style::default()) .block(block.clone()), rect, ); - //f.render_widget(widget, area); - //self.scroll.draw(f, rect, self.content_height()); - - // - // let mut text: Vec> = vec![]; - // for d in &self.details { - // text.push(Line::from(format!( - // "{} - {} {}", - // d.0, - // d.1, - // match d.1 > 1 { - // true => "partitions", - // false => "partition", - // } - // ))); - // for (k, v) in &d.2 { - // text.push(Line::from(format!("{}: lag of {}", k, v))); - // } - // } - // + self.scroll.draw(f, rect, number_of_lines); } Ok(()) @@ -407,6 +363,7 @@ fn test_draw() { replicas: 6, consumer_groups: vec![], count: 0, + config: None, }])) .unwrap(); assert_draw!(component, 120, 20) @@ -423,6 +380,7 @@ fn test_draw_out_of_bounds() { replicas: 6, consumer_groups: vec![], count: 0, + config: None, }])) .unwrap(); //todo!("something needs to be fixed") diff --git a/crates/tui/src/component/ui.rs b/crates/tui/src/component/ui.rs index eb53f638..fe3e476c 100644 --- a/crates/tui/src/component/ui.rs +++ b/crates/tui/src/component/ui.rs @@ -243,7 +243,12 @@ impl Ui { .name("topics-details") .spawn(async move { match app.topic_details(topics) { - Ok(details) => action_tx.send(Action::TopicDetails(details)).unwrap(), + Ok(mut details) => { + for detail in &mut details.iter_mut() { + detail.config = app.topic_config_of(&detail.name).await.ok().flatten(); + } + action_tx.send(Action::TopicDetails(details)).unwrap(); + } Err(e) => action_tx .send(Action::Notification(Notification::new( Level::Error, From 36f8f768f61bf410ed330e065ec1ff9aab235b7f Mon Sep 17 00:00:00 2001 From: Yann Prono Date: Mon, 24 Nov 2025 12:31:07 +0100 Subject: [PATCH 4/5] ci: use `macos-15` for nix --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 985c97e2..353b95c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -344,9 +344,9 @@ jobs: strategy: matrix: platforms: - - os: macos-14 + - os: macos-15 target: aarch64-apple-darwin - - os: macos-14-large + - os: macos-15-large target: x86_64-apple-darwin - os: ubuntu-latest target: x86_64-unknown-linux-gnu From 37c8b9e184a6026b773888182ac90d173e2651ba Mon Sep 17 00:00:00 2001 From: Yann Prono Date: Mon, 24 Nov 2025 12:40:29 +0100 Subject: [PATCH 5/5] chore: Release version v0.0.21 --- Cargo.lock | 12 ++++++------ Cargo.toml | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebefcd48..faf7fee2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6460,7 +6460,7 @@ dependencies = [ [[package]] name = "yozefu" -version = "0.0.20" +version = "0.0.21" dependencies = [ "assert_cmd", "console-subscriber", @@ -6471,7 +6471,7 @@ dependencies = [ [[package]] name = "yozefu-app" -version = "0.0.20" +version = "0.0.21" dependencies = [ "chrono", "directories", @@ -6493,7 +6493,7 @@ dependencies = [ [[package]] name = "yozefu-command" -version = "0.0.20" +version = "0.0.21" dependencies = [ "chrono", "clap", @@ -6522,7 +6522,7 @@ dependencies = [ [[package]] name = "yozefu-lib" -version = "0.0.20" +version = "0.0.21" dependencies = [ "apache-avro", "byteorder", @@ -6544,7 +6544,7 @@ dependencies = [ [[package]] name = "yozefu-tui" -version = "0.0.20" +version = "0.0.21" dependencies = [ "bytesize", "chrono", @@ -6582,7 +6582,7 @@ dependencies = [ [[package]] name = "yozefu-wasm-types" -version = "0.0.20" +version = "0.0.21" dependencies = [ "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index e21dcc52..3d37a56d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ default-members = [ resolver = "3" [workspace.package] -version = "0.0.20" +version = "0.0.21" edition = "2024" authors = ["Yann Prono "] readme = "README.md" @@ -33,12 +33,12 @@ license = "Apache-2.0" rust-version = "1.85.0" [workspace.dependencies] -lib = { package = "yozefu-lib", path = "crates/lib", version = "0.0.20" } -app = { package = "yozefu-app", path = "crates/app", version = "0.0.20" } -command = { package = "yozefu-command", path = "crates/command", version = "0.0.20" } -yozefu = { package = "yozefu", path = "crates/bin", version = "0.0.20" } -tui = { package = "yozefu-tui", path = "crates/tui", version = "0.0.20" } -wasm-types = { package = "wasm-types", path = "crates/wasm-types", version = "0.0.20" } +lib = { package = "yozefu-lib", path = "crates/lib", version = "0.0.21" } +app = { package = "yozefu-app", path = "crates/app", version = "0.0.21" } +command = { package = "yozefu-command", path = "crates/command", version = "0.0.21" } +yozefu = { package = "yozefu", path = "crates/bin", version = "0.0.21" } +tui = { package = "yozefu-tui", path = "crates/tui", version = "0.0.21" } +wasm-types = { package = "wasm-types", path = "crates/wasm-types", version = "0.0.21" } serde_json = { version = "1.0.145", features = ["preserve_order"] } serde = { version = "1.0.228", features = ["derive"] } strum = {version = "0.27.2" }