chore: merge release-v0.6.0 into main
This commit is contained in:
+11
-14
@@ -1,26 +1,23 @@
|
||||
### 2024-01-05
|
||||
### 2024-01-18
|
||||
|
||||
### Chores
|
||||
+ .devcontainer updated, [2313618eb1493ce41d70847b888c32b65fdc40ea], [5af6b8bcd31c3c38ff5a5799c76dc1cbe1167763], [9b0b6b10c3a0c1d5095490cfd3cda18d252f38f5]
|
||||
+ alpine version bump, [061de032dad935c56c6caab419ecb5c9bbac4c7e]
|
||||
+ dependencies updated, [0890991ff1a239fe2d556a0c4eac6ae05beb9b50], [0a7b266b2a358a4788ae877ca8a97f08eac4eef2], [333621f1a7321c1fdf73fd35dd7f3ab165a9dc64], [3e51889cd8a552b1da463ae6a40d5de6eec188f5], [a179bb6f6a7e076269fa830f56c0d4a31cf8488a]
|
||||
+ file formatting, [eb5e74ae67d815bf49f241d2baf319e41cf9adf8]
|
||||
+ Rust 1.75.0 linting, [81be75f27fd32a59ebff57e44c5022ff862df84b]
|
||||
+ dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd], [b6fd35022a99ec0e982ddb154b0450d49c4840e9], [0438c108bdd9815d7eae1b89c47c4e6438f358d6]
|
||||
+ files formatted, [1806165c3e266876b2d1806f7b662d09705f3aad]
|
||||
+ create_release.sh check for unused lint, [d0b27211928f93f8455e1ee5a6a6485c6a21d382]
|
||||
|
||||
### Docs
|
||||
+ screenshot updated, [0231d1bdcda304300d289243a95044ab3bdce85c]
|
||||
+ comment typo, [0ad1ec9d85d6f0cac743b4421d0ad03432c9d717]
|
||||
= Readme updated, screenshot added, [7561a93415c1e1f596b15edba95e7b32a939cd90], [4069e5572f81cb689dbb9f735db919e4636cdccc]
|
||||
|
||||
### Features
|
||||
+ re-arrange columns, container name is now the first column, added a ContainerName & ContainerImage struct via `string_wrapper` macro, closes #32, [e936bb4b78980d0e34a1ef5e9f6f82a9ed0ddc7f]
|
||||
+ Ports section added, closes #21, [65a1afcb0605604ede350a5630c775f94ebb74ee], [7a096a65c40924021fe643fe0aa1067095832df9]
|
||||
|
||||
### Fixes
|
||||
+ Docker Commands hidden, [4301e4709f99fc23ee438bf345b0dc698a05dc4e]
|
||||
+ .gitattributes, [1234ea53897b2ed6ada0eb18cd81b8783a5dc5f5]
|
||||
+ sort arrow now on left of header, [40ddcb727d2c1758d6dd26a58507b85b219f51e2]
|
||||
|
||||
### Refactors
|
||||
+ GitHub workflow action improved, [04b66af2b60c96cfbece0b13109e30b08ef35cc4]
|
||||
+ sort_containers, [ccf8b55a7495982f72b4fb3af6e11a9bd7465216]
|
||||
+ string_wrapper .get() return `&str`, [a722731c6a77e00d1fb13967b51400aa34e72213]
|
||||
+ rename string_wrapper > unit_struct, [27cf53e41f8b379f606c1c27620ee08e79bac57e]
|
||||
|
||||
### Tests
|
||||
+ Finally have tests, currently for layout and associated methods, at the moment running the tests will not interfere with any running Docker containers, [4bcf77db776a36e0a8151ecfbda722a66c4ba46c]
|
||||
|
||||
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 34 KiB |
@@ -1,3 +1,26 @@
|
||||
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.6.0'>v0.6.0</a>
|
||||
### 2024-01-18
|
||||
|
||||
### Chores
|
||||
+ dependencies updated, [53b4bafb](https://github.com/mrjackwills/oxker/commit/53b4bafbe53312fe41608ddf33e865d474222aaa), [58ef1516](https://github.com/mrjackwills/oxker/commit/58ef151600e362048a607c8ae61a5edfe80ab1dd), [b6fd3502](https://github.com/mrjackwills/oxker/commit/b6fd35022a99ec0e982ddb154b0450d49c4840e9), [0438c108](https://github.com/mrjackwills/oxker/commit/0438c108bdd9815d7eae1b89c47c4e6438f358d6)
|
||||
+ files formatted, [1806165c](https://github.com/mrjackwills/oxker/commit/1806165c3e266876b2d1806f7b662d09705f3aad)
|
||||
+ create_release.sh check for unused lint, [d0b27211](https://github.com/mrjackwills/oxker/commit/d0b27211928f93f8455e1ee5a6a6485c6a21d382)
|
||||
|
||||
### Docs
|
||||
= Readme updated, screenshot added, [7561a934](https://github.com/mrjackwills/oxker/commit/7561a93415c1e1f596b15edba95e7b32a939cd90), [4069e557](https://github.com/mrjackwills/oxker/commit/4069e5572f81cb689dbb9f735db919e4636cdccc)
|
||||
|
||||
### Features
|
||||
+ Ports section added, closes [#21](https://github.com/mrjackwills/oxker/issues/21), [65a1afcb](https://github.com/mrjackwills/oxker/commit/65a1afcb0605604ede350a5630c775f94ebb74ee), [7a096a65](https://github.com/mrjackwills/oxker/commit/7a096a65c40924021fe643fe0aa1067095832df9)
|
||||
|
||||
### Fixes
|
||||
+ sort arrow now on left of header, [40ddcb72](https://github.com/mrjackwills/oxker/commit/40ddcb727d2c1758d6dd26a58507b85b219f51e2)
|
||||
|
||||
### Refactors
|
||||
+ rename string_wrapper > unit_struct, [27cf53e4](https://github.com/mrjackwills/oxker/commit/27cf53e41f8b379f606c1c27620ee08e79bac57e)
|
||||
|
||||
### Tests
|
||||
+ Finally have tests, currently for layout and associated methods, at the moment running the tests will not interfere with any running Docker containers, [4bcf77db](https://github.com/mrjackwills/oxker/commit/4bcf77db776a36e0a8151ecfbda722a66c4ba46c)
|
||||
|
||||
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.5.0'>v0.5.0</a>
|
||||
### 2024-01-05
|
||||
|
||||
|
||||
Generated
+44
-44
@@ -52,9 +52,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.5"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
|
||||
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -127,9 +127,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.5"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
@@ -139,9 +139,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bollard"
|
||||
@@ -237,9 +237,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.13"
|
||||
version = "4.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642"
|
||||
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -247,9 +247,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.12"
|
||||
version = "4.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
|
||||
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -295,7 +295,7 @@ version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
@@ -426,9 +426,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -443,9 +443,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.22"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178"
|
||||
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -484,9 +484,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
|
||||
checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
@@ -643,9 +643,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.66"
|
||||
version = "0.3.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
|
||||
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -658,9 +658,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.151"
|
||||
version = "0.2.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -668,7 +668,7 @@ version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -783,7 +783,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "oxker"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -881,9 +881,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.75"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -933,7 +933,7 @@ version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"cassowary",
|
||||
"crossterm",
|
||||
"indoc",
|
||||
@@ -992,18 +992,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.194"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.194"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1110,9 +1110,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.2"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
checksum = "3b187f0231d56fe41bfb12034819dd2bf336422a5866de41bc3fec4b2e3883e8"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
@@ -1382,9 +1382,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@@ -1469,9 +1469,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.89"
|
||||
version = "0.2.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1479,9 +1479,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.89"
|
||||
version = "0.2.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
@@ -1494,9 +1494,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.89"
|
||||
version = "0.2.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1504,9 +1504,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.89"
|
||||
version = "0.2.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1517,9 +1517,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.89"
|
||||
version = "0.2.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
||||
+3
-4
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "oxker"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
edition = "2021"
|
||||
authors = ["Jack Wills <email@mrjackwills.com>"]
|
||||
description = "A simple tui to view & control docker containers"
|
||||
@@ -29,7 +29,7 @@ similar_names = "allow"
|
||||
anyhow = "1.0"
|
||||
bollard = "0.15"
|
||||
cansi = "2.2"
|
||||
clap = { version = "4.4", features = ["derive", "unicode", "color"] }
|
||||
clap = { version = "4.4", features = ["color", "derive", "unicode"] }
|
||||
crossterm = "0.27"
|
||||
futures-util = "0.3"
|
||||
parking_lot = { version = "0.12" }
|
||||
@@ -37,7 +37,7 @@ tokio = { version = "1.35", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
ratatui = "0.25"
|
||||
uuid = { version = "1.6", features = ["v4", "fast-rng"] }
|
||||
uuid = { version = "1.6", features = ["fast-rng", "v4"] }
|
||||
directories = "5.0"
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -48,4 +48,3 @@ codegen-units = 1
|
||||
panic = 'abort'
|
||||
strip = true
|
||||
debug = false
|
||||
|
||||
|
||||
@@ -157,7 +157,8 @@ see <a href="https://forums.raspberrypi.com/viewtopic.php?t=203128" target='_bla
|
||||
|
||||
## Tests
|
||||
|
||||
As of yet untested, needs work
|
||||
~~As of yet untested, needs work~~
|
||||
The work has been done, sp far the tests don't effect any running containers, but this may change in the future.
|
||||
|
||||
```shell
|
||||
cargo test -- --test-threads=1
|
||||
|
||||
+36
-26
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# rust create_release
|
||||
# v0.3.1
|
||||
# v0.4.1
|
||||
|
||||
STAR_LINE='****************************************'
|
||||
CWD=$(pwd)
|
||||
@@ -12,10 +12,9 @@ YELLOW='\033[0;33m'
|
||||
PURPLE='\033[0;35m'
|
||||
RESET='\033[0m'
|
||||
|
||||
|
||||
# $1 string - error message
|
||||
error_close() {
|
||||
echo -e "\n${RED}ERROR - EXITED: ${YELLOW}$1${RESET}\n";
|
||||
echo -e "\n${RED}ERROR - EXITED: ${YELLOW}$1${RESET}\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -56,8 +55,7 @@ get_git_remote_url() {
|
||||
# Check that git status is clean
|
||||
check_git_clean() {
|
||||
GIT_CLEAN=$(git status --porcelain)
|
||||
if [[ -n $GIT_CLEAN ]]
|
||||
then
|
||||
if [[ -n $GIT_CLEAN ]]; then
|
||||
error_close "git dirty"
|
||||
fi
|
||||
}
|
||||
@@ -66,8 +64,7 @@ check_git_clean() {
|
||||
check_git() {
|
||||
CURRENT_GIT_BRANCH=$(git branch --show-current)
|
||||
check_git_clean
|
||||
if [[ ! "$CURRENT_GIT_BRANCH" =~ ^dev$ ]]
|
||||
then
|
||||
if [[ ! "$CURRENT_GIT_BRANCH" =~ ^dev$ ]]; then
|
||||
error_close "not on dev branch"
|
||||
fi
|
||||
}
|
||||
@@ -79,8 +76,7 @@ ask_changelog_update() {
|
||||
printf "%s" "$RELEASE_BODY_TEXT"
|
||||
printf "\n%s\n" "${STAR_LINE}"
|
||||
ask_yn "accept release body"
|
||||
if [[ "$(user_input)" =~ ^y$ ]]
|
||||
then
|
||||
if [[ "$(user_input)" =~ ^y$ ]]; then
|
||||
update_release_body_and_changelog "$RELEASE_BODY_TEXT"
|
||||
else
|
||||
exit
|
||||
@@ -122,8 +118,7 @@ check_tag () {
|
||||
LATEST_TAG=$(git describe --tags "$(git rev-list --tags --max-count=1)")
|
||||
echo -e "\nCurrent tag: ${PURPLE}${LATEST_TAG}${RESET}\n"
|
||||
echo -e "${YELLOW}Choose new tag version:${RESET}\n"
|
||||
if [[ $LATEST_TAG =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$ ]]
|
||||
then
|
||||
if [[ $LATEST_TAG =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$ ]]; then
|
||||
IFS="." read -r MAJOR MINOR PATCH <<<"${LATEST_TAG:1}"
|
||||
else
|
||||
MAJOR="0"
|
||||
@@ -134,23 +129,26 @@ check_tag () {
|
||||
OP_MINOR="minor___v$(update_minor)"
|
||||
OP_PATCH="patch___v$(update_patch)"
|
||||
OPTIONS=("$OP_MAJOR" "$OP_MINOR" "$OP_PATCH")
|
||||
select choice in "${OPTIONS[@]}"
|
||||
do
|
||||
select choice in "${OPTIONS[@]}"; do
|
||||
case $choice in
|
||||
"$OP_MAJOR")
|
||||
MAJOR=$((MAJOR + 1))
|
||||
MINOR=0
|
||||
PATCH=0
|
||||
break;;
|
||||
break
|
||||
;;
|
||||
"$OP_MINOR")
|
||||
MINOR=$((MINOR + 1))
|
||||
PATCH=0
|
||||
break;;
|
||||
break
|
||||
;;
|
||||
"$OP_PATCH")
|
||||
PATCH=$((PATCH + 1))
|
||||
break;;
|
||||
break
|
||||
;;
|
||||
*)
|
||||
error_close "invalid option $REPLY"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
@@ -158,8 +156,7 @@ check_tag () {
|
||||
# ask continue, or quit
|
||||
ask_continue() {
|
||||
ask_yn "continue"
|
||||
if [[ ! "$(user_input)" =~ ^y$ ]]
|
||||
then
|
||||
if [[ ! "$(user_input)" =~ ^y$ ]]; then
|
||||
exit
|
||||
fi
|
||||
}
|
||||
@@ -199,7 +196,6 @@ cargo_build () {
|
||||
cargo_clean
|
||||
}
|
||||
|
||||
|
||||
# $1 text to colourise
|
||||
release_continue() {
|
||||
echo -e "\n${PURPLE}$1${RESET}"
|
||||
@@ -218,8 +214,20 @@ check_typos () {
|
||||
ask_continue
|
||||
}
|
||||
|
||||
# Make sure the unused lint isn't used
|
||||
check_allow_unused() {
|
||||
matches_any=$(find . -type d \( -name .git -o -name target \) -prune -o -type f -exec grep -lE '^#!\[allow\(unused\)\]$' {} +)
|
||||
matches_cargo=$(grep "^unused = \"allow\"" ./Cargo.toml)
|
||||
if [ -n "$matches_any" ]; then
|
||||
error_close "\"#[allow(unused)]\" in ${matches_any}"
|
||||
elif [ -n "$matches_cargo" ]; then
|
||||
error_close "\"unused = \"allow\"\" in Cargo.toml"
|
||||
fi
|
||||
}
|
||||
|
||||
# Full flow to create a new release
|
||||
release_flow() {
|
||||
check_allow_unused
|
||||
check_typos
|
||||
|
||||
check_git
|
||||
@@ -283,7 +291,6 @@ release_flow() {
|
||||
git branch -d "$RELEASE_BRANCH"
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16)
|
||||
options=(
|
||||
@@ -297,22 +304,25 @@ main() {
|
||||
if [ $exitStatus -ne 0 ]; then
|
||||
exit
|
||||
fi
|
||||
for choice in $choices
|
||||
do
|
||||
for choice in $choices; do
|
||||
case $choice in
|
||||
0)
|
||||
exit;;
|
||||
exit
|
||||
;;
|
||||
1)
|
||||
cargo_test
|
||||
main
|
||||
break;;
|
||||
break
|
||||
;;
|
||||
2)
|
||||
release_flow
|
||||
break;;
|
||||
break
|
||||
;;
|
||||
3)
|
||||
cargo_build
|
||||
main
|
||||
break;;
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ services:
|
||||
ipc: private
|
||||
restart: always
|
||||
shm_size: 256MB
|
||||
ports:
|
||||
- "127.0.0.1:4040:4040"
|
||||
networks:
|
||||
- oxker-example-net
|
||||
deploy:
|
||||
|
||||
+165
-15
@@ -4,6 +4,7 @@ use std::{
|
||||
fmt,
|
||||
};
|
||||
|
||||
use bollard::service::Port;
|
||||
use ratatui::{
|
||||
style::Color,
|
||||
widgets::{ListItem, ListState},
|
||||
@@ -49,7 +50,7 @@ impl PartialOrd for ContainerId {
|
||||
|
||||
/// TODO - use string_wrapper for ContainerId?
|
||||
/// ContainerName and ContainerImage are simple structs, used so can implement custom fmt functions to them
|
||||
macro_rules! string_wrapper {
|
||||
macro_rules! unit_struct {
|
||||
($name:ident) => {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct $name(String);
|
||||
@@ -60,6 +61,13 @@ macro_rules! string_wrapper {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl From<&str> for $name {
|
||||
fn from(value: &str) -> Self {
|
||||
Self(value.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl$name {
|
||||
pub fn get(&self) -> &str {
|
||||
self.0.as_str()
|
||||
@@ -90,10 +98,51 @@ macro_rules! string_wrapper {
|
||||
};
|
||||
}
|
||||
|
||||
string_wrapper!(ContainerName);
|
||||
string_wrapper!(ContainerImage);
|
||||
unit_struct!(ContainerName);
|
||||
unit_struct!(ContainerImage);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ContainerPorts {
|
||||
pub ip: Option<String>,
|
||||
pub private: u16,
|
||||
pub public: Option<u16>,
|
||||
}
|
||||
|
||||
impl From<&Port> for ContainerPorts {
|
||||
fn from(value: &Port) -> Self {
|
||||
Self {
|
||||
ip: value.ip.clone(),
|
||||
private: value.private_port,
|
||||
public: value.public_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ContainerPorts {
|
||||
pub fn len_ip(&self) -> usize {
|
||||
self.ip.as_ref().unwrap_or(&String::new()).chars().count()
|
||||
}
|
||||
pub fn len_private(&self) -> usize {
|
||||
format!("{}", self.private).chars().count()
|
||||
}
|
||||
pub fn len_public(&self) -> usize {
|
||||
format!("{}", self.public.unwrap_or_default())
|
||||
.chars()
|
||||
.count()
|
||||
}
|
||||
|
||||
pub fn print(&self) -> (String, String, String) {
|
||||
(
|
||||
self.ip
|
||||
.as_ref()
|
||||
.map_or(String::new(), std::borrow::ToOwned::to_owned),
|
||||
format!("{}", self.private),
|
||||
self.public.map_or(String::new(), |s| s.to_string()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct StatefulList<T> {
|
||||
pub state: ListState,
|
||||
pub items: Vec<T>,
|
||||
@@ -234,13 +283,13 @@ impl fmt::Display for State {
|
||||
}
|
||||
|
||||
/// Items for the container control list
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DockerControls {
|
||||
Pause,
|
||||
Restart,
|
||||
Start,
|
||||
Stop,
|
||||
Unpause,
|
||||
Resume,
|
||||
Delete,
|
||||
}
|
||||
|
||||
@@ -252,7 +301,7 @@ impl DockerControls {
|
||||
Self::Start => Color::Green,
|
||||
Self::Stop => Color::Red,
|
||||
Self::Delete => Color::Gray,
|
||||
Self::Unpause => Color::Blue,
|
||||
Self::Resume => Color::Blue,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +309,7 @@ impl DockerControls {
|
||||
pub fn gen_vec(state: State) -> Vec<Self> {
|
||||
match state {
|
||||
State::Dead | State::Exited => vec![Self::Start, Self::Restart, Self::Delete],
|
||||
State::Paused => vec![Self::Unpause, Self::Stop, Self::Delete],
|
||||
State::Paused => vec![Self::Resume, Self::Stop, Self::Delete],
|
||||
State::Restarting => vec![Self::Stop, Self::Delete],
|
||||
State::Running => vec![Self::Pause, Self::Restart, Self::Stop, Self::Delete],
|
||||
_ => vec![Self::Delete],
|
||||
@@ -276,7 +325,7 @@ impl fmt::Display for DockerControls {
|
||||
Self::Restart => "restart",
|
||||
Self::Start => "start",
|
||||
Self::Stop => "stop",
|
||||
Self::Unpause => "resume",
|
||||
Self::Resume => "resume",
|
||||
};
|
||||
write!(f, "{disp}")
|
||||
}
|
||||
@@ -416,7 +465,7 @@ impl fmt::Display for LogsTz {
|
||||
/// Store the logs alongside a HashSet, each log *should* generate a unique timestamp,
|
||||
/// so if we store the timestamp separately in a HashSet, we can then check if we should insert a log line into the
|
||||
/// stateful list dependent on whethere the timestamp is in the HashSet or not
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Logs {
|
||||
logs: StatefulList<ListItem<'static>>,
|
||||
tz: HashSet<LogsTz>,
|
||||
@@ -475,23 +524,25 @@ impl Logs {
|
||||
}
|
||||
|
||||
/// Info for each container
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ContainerItem {
|
||||
pub created: u64,
|
||||
pub cpu_stats: VecDeque<CpuStats>,
|
||||
pub created: u64,
|
||||
pub docker_controls: StatefulList<DockerControls>,
|
||||
pub id: ContainerId,
|
||||
pub image: ContainerImage,
|
||||
pub is_oxker: bool,
|
||||
pub last_updated: u64,
|
||||
pub logs: Logs,
|
||||
pub mem_limit: ByteStats,
|
||||
pub mem_stats: VecDeque<ByteStats>,
|
||||
pub name: ContainerName,
|
||||
// todo remove option, can be empty vec
|
||||
pub ports: Vec<ContainerPorts>,
|
||||
pub rx: ByteStats,
|
||||
pub state: State,
|
||||
pub status: String,
|
||||
pub tx: ByteStats,
|
||||
pub is_oxker: bool,
|
||||
}
|
||||
|
||||
/// Basic display information, for when running in debug mode
|
||||
@@ -509,6 +560,7 @@ impl fmt::Display for ContainerItem {
|
||||
}
|
||||
|
||||
impl ContainerItem {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// Create a new container item
|
||||
pub fn new(
|
||||
created: u64,
|
||||
@@ -516,14 +568,16 @@ impl ContainerItem {
|
||||
image: String,
|
||||
is_oxker: bool,
|
||||
name: String,
|
||||
ports: Vec<ContainerPorts>,
|
||||
state: State,
|
||||
status: String,
|
||||
) -> Self {
|
||||
let mut docker_controls = StatefulList::new(DockerControls::gen_vec(state));
|
||||
docker_controls.start();
|
||||
|
||||
Self {
|
||||
created,
|
||||
cpu_stats: VecDeque::with_capacity(60),
|
||||
created,
|
||||
docker_controls,
|
||||
id,
|
||||
image: image.into(),
|
||||
@@ -533,6 +587,7 @@ impl ContainerItem {
|
||||
mem_limit: ByteStats::default(),
|
||||
mem_stats: VecDeque::with_capacity(60),
|
||||
name: name.into(),
|
||||
ports,
|
||||
rx: ByteStats::default(),
|
||||
state,
|
||||
status,
|
||||
@@ -594,7 +649,7 @@ impl ContainerItem {
|
||||
}
|
||||
|
||||
/// Container information panel headings + widths, for nice pretty formatting
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Columns {
|
||||
pub name: (Header, u8),
|
||||
pub state: (Header, u8),
|
||||
@@ -623,3 +678,98 @@ impl Columns {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ratatui::widgets::ListItem;
|
||||
|
||||
use crate::{
|
||||
app_data::{ContainerImage, Logs},
|
||||
ui::log_sanitizer,
|
||||
};
|
||||
|
||||
use super::{ByteStats, ContainerName, CpuStats, LogsTz};
|
||||
|
||||
#[test]
|
||||
// Display CpuStats as a string
|
||||
fn test_container_state_cpustats_to_string() {
|
||||
let test = |f: f64, s: &str| {
|
||||
assert_eq!(CpuStats::new(f).to_string(), s);
|
||||
};
|
||||
|
||||
test(0.0, "00.00%");
|
||||
test(1.5, "01.50%");
|
||||
test(15.15, "15.15%");
|
||||
test(150.15, "150.15%");
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Display bytestats as a string, convert into correct data unit (Kb, MB, GB)
|
||||
fn test_container_state_bytestats_to_string() {
|
||||
let test = |u: u64, s: &str| {
|
||||
assert_eq!(ByteStats::new(u).to_string(), s);
|
||||
};
|
||||
|
||||
test(0, "0.00 kB");
|
||||
test(150, "0.15 kB");
|
||||
test(1500, "1.50 kB");
|
||||
test(150_000, "150.00 kB");
|
||||
test(1_500_000, "1.50 MB");
|
||||
test(15_000_000, "15.00 MB");
|
||||
test(150_000_000, "150.00 MB");
|
||||
test(1_500_000_000, "1.50 GB");
|
||||
test(15_000_000_000, "15.00 GB");
|
||||
test(150_000_000_000, "150.00 GB");
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// ContainerName as string truncated correctly
|
||||
fn test_container_state_container_name_to_string() {
|
||||
let result = ContainerName::from("name_01");
|
||||
assert_eq!(result.to_string(), "name_01");
|
||||
|
||||
let result = ContainerName::from("name_01_name_01_name_01_name_01_");
|
||||
assert_eq!(result.to_string(), "name_01_name_01_name_01_name_…");
|
||||
|
||||
let result = result.get();
|
||||
assert_eq!(result, "name_01_name_01_name_01_name_01_");
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// ContainerImage as string truncated correctly
|
||||
fn test_container_state_container_image() {
|
||||
let result = ContainerImage::from("name_01");
|
||||
assert_eq!(result.to_string(), "name_01");
|
||||
|
||||
let result = ContainerImage::from("name_01_name_01_name_01_name_01_");
|
||||
assert_eq!(result.to_string(), "name_01_name_01_name_01_name_…");
|
||||
|
||||
let result = result.get();
|
||||
assert_eq!(result, "name_01_name_01_name_01_name_01_");
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Logs can only contain 1 entry per LogzTz
|
||||
fn test_container_state_logz() {
|
||||
let input = "2023-01-14T19:13:30.783138328Z Lorem ipsum dolor sit amet";
|
||||
let tz = LogsTz::from(input);
|
||||
let mut logs = Logs::default();
|
||||
let line = log_sanitizer::remove_ansi(input);
|
||||
|
||||
logs.insert(ListItem::new(line.clone()), tz.clone());
|
||||
logs.insert(ListItem::new(line.clone()), tz.clone());
|
||||
logs.insert(ListItem::new(line), tz);
|
||||
|
||||
assert_eq!(logs.logs.items.len(), 1);
|
||||
|
||||
let input = "2023-01-15T19:13:30.783138328Z Lorem ipsum dolor sit amet";
|
||||
let tz = LogsTz::from(input);
|
||||
let line = log_sanitizer::remove_ansi(input);
|
||||
|
||||
logs.insert(ListItem::new(line.clone()), tz.clone());
|
||||
logs.insert(ListItem::new(line.clone()), tz.clone());
|
||||
logs.insert(ListItem::new(line), tz);
|
||||
|
||||
assert_eq!(logs.logs.items.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
+1325
-150
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,6 @@ pub enum DockerMessage {
|
||||
Restart(ContainerId),
|
||||
Start(ContainerId),
|
||||
Stop(ContainerId),
|
||||
Unpause(ContainerId),
|
||||
Resume(ContainerId),
|
||||
Update,
|
||||
}
|
||||
|
||||
+148
-6
@@ -71,6 +71,7 @@ pub struct DockerData {
|
||||
impl DockerData {
|
||||
/// Use docker stats to calculate current cpu usage
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
// FIX: this can overflow
|
||||
fn calculate_usage(stats: &Stats) -> f64 {
|
||||
let mut cpu_percentage = 0.0;
|
||||
let previous_cpu = stats.precpu_stats.cpu_usage.total_usage;
|
||||
@@ -150,7 +151,7 @@ impl DockerData {
|
||||
|
||||
app_data
|
||||
.lock()
|
||||
.update_stats(&id, cpu_stats, mem_stat, mem_limit, rx, tx);
|
||||
.update_stats_by_id(&id, cpu_stats, mem_stat, mem_limit, rx, tx);
|
||||
}
|
||||
}
|
||||
spawns.lock().remove(&spawn_id);
|
||||
@@ -162,7 +163,6 @@ impl DockerData {
|
||||
/// Update all stats, spawn each container into own tokio::spawn thread
|
||||
fn update_all_container_stats(&mut self, all_ids: &[(State, ContainerId)]) {
|
||||
for (state, id) in all_ids {
|
||||
// let init = self.init.as_ref().map_or_else(|| None, |x| Some((Arc::clone(x), all_ids.len())));
|
||||
let docker = Arc::clone(&self.docker);
|
||||
let app_data = Arc::clone(&self.app_data);
|
||||
let spawns = Arc::clone(&self.spawns);
|
||||
@@ -387,11 +387,11 @@ impl DockerData {
|
||||
});
|
||||
self.update_everything().await;
|
||||
}
|
||||
DockerMessage::Unpause(id) => {
|
||||
DockerMessage::Resume(id) => {
|
||||
tokio::spawn(async move {
|
||||
let handle = GuiState::start_loading_animation(&gui_state, uuid);
|
||||
if docker.unpause_container(id.get()).await.is_err() {
|
||||
Self::set_error(&app_data, DockerControls::Unpause, &gui_state);
|
||||
Self::set_error(&app_data, DockerControls::Resume, &gui_state);
|
||||
}
|
||||
gui_state.lock().stop_loading_animation(&handle, uuid);
|
||||
});
|
||||
@@ -436,7 +436,7 @@ impl DockerData {
|
||||
}
|
||||
|
||||
/// Send an update message every x ms, where x is the args.docker_interval
|
||||
fn croner(args: &CliArgs, docker_tx: Sender<DockerMessage>) {
|
||||
fn scheduler(args: &CliArgs, docker_tx: Sender<DockerMessage>) {
|
||||
let update_duration = std::time::Duration::from_millis(u64::from(args.docker_interval));
|
||||
let mut now = std::time::Instant::now();
|
||||
tokio::spawn(async move {
|
||||
@@ -472,10 +472,152 @@ impl DockerData {
|
||||
spawns: Arc::new(Mutex::new(HashMap::new())),
|
||||
};
|
||||
inner.initialise_container_data().await;
|
||||
Self::croner(&args, docker_tx);
|
||||
Self::scheduler(&args, docker_tx);
|
||||
inner.message_handler().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tests, use redis-test container, check logs exists, and selector of logs, and that it increases, and matches end, when you run restart on the docker containers
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bollard::container::{
|
||||
BlkioStats, CPUStats, CPUUsage, MemoryStats, PidsStats, StorageStats, ThrottlingData,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn gen_stats(x: u64, y: u64) -> Stats {
|
||||
Stats {
|
||||
read: String::new(),
|
||||
preread: String::new(),
|
||||
num_procs: 0,
|
||||
pids_stats: PidsStats {
|
||||
current: None,
|
||||
limit: None,
|
||||
},
|
||||
network: None,
|
||||
networks: None,
|
||||
memory_stats: MemoryStats {
|
||||
stats: None,
|
||||
max_usage: None,
|
||||
usage: None,
|
||||
failcnt: None,
|
||||
limit: None,
|
||||
commit: None,
|
||||
commit_peak: None,
|
||||
commitbytes: None,
|
||||
commitpeakbytes: None,
|
||||
privateworkingset: None,
|
||||
},
|
||||
blkio_stats: BlkioStats {
|
||||
io_service_bytes_recursive: None,
|
||||
io_serviced_recursive: None,
|
||||
io_queue_recursive: None,
|
||||
io_service_time_recursive: None,
|
||||
io_wait_time_recursive: None,
|
||||
io_merged_recursive: None,
|
||||
io_time_recursive: None,
|
||||
sectors_recursive: None,
|
||||
},
|
||||
cpu_stats: CPUStats {
|
||||
cpu_usage: CPUUsage {
|
||||
percpu_usage: Some(vec![
|
||||
291_593_800,
|
||||
182_192_900,
|
||||
195_048_700,
|
||||
23_032_300,
|
||||
132_928_700,
|
||||
235_555_600,
|
||||
120_225_700,
|
||||
175_752_000,
|
||||
213_060_300,
|
||||
95_321_600,
|
||||
226_821_000,
|
||||
0,
|
||||
109_151_300,
|
||||
0,
|
||||
86_240_200,
|
||||
1_884_400,
|
||||
59_077_300,
|
||||
23_224_900,
|
||||
95_386_300,
|
||||
144_987_400,
|
||||
]),
|
||||
total_usage: 250_000_000,
|
||||
usage_in_usermode: 1_020_000_000,
|
||||
usage_in_kernelmode: 1_030_000_000,
|
||||
},
|
||||
system_cpu_usage: Some(x),
|
||||
online_cpus: Some(1),
|
||||
throttling_data: ThrottlingData {
|
||||
periods: 0,
|
||||
throttled_periods: 0,
|
||||
throttled_time: 0,
|
||||
},
|
||||
},
|
||||
precpu_stats: CPUStats {
|
||||
cpu_usage: CPUUsage {
|
||||
percpu_usage: Some(vec![
|
||||
291_593_800,
|
||||
182_192_900,
|
||||
195_048_700,
|
||||
23_032_300,
|
||||
132_928_700,
|
||||
235_555_600,
|
||||
120_225_700,
|
||||
175_752_000,
|
||||
213_060_300,
|
||||
95_321_600,
|
||||
226_821_000,
|
||||
0,
|
||||
109_151_300,
|
||||
0,
|
||||
86_240_200,
|
||||
1_884_400,
|
||||
59_077_300,
|
||||
23_224_900,
|
||||
93_831_100,
|
||||
144_987_400,
|
||||
]),
|
||||
total_usage: 200_000_000,
|
||||
usage_in_usermode: 1_020_000_000,
|
||||
usage_in_kernelmode: 1_020_000_000,
|
||||
},
|
||||
system_cpu_usage: Some(y),
|
||||
online_cpus: Some(1),
|
||||
throttling_data: ThrottlingData {
|
||||
periods: 0,
|
||||
throttled_periods: 0,
|
||||
throttled_time: 0,
|
||||
},
|
||||
},
|
||||
storage_stats: StorageStats {
|
||||
read_count_normalized: None,
|
||||
read_size_bytes: None,
|
||||
write_count_normalized: None,
|
||||
write_size_bytes: None,
|
||||
},
|
||||
name: "/container_1".to_owned(),
|
||||
id: "1".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::float_cmp)]
|
||||
/// Test the stats calculator, had to cheat here to get round input/outputs
|
||||
fn test_calculate_usage_no_previous_cpu() {
|
||||
let stats = gen_stats(1_000_000_000, 900_000_000);
|
||||
let result = DockerData::calculate_usage(&stats);
|
||||
assert_eq!(result, 50.0);
|
||||
|
||||
let stats = gen_stats(1_000_000_000, 800_000_000);
|
||||
let result = DockerData::calculate_usage(&stats);
|
||||
assert_eq!(result, 25.0);
|
||||
|
||||
let stats = gen_stats(1_000_000_000, 750_000_000);
|
||||
let result = DockerData::calculate_usage(&stats);
|
||||
assert_eq!(result, 20.00);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,11 +268,11 @@ impl InputHandler {
|
||||
// This isn't great, just means you can't send docker commands before full initialization of the program
|
||||
let panel = self.gui_state.lock().get_selected_panel();
|
||||
if panel == SelectablePanel::Commands {
|
||||
let option_command = self.app_data.lock().selected_docker_command();
|
||||
let option_command = self.app_data.lock().selected_docker_controls();
|
||||
|
||||
if let Some(command) = option_command {
|
||||
// Poor way of disallowing commands to be sent to a containerised okxer
|
||||
if self.app_data.lock().is_oxker() {
|
||||
if self.app_data.lock().is_oxker_in_container() {
|
||||
return;
|
||||
};
|
||||
let option_id = self.app_data.lock().get_selected_container_id();
|
||||
@@ -286,8 +286,8 @@ impl InputHandler {
|
||||
DockerControls::Pause => {
|
||||
self.docker_tx.send(DockerMessage::Pause(id)).await.ok()
|
||||
}
|
||||
DockerControls::Unpause => {
|
||||
self.docker_tx.send(DockerMessage::Unpause(id)).await.ok()
|
||||
DockerControls::Resume => {
|
||||
self.docker_tx.send(DockerMessage::Resume(id)).await.ok()
|
||||
}
|
||||
DockerControls::Start => {
|
||||
self.docker_tx.send(DockerMessage::Start(id)).await.ok()
|
||||
@@ -337,7 +337,7 @@ impl InputHandler {
|
||||
match selected_panel {
|
||||
SelectablePanel::Containers => locked_data.containers_start(),
|
||||
SelectablePanel::Logs => locked_data.log_start(),
|
||||
SelectablePanel::Commands => locked_data.docker_command_start(),
|
||||
SelectablePanel::Commands => locked_data.docker_controls_start(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ impl InputHandler {
|
||||
match selected_panel {
|
||||
SelectablePanel::Containers => locked_data.containers_end(),
|
||||
SelectablePanel::Logs => locked_data.log_end(),
|
||||
SelectablePanel::Commands => locked_data.docker_command_end(),
|
||||
SelectablePanel::Commands => locked_data.docker_controls_end(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,7 +481,7 @@ impl InputHandler {
|
||||
match selected_panel {
|
||||
SelectablePanel::Containers => locked_data.containers_next(),
|
||||
SelectablePanel::Logs => locked_data.log_next(),
|
||||
SelectablePanel::Commands => locked_data.docker_command_next(),
|
||||
SelectablePanel::Commands => locked_data.docker_controls_next(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ impl InputHandler {
|
||||
match selected_panel {
|
||||
SelectablePanel::Containers => locked_data.containers_previous(),
|
||||
SelectablePanel::Logs => locked_data.log_previous(),
|
||||
SelectablePanel::Commands => locked_data.docker_command_previous(),
|
||||
SelectablePanel::Commands => locked_data.docker_controls_previous(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+89
@@ -164,3 +164,92 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used, clippy::many_single_char_names, unused)]
|
||||
mod tests {
|
||||
use bollard::service::{ContainerSummary, Port};
|
||||
|
||||
use crate::{
|
||||
app_data::{AppData, ContainerId, ContainerItem, ContainerPorts, State, StatefulList},
|
||||
parse_args::CliArgs,
|
||||
};
|
||||
|
||||
pub const fn gen_args() -> CliArgs {
|
||||
CliArgs {
|
||||
color: false,
|
||||
docker_interval: 1000,
|
||||
gui: true,
|
||||
host: None,
|
||||
in_container: false,
|
||||
save_dir: None,
|
||||
raw: false,
|
||||
show_self: false,
|
||||
timestamp: false,
|
||||
use_cli: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_item(id: &ContainerId, index: usize) -> ContainerItem {
|
||||
ContainerItem::new(
|
||||
u64::try_from(index).unwrap(),
|
||||
id.clone(),
|
||||
format!("image_{index}"),
|
||||
false,
|
||||
format!("container_{index}"),
|
||||
vec![ContainerPorts {
|
||||
ip: None,
|
||||
private: u16::try_from(index).unwrap_or(1) + 8000,
|
||||
public: None,
|
||||
}],
|
||||
State::Running,
|
||||
format!("Up {index} hour"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn gen_appdata(containers: &[ContainerItem]) -> AppData {
|
||||
AppData {
|
||||
containers: StatefulList::new(containers.to_vec()),
|
||||
error: None,
|
||||
sorted_by: None,
|
||||
args: gen_args(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_containers() -> (Vec<ContainerId>, Vec<ContainerItem>) {
|
||||
let ids = (1..=3)
|
||||
.map(|i| ContainerId::from(format!("{i}").as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
let containers = ids
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, id)| gen_item(id, index + 1))
|
||||
.collect::<Vec<_>>();
|
||||
(ids, containers)
|
||||
}
|
||||
|
||||
pub fn gen_container_summary(index: usize, state: &str) -> ContainerSummary {
|
||||
ContainerSummary {
|
||||
id: Some(format!("{index}")),
|
||||
names: Some(vec![format!("container_{}", index)]),
|
||||
image: Some(format!("image_{index}")),
|
||||
image_id: Some(format!("{index}")),
|
||||
command: None,
|
||||
created: Some(i64::try_from(index).unwrap()),
|
||||
ports: Some(vec![Port {
|
||||
ip: None,
|
||||
private_port: u16::try_from(index).unwrap_or(1) + 8000,
|
||||
public_port: None,
|
||||
typ: None,
|
||||
}]),
|
||||
size_rw: None,
|
||||
size_root_fs: None,
|
||||
labels: None,
|
||||
state: Some(state.to_owned()),
|
||||
status: Some(format!("Up {index} hour")),
|
||||
host_config: None,
|
||||
network_settings: None,
|
||||
mounts: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,3 +72,79 @@ pub mod log_sanitizer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ratatui::{
|
||||
style::{Color, Style},
|
||||
text::{Line, Span},
|
||||
};
|
||||
|
||||
use super::log_sanitizer;
|
||||
|
||||
// This spells out "oxker", with each char having a foreground and background colour
|
||||
const INPUT: &str = "\x1b[31;47mo\x1b[32;40mx\x1b[33;41mk\x1b[34;42me\x1b[35;43mr\x1b[0m";
|
||||
|
||||
#[test]
|
||||
/// Return test raw, as in show escape codes
|
||||
fn color_match_raw() {
|
||||
let result = log_sanitizer::raw(INPUT);
|
||||
let expected = vec![Line {
|
||||
spans: [Span {
|
||||
content: std::borrow::Cow::Borrowed(
|
||||
"\x1b[31;47mo\x1b[32;40mx\x1b[33;41mk\x1b[34;42me\x1b[35;43mr\x1b[0m",
|
||||
),
|
||||
style: Style::default(),
|
||||
}]
|
||||
.to_vec(),
|
||||
alignment: None,
|
||||
}];
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Use the escape codes to colorize the text
|
||||
fn color_match_colorize() {
|
||||
let result = log_sanitizer::colorize_logs(INPUT);
|
||||
let expected = vec![Line {
|
||||
spans: vec![
|
||||
Span {
|
||||
content: std::borrow::Cow::Borrowed("o"),
|
||||
style: Style::default().fg(Color::Red).bg(Color::White),
|
||||
},
|
||||
Span {
|
||||
content: std::borrow::Cow::Borrowed("x"),
|
||||
style: Style::default().fg(Color::Green).bg(Color::Black),
|
||||
},
|
||||
Span {
|
||||
content: std::borrow::Cow::Borrowed("k"),
|
||||
style: Style::default().fg(Color::Yellow).bg(Color::Red),
|
||||
},
|
||||
Span {
|
||||
content: std::borrow::Cow::Borrowed("e"),
|
||||
style: Style::default().fg(Color::Blue).bg(Color::Green),
|
||||
},
|
||||
Span {
|
||||
content: std::borrow::Cow::Borrowed("r"),
|
||||
style: Style::default().fg(Color::Magenta).bg(Color::Yellow),
|
||||
},
|
||||
],
|
||||
alignment: None,
|
||||
}];
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Remove all escape ansi codes from given input
|
||||
fn color_match_remove_ansi() {
|
||||
let result = log_sanitizer::remove_ansi(INPUT);
|
||||
let expected = vec![Line {
|
||||
spans: vec![Span {
|
||||
content: std::borrow::Cow::Borrowed("oxker"),
|
||||
style: Style::default(),
|
||||
}],
|
||||
alignment: None,
|
||||
}];
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
|
||||
+2145
-65
File diff suppressed because it is too large
Load Diff
+20
-30
@@ -217,22 +217,6 @@ impl Ui {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn get_wholelayout(f: &Frame) -> std::rc::Rc<[ratatui::layout::Rect]> {
|
||||
Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Min(1), Constraint::Min(100)].as_ref())
|
||||
.split(f.size())
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn get_wholelayout(f: &Frame) -> std::rc::Rc<[ratatui::layout::Rect]> {
|
||||
Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Min(1), Constraint::Min(1), Constraint::Min(100)].as_ref())
|
||||
.split(f.size())
|
||||
}
|
||||
|
||||
/// Frequent data required by multiple framde drawing functions, can reduce mutex reads by placing it all in here
|
||||
#[derive(Debug)]
|
||||
pub struct FrameData {
|
||||
@@ -279,21 +263,16 @@ impl From<(MutexGuard<'_, AppData>, MutexGuard<'_, GuiState>)> for FrameData {
|
||||
fn draw_frame(f: &mut Frame, app_data: &Arc<Mutex<AppData>>, gui_state: &Arc<Mutex<GuiState>>) {
|
||||
let fd = FrameData::from((app_data.lock(), gui_state.lock()));
|
||||
|
||||
let whole_layout = get_wholelayout(f);
|
||||
#[cfg(debug_assertions)]
|
||||
draw_blocks::debug_bar(whole_layout[0], f, app_data.lock().get_debug_string());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
let whole_layout_split = (1, 2);
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
let whole_layout_split = (0, 1);
|
||||
let whole_layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Min(1), Constraint::Min(100)].as_ref())
|
||||
.split(f.size());
|
||||
|
||||
// Split into 3, containers+controls, logs, then graphs
|
||||
let upper_main = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Max(fd.height), Constraint::Percentage(50)].as_ref())
|
||||
.split(whole_layout[whole_layout_split.1]);
|
||||
.split(whole_layout[1]);
|
||||
|
||||
let top_split = if fd.has_containers {
|
||||
vec![Constraint::Percentage(90), Constraint::Percentage(10)]
|
||||
@@ -307,7 +286,7 @@ fn draw_frame(f: &mut Frame, app_data: &Arc<Mutex<AppData>>, gui_state: &Arc<Mut
|
||||
.split(upper_main[0]);
|
||||
|
||||
let lower_split = if fd.has_containers {
|
||||
vec![Constraint::Percentage(75), Constraint::Percentage(25)]
|
||||
vec![Constraint::Percentage(70), Constraint::Percentage(20)]
|
||||
} else {
|
||||
vec![Constraint::Percentage(100)]
|
||||
};
|
||||
@@ -318,11 +297,11 @@ fn draw_frame(f: &mut Frame, app_data: &Arc<Mutex<AppData>>, gui_state: &Arc<Mut
|
||||
.constraints(lower_split)
|
||||
.split(upper_main[1]);
|
||||
|
||||
draw_blocks::containers(app_data, top_panel[0], f, &fd, gui_state, &fd.columns);
|
||||
draw_blocks::containers(app_data, top_panel[0], f, &fd, gui_state);
|
||||
|
||||
draw_blocks::logs(app_data, lower_main[0], f, &fd, gui_state);
|
||||
|
||||
draw_blocks::heading_bar(whole_layout[whole_layout_split.0], f, &fd, gui_state);
|
||||
draw_blocks::heading_bar(whole_layout[0], f, &fd, gui_state);
|
||||
|
||||
if let Some(id) = fd.delete_confirm.as_ref() {
|
||||
app_data.lock().get_container_name_by_id(id).map_or_else(
|
||||
@@ -340,7 +319,18 @@ fn draw_frame(f: &mut Frame, app_data: &Arc<Mutex<AppData>>, gui_state: &Arc<Mut
|
||||
// only draw commands + charts if there are containers
|
||||
if fd.has_containers {
|
||||
draw_blocks::commands(app_data, top_panel[1], f, &fd, gui_state);
|
||||
draw_blocks::chart(f, lower_main[1], app_data);
|
||||
|
||||
// Can calculate the max string length here, and then use that to keep the ports section as small as possible (+4 for some padding + border)
|
||||
let max_lens = app_data.lock().get_longest_port();
|
||||
let ports_len = u16::try_from(max_lens.0 + max_lens.1 + max_lens.2 + 2).unwrap_or(26);
|
||||
|
||||
let lower = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Min(1), Constraint::Max(ports_len)])
|
||||
.split(lower_main[1]);
|
||||
|
||||
draw_blocks::chart(f, lower[0], app_data);
|
||||
draw_blocks::ports(f, lower[1], app_data, max_lens);
|
||||
}
|
||||
|
||||
if let Some((text, instant)) = fd.info_text {
|
||||
|
||||
Reference in New Issue
Block a user