Compare commits
3 commits
95d94218c8
...
8dfbab2a98
Author | SHA1 | Date | |
---|---|---|---|
|
8dfbab2a98 | ||
bd6b0b2d31 | |||
7a4f9a45a6 |
54 changed files with 1634 additions and 397 deletions
385
Cargo.lock
generated
385
Cargo.lock
generated
|
@ -389,7 +389,7 @@ version = "0.9.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
"stacker",
|
||||
]
|
||||
|
||||
|
@ -526,19 +526,6 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.12"
|
||||
|
@ -548,34 +535,6 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
|
@ -624,7 +583,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
|
@ -704,6 +663,15 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "1.0.5"
|
||||
|
@ -711,10 +679,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"redox_users 0.3.5",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.4.5",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
|
@ -1046,13 +1026,16 @@ dependencies = [
|
|||
"assert2",
|
||||
"bytes",
|
||||
"clap",
|
||||
"color-eyre",
|
||||
"derive-with",
|
||||
"derive_more",
|
||||
"directories",
|
||||
"git-conventional",
|
||||
"git-next-core",
|
||||
"git-next-forge-forgejo",
|
||||
"git-next-forge-github",
|
||||
"kxio",
|
||||
"lazy_static",
|
||||
"lettre",
|
||||
"mockall",
|
||||
"notifica",
|
||||
|
@ -1070,6 +1053,7 @@ dependencies = [
|
|||
"time",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
"ulid",
|
||||
"warp",
|
||||
|
@ -1156,9 +1140,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix"
|
||||
version = "0.64.0"
|
||||
version = "0.66.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d78414d29fcc82329080166077e0f7689f4016551fdb334d787c3d040fe2634f"
|
||||
checksum = "9048b8d1ae2104f045cb37e5c450fc49d5d8af22609386bfc739c11ba88995eb"
|
||||
dependencies = [
|
||||
"gix-actor",
|
||||
"gix-archive",
|
||||
|
@ -1180,7 +1164,6 @@ dependencies = [
|
|||
"gix-ignore",
|
||||
"gix-index",
|
||||
"gix-lock",
|
||||
"gix-macros",
|
||||
"gix-mailmap",
|
||||
"gix-negotiate",
|
||||
"gix-object",
|
||||
|
@ -1217,9 +1200,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-actor"
|
||||
version = "0.31.5"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0e454357e34b833cc3a00b6efbbd3dd4d18b24b9fb0c023876ec2645e8aa3f2"
|
||||
checksum = "fc19e312cd45c4a66cd003f909163dc2f8e1623e30a0c0c6df3776e89b308665"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-date",
|
||||
|
@ -1231,22 +1214,23 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-archive"
|
||||
version = "0.13.2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b6bbebdf0223d1d4a69d6027e8b2482daad8eb1a8d3ec97176c7ec58e796d4"
|
||||
checksum = "9147c08a55c1398b755539e2cdd63ff690ffe4a2e5e5e0780ee6ef2b49b0a60a"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-date",
|
||||
"gix-object",
|
||||
"gix-worktree-stream",
|
||||
"jiff",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gix-attributes"
|
||||
version = "0.22.3"
|
||||
version = "0.22.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e37ce99c7e81288c28b703641b6d5d119aacc45c1a6b247156e6249afa486257"
|
||||
checksum = "ebccbf25aa4a973dd352564a9000af69edca90623e8a16dad9cbc03713131311"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-glob",
|
||||
|
@ -1279,9 +1263,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-command"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d76867867da891cbe32021ad454e8cae90242f6afb06762e4dd0d357afd1d7b"
|
||||
checksum = "dff2e692b36bbcf09286c70803006ca3fd56551a311de450be317a0ab8ea92e7"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-path",
|
||||
|
@ -1305,9 +1289,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-config"
|
||||
version = "0.38.0"
|
||||
version = "0.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28f53fd03d1bf09ebcc2c8654f08969439c4556e644ca925f27cf033bc43e658"
|
||||
checksum = "78e797487e6ca3552491de1131b4f72202f282fb33f198b1c34406d765b42bb0"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-config-value",
|
||||
|
@ -1326,9 +1310,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-config-value"
|
||||
version = "0.14.7"
|
||||
version = "0.14.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b328997d74dd15dc71b2773b162cb4af9a25c424105e4876e6d0686ab41c383e"
|
||||
checksum = "03f76169faa0dec598eac60f83d7fcdd739ec16596eca8fb144c88973dbe6f8c"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bstr",
|
||||
|
@ -1339,9 +1323,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-credentials"
|
||||
version = "0.24.4"
|
||||
version = "0.24.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "198588f532e4d1202e04e6c3f50e4d7c060dffc66801c6f53cc246f1d234739e"
|
||||
checksum = "8ce391d305968782f1ae301c4a3d42c5701df7ff1d8bc03740300f6fd12bce78"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-command",
|
||||
|
@ -1356,21 +1340,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-date"
|
||||
version = "0.8.7"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0"
|
||||
checksum = "35c84b7af01e68daf7a6bb8bb909c1ff5edb3ce4326f1f43063a5a96d3c3c8a5"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"itoa",
|
||||
"jiff",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gix-diff"
|
||||
version = "0.44.1"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1996d5c8a305b59709467d80617c9fde48d9d75fd1f4179ea970912630886c9d"
|
||||
checksum = "92c9afd80fff00f8b38b1c1928442feb4cd6d2232a6ed806b6b193151a3d336c"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-command",
|
||||
|
@ -1388,9 +1372,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-dir"
|
||||
version = "0.6.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c975679aa00dd2d757bfd3ddb232e8a188c0094c3306400575a0813858b1365"
|
||||
checksum = "0ed3a9076661359a1c5a27c12ad6c3ebe2dd96b8b3c0af6488ab7c128b7bdd98"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-discover",
|
||||
|
@ -1408,9 +1392,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-discover"
|
||||
version = "0.33.0"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67662731cec3cb31ba3ed2463809493f76d8e5d6c6d245de8b0560438c13450e"
|
||||
checksum = "0577366b9567376bc26e815fd74451ebd0e6218814e242f8e5b7072c58d956d2"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"dunce",
|
||||
|
@ -1436,7 +1420,6 @@ dependencies = [
|
|||
"gix-hash",
|
||||
"gix-trace",
|
||||
"gix-utils",
|
||||
"jwalk",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
|
@ -1448,9 +1431,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-filter"
|
||||
version = "0.11.3"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6547738da28275f4dff4e9f3a0f28509f53f94dd6bd822733c91cb306bca61a"
|
||||
checksum = "4121790ae140066e5b953becc72e7496278138d19239be2e63b5067b0843119e"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"encoding_rs",
|
||||
|
@ -1469,9 +1452,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-fs"
|
||||
version = "0.11.2"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6adf99c27cdf17b1c4d77680c917e0d94d8783d4e1c73d3be0d1d63107163d7a"
|
||||
checksum = "f2bfe6249cfea6d0c0e0990d5226a4cb36f030444ba9e35e0639275db8f98575"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"gix-features",
|
||||
|
@ -1480,9 +1463,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-glob"
|
||||
version = "0.16.4"
|
||||
version = "0.16.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7df15afa265cc8abe92813cd354d522f1ac06b29ec6dfa163ad320575cb447"
|
||||
checksum = "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bstr",
|
||||
|
@ -1507,15 +1490,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "7ddf80e16f3c19ac06ce415a38b8591993d3f73aede049cb561becb5b3a8e242"
|
||||
dependencies = [
|
||||
"gix-hash",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gix-ignore"
|
||||
version = "0.11.3"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6afb8f98e314d4e1adc822449389ada863c174b5707cedd327d67b84dba527"
|
||||
checksum = "e447cd96598460f5906a0f6c75e950a39f98c2705fc755ad2f2020c9e937fab7"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-glob",
|
||||
|
@ -1526,9 +1509,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-index"
|
||||
version = "0.33.1"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a9a44eb55bd84bb48f8a44980e951968ced21e171b22d115d1cdcef82a7d73f"
|
||||
checksum = "0cd4203244444017682176e65fd0180be9298e58ed90bd4a8489a357795ed22d"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bstr",
|
||||
|
@ -1543,7 +1526,7 @@ dependencies = [
|
|||
"gix-traverse",
|
||||
"gix-utils",
|
||||
"gix-validate",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
"itoa",
|
||||
"libc",
|
||||
"memmap2",
|
||||
|
@ -1563,22 +1546,11 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gix-macros"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "999ce923619f88194171a67fb3e6d613653b8d4d6078b529b15a765da0edcc17"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gix-mailmap"
|
||||
version = "0.23.5"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef6daca6edb6a590c7c0533f3f8e75c54663eb56ce08f46f0891db9fc6f09208"
|
||||
checksum = "d7d522c8ec2501e1a5b2b4cb54e83cb5d9a52471c9d23b3a1e8dadaf063752f7"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-actor",
|
||||
|
@ -1588,9 +1560,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-negotiate"
|
||||
version = "0.13.2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ec879fb6307bb63519ba89be0024c6f61b4b9d61f1a91fd2ce572d89fe9c224"
|
||||
checksum = "b4063bf329a191a9e24b6f948a17ccf6698c0380297f5e169cee4f1d2ab9475b"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"gix-commitgraph",
|
||||
|
@ -1604,9 +1576,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-object"
|
||||
version = "0.42.3"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25da2f46b4e7c2fa7b413ce4dffb87f69eaf89c2057e386491f4c55cadbfe386"
|
||||
checksum = "2f5b801834f1de7640731820c2df6ba88d95480dc4ab166a5882f8ff12b88efa"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-actor",
|
||||
|
@ -1623,9 +1595,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-odb"
|
||||
version = "0.61.1"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20d384fe541d93d8a3bb7d5d5ef210780d6df4f50c4e684ccba32665a5e3bc9b"
|
||||
checksum = "a3158068701c17df54f0ab2adda527f5a6aca38fd5fd80ceb7e3c0a2717ec747"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"gix-date",
|
||||
|
@ -1643,9 +1615,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-pack"
|
||||
version = "0.51.1"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0594491fffe55df94ba1c111a6566b7f56b3f8d2e1efc750e77d572f5f5229"
|
||||
checksum = "3223aa342eee21e1e0e403cad8ae9caf9edca55ef84c347738d10681676fd954"
|
||||
dependencies = [
|
||||
"clru",
|
||||
"gix-chunk",
|
||||
|
@ -1664,9 +1636,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-packetline"
|
||||
version = "0.17.5"
|
||||
version = "0.17.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b70486beda0903b6d5b65dfa6e40585098cdf4e6365ca2dff4f74c387354a515"
|
||||
checksum = "8c43ef4d5fe2fa222c606731c8bdbf4481413ee4ef46d61340ec39e4df4c5e49"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"faster-hex",
|
||||
|
@ -1676,9 +1648,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-packetline-blocking"
|
||||
version = "0.17.4"
|
||||
version = "0.17.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31d42378a3d284732e4d589979930d0d253360eccf7ec7a80332e5ccb77e14a"
|
||||
checksum = "b9802304baa798dd6f5ff8008a2b6516d54b74a69ca2d3a2b9e2d6c3b5556b40"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"faster-hex",
|
||||
|
@ -1688,9 +1660,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-path"
|
||||
version = "0.10.9"
|
||||
version = "0.10.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d23d5bbda31344d8abc8de7c075b3cf26e5873feba7c4a15d916bce67382bd9"
|
||||
checksum = "38d5b8722112fa2fa87135298780bc833b0e9f6c56cc82795d209804b3a03484"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-trace",
|
||||
|
@ -1701,9 +1673,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-pathspec"
|
||||
version = "0.7.6"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d307d1b8f84dc8386c4aa20ce0cf09242033840e15469a3ecba92f10cfb5c046"
|
||||
checksum = "5d23bf239532b4414d0e63b8ab3a65481881f7237ed9647bb10c1e3cc54c5ceb"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bstr",
|
||||
|
@ -1716,9 +1688,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-prompt"
|
||||
version = "0.8.6"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e0595d2be4b6d6a71a099e989bdd610882b882da35fb8503d91d6f81aa0936f"
|
||||
checksum = "74fde865cdb46b30d8dad1293385d9bcf998d3a39cbf41bee67d0dab026fe6b1"
|
||||
dependencies = [
|
||||
"gix-command",
|
||||
"gix-config-value",
|
||||
|
@ -1729,9 +1701,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-protocol"
|
||||
version = "0.45.2"
|
||||
version = "0.45.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bad8da8e89f24177bd77947092199bb13dcc318bbd73530ba8a05e6d6adaaa9d"
|
||||
checksum = "cc43a1006f01b5efee22a003928c9eb83dde2f52779ded9d4c0732ad93164e3e"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-credentials",
|
||||
|
@ -1758,9 +1730,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-ref"
|
||||
version = "0.45.0"
|
||||
version = "0.47.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "636e96a0a5562715153fee098c217110c33a6f8218f08f4687ff99afde159bb5"
|
||||
checksum = "ae0d8406ebf9aaa91f55a57f053c5a1ad1a39f60fdf0303142b7be7ea44311e5"
|
||||
dependencies = [
|
||||
"gix-actor",
|
||||
"gix-features",
|
||||
|
@ -1779,9 +1751,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-refspec"
|
||||
version = "0.23.1"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6868f8cd2e62555d1f7c78b784bece43ace40dd2a462daf3b588d5416e603f37"
|
||||
checksum = "ebb005f82341ba67615ffdd9f7742c87787544441c88090878393d0682869ca6"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-hash",
|
||||
|
@ -1793,9 +1765,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-revision"
|
||||
version = "0.27.2"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01b13e43c2118c4b0537ddac7d0821ae0dfa90b7b8dbf20c711e153fb749adce"
|
||||
checksum = "ba4621b219ac0cdb9256883030c3d56a6c64a6deaa829a92da73b9a576825e1e"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-date",
|
||||
|
@ -1809,9 +1781,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-revwalk"
|
||||
version = "0.13.2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b030ccaab71af141f537e0225f19b9e74f25fefdba0372246b844491cab43e0"
|
||||
checksum = "b41e72544b93084ee682ef3d5b31b1ba4d8fa27a017482900e5e044d5b1b3984"
|
||||
dependencies = [
|
||||
"gix-commitgraph",
|
||||
"gix-date",
|
||||
|
@ -1824,9 +1796,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-sec"
|
||||
version = "0.10.7"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1547d26fa5693a7f34f05b4a3b59a90890972922172653bcb891ab3f09f436df"
|
||||
checksum = "0fe4d52f30a737bbece5276fab5d3a8b276dc2650df963e293d0673be34e7a5f"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"gix-path",
|
||||
|
@ -1836,9 +1808,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-status"
|
||||
version = "0.11.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83f7b084cb65c3d007ce6bb479755ca13d602ca3cd91c4f08d7e59904de33736"
|
||||
checksum = "f70d35ba639f0c16a6e4cca81aa374a05f07b23fa36ee8beb72c100d98b4ffea"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"filetime",
|
||||
|
@ -1859,9 +1831,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-submodule"
|
||||
version = "0.12.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f2e0f69aa00805e39d39ec80472a7e9da20ed5d73318b27925a2cc198e854fd"
|
||||
checksum = "529d0af78cc2f372b3218f15eb1e3d1635a21c8937c12e2dd0b6fc80c2ca874b"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-config",
|
||||
|
@ -1896,9 +1868,9 @@ checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e"
|
|||
|
||||
[[package]]
|
||||
name = "gix-transport"
|
||||
version = "0.42.2"
|
||||
version = "0.42.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27c02b83763ffe95bcc27ce5821b2b7f843315a009c06f1cd59c9b66c508c058"
|
||||
checksum = "421dcccab01b41a15d97b226ad97a8f9262295044e34fbd37b10e493b0a6481f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bstr",
|
||||
|
@ -1915,9 +1887,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-traverse"
|
||||
version = "0.39.2"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e499a18c511e71cf4a20413b743b9f5bcf64b3d9e81e9c3c6cd399eae55a8840"
|
||||
checksum = "030da39af94e4df35472e9318228f36530989327906f38e27807df305fccb780"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"gix-commitgraph",
|
||||
|
@ -1932,9 +1904,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-url"
|
||||
version = "0.27.4"
|
||||
version = "0.27.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2eb9b35bba92ea8f0b5ab406fad3cf6b87f7929aa677ff10aa042c6da621156"
|
||||
checksum = "fd280c5e84fb22e128ed2a053a0daeacb6379469be6a85e3d518a0636e160c89"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-features",
|
||||
|
@ -1957,9 +1929,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-validate"
|
||||
version = "0.8.5"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf"
|
||||
checksum = "81f2badbb64e57b404593ee26b752c26991910fd0d81fe6f9a71c1a8309b6c86"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"thiserror",
|
||||
|
@ -1967,9 +1939,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-worktree"
|
||||
version = "0.34.1"
|
||||
version = "0.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26f7326ebe0b9172220694ea69d344c536009a9b98fb0f9de092c440f3efe7a6"
|
||||
checksum = "c312ad76a3f2ba8e865b360d5cb3aa04660971d16dec6dd0ce717938d903149a"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-attributes",
|
||||
|
@ -1986,9 +1958,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-worktree-state"
|
||||
version = "0.11.1"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ed6205b5f51067a485b11843babcf3304c0799e265a06eb0dde7f69cd85cd8"
|
||||
checksum = "7b05c4b313fa702c0bacd5068dd3e01671da73b938fade97676859fee286de43"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gix-features",
|
||||
|
@ -2006,9 +1978,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gix-worktree-stream"
|
||||
version = "0.13.1"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e35d4896249a41856f44571d94d7583b9f3b9cd1a75eaef4f34a4aa2981bed21"
|
||||
checksum = "68e81b87c1a3ece22a54b682d6fdc37fbb3977132da972cafe5ec07175fddbca"
|
||||
dependencies = [
|
||||
"gix-attributes",
|
||||
"gix-features",
|
||||
|
@ -2066,12 +2038,6 @@ dependencies = [
|
|||
"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.14.5"
|
||||
|
@ -2371,12 +2337,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "imara-diff"
|
||||
version = "0.1.5"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8"
|
||||
checksum = "fc9da1a252bd44cd341657203722352efc9bc0c847d06ea6d2dc1cd1135e0a01"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"hashbrown 0.12.3",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2392,7 +2358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2467,6 +2433,31 @@ version = "1.0.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2b7379a75544c94b3da32821b0bf41f9062e9970e23b78cc577d0d89676d16"
|
||||
dependencies = [
|
||||
"jiff-tzdb-platform",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05fac328b3df1c0f18a3c2ab6cb7e06e4e549f366017d796e3e66b6d6889abe6"
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb-platform"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8da387d5feaf355954c2c122c194d6df9c57d865125a67984bb453db5336940"
|
||||
dependencies = [
|
||||
"jiff-tzdb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
|
@ -2476,16 +2467,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jwalk"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2735847566356cd2179a2a38264839308f7079fa96e6bd5a42d740460e003c56"
|
||||
dependencies = [
|
||||
"crossbeam",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
|
@ -2537,9 +2518,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
|
@ -2581,6 +2562,16 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
|
@ -2609,7 +2600,7 @@ version = "0.12.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2871,15 +2862,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
|
@ -2968,6 +2950,12 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
|
@ -3222,26 +3210,6 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
|
@ -3277,6 +3245,17 @@ dependencies = [
|
|||
"rust-argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
|
||||
dependencies = [
|
||||
"getrandom 0.2.14",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.4"
|
||||
|
@ -3972,14 +3951,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3988,16 +3963,6 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
|
|
@ -28,13 +28,17 @@ git-next-forge-github = { path = "crates/forge-github", version = "0.13" }
|
|||
|
||||
# TUI
|
||||
ratatui = "0.28"
|
||||
directories = "5.0.1"
|
||||
lazy_static = "1.5.0"
|
||||
color-eyre = "0.6.3"
|
||||
|
||||
# CLI parsing
|
||||
clap = { version = "4.5", features = ["cargo", "derive"] }
|
||||
|
||||
# logging
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tracing-error = "0.2.0"
|
||||
|
||||
# base64 decoding
|
||||
base64 = "0.22"
|
||||
|
@ -46,7 +50,7 @@ hex = "0.4"
|
|||
|
||||
# git
|
||||
# gix = "0.62"
|
||||
gix = { version = "0.64", features = [
|
||||
gix = { version = "0.66", features = [
|
||||
"dirwalk",
|
||||
"blocking-http-transport-reqwest-rust-tls",
|
||||
] }
|
||||
|
|
|
@ -12,10 +12,11 @@ keywords = { workspace = true }
|
|||
categories = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["forgejo", "github"]
|
||||
# default = ["forgejo", "github"]
|
||||
default = ["forgejo", "github", "tui"]
|
||||
forgejo = ["git-next-forge-forgejo"]
|
||||
github = ["git-next-forge-github"]
|
||||
tui = ["ratatui"]
|
||||
tui = ["ratatui", "directories", "lazy_static"]
|
||||
|
||||
[dependencies]
|
||||
git-next-core = { workspace = true }
|
||||
|
@ -24,6 +25,9 @@ git-next-forge-github = { workspace = true, optional = true }
|
|||
|
||||
# TUI
|
||||
ratatui = { workspace = true, optional = true }
|
||||
directories = { workspace = true, optional = true }
|
||||
lazy_static = { workspace = true, optional = true }
|
||||
color-eyre = { workspace = true }
|
||||
|
||||
# CLI parsing
|
||||
clap = { workspace = true }
|
||||
|
@ -34,6 +38,7 @@ kxio = { workspace = true }
|
|||
# logging
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
tracing-error.workspace = true
|
||||
|
||||
# Conventional Commit check
|
||||
git-conventional = { workspace = true }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
use anyhow::{Context, Result};
|
||||
use color_eyre::{eyre::Context, Result};
|
||||
use kxio::fs::FileSystem;
|
||||
|
||||
pub fn run(fs: &FileSystem) -> Result<()> {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
//
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
mod alerts;
|
||||
mod file_watcher;
|
||||
mod forge;
|
||||
|
@ -17,8 +19,8 @@ use git_next_core::git;
|
|||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use kxio::{fs, network::Network};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
|
|
@ -17,15 +17,13 @@ use tracing::{info, instrument, warn};
|
|||
// advance next to the next commit towards the head of the dev branch
|
||||
#[instrument(fields(next), skip_all)]
|
||||
pub fn advance_next(
|
||||
next: &Commit,
|
||||
main: &Commit,
|
||||
dev_commit_history: &[Commit],
|
||||
commit: Option<Commit>,
|
||||
force: git_next_core::git::push::Force,
|
||||
repo_details: RepoDetails,
|
||||
repo_config: RepoConfig,
|
||||
open_repository: &dyn OpenRepositoryLike,
|
||||
message_token: MessageToken,
|
||||
) -> Result<MessageToken> {
|
||||
let (commit, force) = find_next_commit_on_dev(next, main, dev_commit_history);
|
||||
let commit = commit.ok_or_else(|| Error::NextAtDev)?;
|
||||
validate_commit_message(commit.message())?;
|
||||
info!("Advancing next to commit '{}'", commit);
|
||||
|
|
|
@ -5,11 +5,14 @@ use git_next_core::RepoConfigSource;
|
|||
|
||||
use tracing::warn;
|
||||
|
||||
use crate::repo::{
|
||||
branch::advance_main,
|
||||
do_send,
|
||||
messages::{AdvanceMain, LoadConfigFromRepo, ValidateRepo},
|
||||
RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
branch::advance_main,
|
||||
do_send,
|
||||
messages::{AdvanceMain, LoadConfigFromRepo, ValidateRepo},
|
||||
RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<AdvanceMain> for RepoActor {
|
||||
|
@ -26,13 +29,13 @@ impl Handler<AdvanceMain> for RepoActor {
|
|||
let repo_details = self.repo_details.clone();
|
||||
let addr = ctx.address();
|
||||
let message_token = self.message_token;
|
||||
let commit = msg.unwrap();
|
||||
|
||||
match advance_main(
|
||||
msg.unwrap(),
|
||||
&repo_details,
|
||||
&repo_config,
|
||||
&**open_repository,
|
||||
) {
|
||||
self.update_tui(RepoUpdate::AdvancingMain {
|
||||
commit: commit.clone(),
|
||||
});
|
||||
|
||||
match advance_main(commit, &repo_details, &repo_config, &**open_repository) {
|
||||
Err(err) => {
|
||||
warn!("advance main: {err}");
|
||||
}
|
||||
|
|
|
@ -3,11 +3,14 @@ use actix::prelude::*;
|
|||
|
||||
use tracing::warn;
|
||||
|
||||
use crate::repo::{
|
||||
branch::advance_next,
|
||||
do_send,
|
||||
messages::{AdvanceNext, AdvanceNextPayload, ValidateRepo},
|
||||
RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
branch::{advance_next, find_next_commit_on_dev},
|
||||
do_send,
|
||||
messages::{AdvanceNext, AdvanceNextPayload, ValidateRepo},
|
||||
RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<AdvanceNext> for RepoActor {
|
||||
|
@ -20,6 +23,7 @@ impl Handler<AdvanceNext> for RepoActor {
|
|||
let Some(open_repository) = &self.open_repository else {
|
||||
return;
|
||||
};
|
||||
|
||||
let AdvanceNextPayload {
|
||||
next,
|
||||
main,
|
||||
|
@ -29,10 +33,15 @@ impl Handler<AdvanceNext> for RepoActor {
|
|||
let repo_config = repo_config.clone();
|
||||
let addr = ctx.address();
|
||||
|
||||
let (commit, force) = find_next_commit_on_dev(&next, &main, &dev_commit_history);
|
||||
self.update_tui(RepoUpdate::AdvancingNext {
|
||||
commit: commit.clone(),
|
||||
force: force.clone(),
|
||||
});
|
||||
|
||||
match advance_next(
|
||||
&next,
|
||||
&main,
|
||||
&dev_commit_history,
|
||||
commit,
|
||||
force,
|
||||
repo_details,
|
||||
repo_config,
|
||||
&**open_repository,
|
||||
|
|
|
@ -3,10 +3,13 @@ use actix::prelude::*;
|
|||
|
||||
use tracing::{debug, Instrument as _};
|
||||
|
||||
use crate::repo::{
|
||||
do_send,
|
||||
messages::{CheckCIStatus, ReceiveCIStatus},
|
||||
RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
do_send,
|
||||
messages::{CheckCIStatus, ReceiveCIStatus},
|
||||
RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<CheckCIStatus> for RepoActor {
|
||||
|
@ -14,11 +17,13 @@ impl Handler<CheckCIStatus> for RepoActor {
|
|||
|
||||
fn handle(&mut self, msg: CheckCIStatus, ctx: &mut Self::Context) -> Self::Result {
|
||||
crate::repo::logger(self.log.as_ref(), "start: CheckCIStatus");
|
||||
|
||||
let addr = ctx.address();
|
||||
let forge = self.forge.duplicate();
|
||||
let next = msg.unwrap();
|
||||
let log = self.log.clone();
|
||||
|
||||
self.update_tui(RepoUpdate::CheckingCI);
|
||||
// get the status - pass, fail, pending (all others map to fail, e.g. error)
|
||||
async move {
|
||||
let status = forge.commit_status(&next).await;
|
||||
|
|
|
@ -4,10 +4,13 @@ use actix::prelude::*;
|
|||
use git_next_core::git;
|
||||
use tracing::{debug, instrument, warn};
|
||||
|
||||
use crate::repo::{
|
||||
do_send, logger,
|
||||
messages::{CloneRepo, LoadConfigFromRepo, RegisterWebhook},
|
||||
RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
do_send, logger,
|
||||
messages::{CloneRepo, LoadConfigFromRepo, RegisterWebhook},
|
||||
RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<CloneRepo> for RepoActor {
|
||||
|
@ -15,21 +18,26 @@ impl Handler<CloneRepo> for RepoActor {
|
|||
#[instrument(name = "RepoActor::CloneRepo", skip_all, fields(repo = %self.repo_details))]
|
||||
fn handle(&mut self, _msg: CloneRepo, ctx: &mut Self::Context) -> Self::Result {
|
||||
logger(self.log.as_ref(), "Handler: CloneRepo: start");
|
||||
self.update_tui(RepoUpdate::Opening);
|
||||
debug!("Handler: CloneRepo: start");
|
||||
match git::repository::open(&*self.repository_factory, &self.repo_details) {
|
||||
Ok(repository) => {
|
||||
logger(self.log.as_ref(), "open okay");
|
||||
debug!("open okay");
|
||||
self.update_tui(RepoUpdate::Opened);
|
||||
self.open_repository.replace(repository);
|
||||
if self.repo_details.repo_config.is_none() {
|
||||
self.update_tui(RepoUpdate::LoadingConfigFromRepo);
|
||||
do_send(&ctx.address(), LoadConfigFromRepo, self.log.as_ref());
|
||||
} else {
|
||||
self.update_tui(RepoUpdate::RegisteringWebhook);
|
||||
do_send(&ctx.address(), RegisterWebhook::new(), self.log.as_ref());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
logger(self.log.as_ref(), "open failed");
|
||||
warn!("Could not open repo: {err:?}");
|
||||
self.alert_tui(err.to_string());
|
||||
}
|
||||
}
|
||||
debug!("Handler: CloneRepo: finish");
|
||||
|
|
|
@ -5,10 +5,13 @@ use git_next_core::git::UserNotification;
|
|||
|
||||
use tracing::{debug, instrument, Instrument as _};
|
||||
|
||||
use crate::repo::{
|
||||
do_send, load,
|
||||
messages::{LoadConfigFromRepo, ReceiveRepoConfig},
|
||||
notify_user, RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
do_send, load,
|
||||
messages::{LoadConfigFromRepo, ReceiveRepoConfig},
|
||||
notify_user, RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<LoadConfigFromRepo> for RepoActor {
|
||||
|
@ -16,6 +19,7 @@ impl Handler<LoadConfigFromRepo> for RepoActor {
|
|||
#[instrument(name = "Repocrate::repo::LoadConfigFromRepo", skip_all, fields(repo = %self.repo_details))]
|
||||
fn handle(&mut self, _msg: LoadConfigFromRepo, ctx: &mut Self::Context) -> Self::Result {
|
||||
debug!("Handler: LoadConfigFromRepo: start");
|
||||
self.update_tui(RepoUpdate::LoadingConfigFromRepo);
|
||||
let Some(open_repository) = &self.open_repository else {
|
||||
return;
|
||||
};
|
||||
|
|
|
@ -4,10 +4,13 @@ use actix::prelude::*;
|
|||
use git_next_core::git::{forge::commit::Status, graph, UserNotification};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::repo::{
|
||||
delay_send, do_send, logger,
|
||||
messages::{AdvanceMain, ReceiveCIStatus, ValidateRepo},
|
||||
notify_user, RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
delay_send, do_send, logger,
|
||||
messages::{AdvanceMain, ReceiveCIStatus, ValidateRepo},
|
||||
notify_user, RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<ReceiveCIStatus> for RepoActor {
|
||||
|
@ -22,6 +25,11 @@ impl Handler<ReceiveCIStatus> for RepoActor {
|
|||
let repo_alias = self.repo_details.repo_alias.clone();
|
||||
let message_token = self.message_token;
|
||||
let sleep_duration = self.sleep_duration;
|
||||
let graph_log = graph::log(&self.repo_details);
|
||||
self.update_tui_log(graph_log.clone());
|
||||
self.update_tui(RepoUpdate::ReceiveCIStatus {
|
||||
status: status.clone(),
|
||||
});
|
||||
|
||||
debug!(?status, "");
|
||||
match status {
|
||||
|
@ -34,13 +42,14 @@ impl Handler<ReceiveCIStatus> for RepoActor {
|
|||
}
|
||||
Status::Fail => {
|
||||
tracing::warn!("Checks have failed");
|
||||
|
||||
notify_user(
|
||||
self.notify_user_recipient.as_ref(),
|
||||
UserNotification::CICheckFailed {
|
||||
forge_alias,
|
||||
repo_alias,
|
||||
commit: next,
|
||||
log: graph::log(&self.repo_details),
|
||||
log: graph_log,
|
||||
},
|
||||
log.as_ref(),
|
||||
);
|
||||
|
|
|
@ -2,10 +2,13 @@
|
|||
use actix::prelude::*;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::repo::{
|
||||
do_send,
|
||||
messages::{ReceiveRepoConfig, RegisterWebhook},
|
||||
RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
do_send,
|
||||
messages::{ReceiveRepoConfig, RegisterWebhook},
|
||||
RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<ReceiveRepoConfig> for RepoActor {
|
||||
|
@ -13,8 +16,11 @@ impl Handler<ReceiveRepoConfig> for RepoActor {
|
|||
#[instrument(name = "RepoActor::ReceiveRepoConfig", skip_all, fields(repo = %self.repo_details, branches = ?msg))]
|
||||
fn handle(&mut self, msg: ReceiveRepoConfig, ctx: &mut Self::Context) -> Self::Result {
|
||||
let repo_config = msg.unwrap();
|
||||
self.update_tui(RepoUpdate::ReceiveRepoConfig {
|
||||
repo_config: repo_config.clone(),
|
||||
});
|
||||
self.repo_details.repo_config.replace(repo_config);
|
||||
|
||||
self.update_tui_branches();
|
||||
do_send(&ctx.address(), RegisterWebhook::new(), self.log.as_ref());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
//
|
||||
use actix::prelude::*;
|
||||
|
||||
use tracing::{debug, Instrument as _};
|
||||
use tracing::{debug, error, Instrument as _};
|
||||
|
||||
use crate::repo::{
|
||||
do_send,
|
||||
messages::{RegisterWebhook, WebhookRegistered},
|
||||
notify_user, RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
do_send,
|
||||
messages::{RegisterWebhook, WebhookRegistered},
|
||||
notify_user, RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
use git_next_core::git::UserNotification;
|
||||
|
@ -25,11 +28,12 @@ impl Handler<RegisterWebhook> for RepoActor {
|
|||
let addr = ctx.address();
|
||||
let notify_user_recipient = self.notify_user_recipient.clone();
|
||||
let log = self.log.clone();
|
||||
self.update_tui(RepoUpdate::RegisteringWebhook);
|
||||
debug!("registering webhook");
|
||||
async move {
|
||||
match forge.register_webhook(&repo_listen_url).await {
|
||||
Ok(registered_webhook) => {
|
||||
debug!(?registered_webhook, "");
|
||||
debug!(?registered_webhook, "webhook registered");
|
||||
do_send(
|
||||
&addr,
|
||||
WebhookRegistered::from(registered_webhook),
|
||||
|
@ -37,6 +41,7 @@ impl Handler<RegisterWebhook> for RepoActor {
|
|||
);
|
||||
}
|
||||
Err(err) => {
|
||||
error!(?err, "failed to register webhook");
|
||||
notify_user(
|
||||
notify_user_recipient.as_ref(),
|
||||
UserNotification::WebhookRegistration {
|
||||
|
@ -52,6 +57,8 @@ impl Handler<RegisterWebhook> for RepoActor {
|
|||
.in_current_span()
|
||||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
} else {
|
||||
self.alert_tui("already have a webhook id - cant register webhook");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,17 @@ use actix::prelude::*;
|
|||
|
||||
use tracing::{debug, warn, Instrument as _};
|
||||
|
||||
use crate::repo::{messages::UnRegisterWebhook, RepoActor};
|
||||
use crate::{
|
||||
repo::{messages::UnRegisterWebhook, RepoActor},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<UnRegisterWebhook> for RepoActor {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, _msg: UnRegisterWebhook, ctx: &mut Self::Context) -> Self::Result {
|
||||
if let Some(webhook_id) = self.webhook_id.take() {
|
||||
self.update_tui(RepoUpdate::UnregisteringWebhook);
|
||||
let forge = self.forge.duplicate();
|
||||
debug!("unregistering webhook");
|
||||
async move {
|
||||
|
|
|
@ -3,10 +3,13 @@ use actix::prelude::*;
|
|||
|
||||
use tracing::{debug, instrument, Instrument as _};
|
||||
|
||||
use crate::repo::{
|
||||
do_send, logger,
|
||||
messages::{AdvanceNext, AdvanceNextPayload, CheckCIStatus, MessageToken, ValidateRepo},
|
||||
notify_user, RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
do_send, logger,
|
||||
messages::{AdvanceNext, AdvanceNextPayload, CheckCIStatus, MessageToken, ValidateRepo},
|
||||
notify_user, RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
use git_next_core::git::validation::positions::{validate, Error, Positions};
|
||||
|
@ -41,14 +44,18 @@ impl Handler<ValidateRepo> for RepoActor {
|
|||
format!("accepted token: {}", self.message_token),
|
||||
);
|
||||
|
||||
self.update_tui(RepoUpdate::ValidateRepo);
|
||||
|
||||
// Repository positions
|
||||
let Some(ref open_repository) = self.open_repository else {
|
||||
logger(self.log.as_ref(), "no open repository");
|
||||
self.alert_tui("repo not open");
|
||||
return;
|
||||
};
|
||||
logger(self.log.as_ref(), "have open repository");
|
||||
let Some(repo_config) = self.repo_details.repo_config.clone() else {
|
||||
logger(self.log.as_ref(), "no repo config");
|
||||
self.alert_tui("no repo config");
|
||||
return;
|
||||
};
|
||||
logger(self.log.as_ref(), "have repo config");
|
||||
|
@ -75,10 +82,11 @@ impl Handler<ValidateRepo> for RepoActor {
|
|||
self.log.as_ref(),
|
||||
);
|
||||
} else {
|
||||
// do nothing
|
||||
self.update_tui(RepoUpdate::Okay);
|
||||
}
|
||||
}
|
||||
Err(Error::Retryable(message)) => {
|
||||
self.alert_tui(format!("[retryable: {message}]"));
|
||||
logger(self.log.as_ref(), message);
|
||||
let addr = ctx.address();
|
||||
let message_token = self.message_token;
|
||||
|
@ -95,12 +103,16 @@ impl Handler<ValidateRepo> for RepoActor {
|
|||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
}
|
||||
Err(Error::UserIntervention(user_notification)) => notify_user(
|
||||
self.notify_user_recipient.as_ref(),
|
||||
user_notification,
|
||||
self.log.as_ref(),
|
||||
),
|
||||
Err(Error::UserIntervention(user_notification)) => {
|
||||
self.alert_tui(format!("[USER INTERVENTION: {user_notification}]"));
|
||||
notify_user(
|
||||
self.notify_user_recipient.as_ref(),
|
||||
user_notification,
|
||||
self.log.as_ref(),
|
||||
);
|
||||
}
|
||||
Err(Error::NonRetryable(message)) => {
|
||||
self.alert_tui(format!("[Error: {message}]"));
|
||||
logger(self.log.as_ref(), message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,13 @@ use actix::prelude::*;
|
|||
|
||||
use tracing::{info, instrument, warn};
|
||||
|
||||
use crate::repo::{
|
||||
do_send, logger,
|
||||
messages::{ValidateRepo, WebhookNotification},
|
||||
ActorLog, RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
do_send, logger,
|
||||
messages::{ValidateRepo, WebhookNotification},
|
||||
ActorLog, RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
use git_next_core::{
|
||||
|
@ -52,6 +55,10 @@ impl Handler<WebhookNotification> for RepoActor {
|
|||
return;
|
||||
}
|
||||
Some(Branch::Main) => {
|
||||
self.update_tui(RepoUpdate::WebhookReceived {
|
||||
branch: Branch::Main,
|
||||
push: push.clone(),
|
||||
});
|
||||
if handle_push(
|
||||
push,
|
||||
&config.branches().main(),
|
||||
|
@ -64,6 +71,10 @@ impl Handler<WebhookNotification> for RepoActor {
|
|||
};
|
||||
}
|
||||
Some(Branch::Next) => {
|
||||
self.update_tui(RepoUpdate::WebhookReceived {
|
||||
branch: Branch::Next,
|
||||
push: push.clone(),
|
||||
});
|
||||
if handle_push(
|
||||
push,
|
||||
&config.branches().next(),
|
||||
|
@ -76,6 +87,10 @@ impl Handler<WebhookNotification> for RepoActor {
|
|||
};
|
||||
}
|
||||
Some(Branch::Dev) => {
|
||||
self.update_tui(RepoUpdate::WebhookReceived {
|
||||
branch: Branch::Dev,
|
||||
push: push.clone(),
|
||||
});
|
||||
if handle_push(
|
||||
push,
|
||||
&config.branches().dev(),
|
||||
|
@ -135,7 +150,7 @@ fn handle_push(
|
|||
last_commit: &mut Option<Commit>,
|
||||
log: Option<&ActorLog>,
|
||||
) -> Result<(), ()> {
|
||||
logger(log, "message is for dev branch");
|
||||
logger(log, format!("message is for {branch} branch"));
|
||||
let commit = Commit::from(push);
|
||||
if last_commit.as_ref() == Some(&commit) {
|
||||
logger(log, format!("not a new commit on {branch}"));
|
||||
|
|
|
@ -2,16 +2,20 @@
|
|||
use actix::prelude::*;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::repo::{
|
||||
do_send,
|
||||
messages::{ValidateRepo, WebhookRegistered},
|
||||
RepoActor,
|
||||
use crate::{
|
||||
repo::{
|
||||
do_send,
|
||||
messages::{ValidateRepo, WebhookRegistered},
|
||||
RepoActor,
|
||||
},
|
||||
server::actor::messages::RepoUpdate,
|
||||
};
|
||||
|
||||
impl Handler<WebhookRegistered> for RepoActor {
|
||||
type Result = ();
|
||||
#[instrument(name = "RepoActor::WebhookRegistered", skip_all, fields(repo = %self.repo_details, webhook_id = %msg.webhook_id()))]
|
||||
fn handle(&mut self, msg: WebhookRegistered, ctx: &mut Self::Context) -> Self::Result {
|
||||
self.update_tui(RepoUpdate::RegisteredWebhook);
|
||||
self.webhook_id.replace(msg.webhook_id().clone());
|
||||
self.webhook_auth.replace(msg.webhook_auth().clone());
|
||||
do_send(
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
//
|
||||
use actix::prelude::*;
|
||||
|
||||
use crate::alerts::messages::NotifyUser;
|
||||
use crate::{
|
||||
alerts::messages::NotifyUser,
|
||||
server::{
|
||||
actor::messages::{RepoUpdate, ServerUpdate},
|
||||
ServerActor,
|
||||
},
|
||||
};
|
||||
use derive_more::Deref;
|
||||
use kxio::network::Network;
|
||||
use std::time::Duration;
|
||||
|
@ -11,6 +17,7 @@ use git_next_core::{
|
|||
git::{
|
||||
self,
|
||||
repository::{factory::RepositoryFactory, open::OpenRepositoryLike},
|
||||
validation::positions::get_commit_histories,
|
||||
UserNotification,
|
||||
},
|
||||
server::ListenUrl,
|
||||
|
@ -59,6 +66,7 @@ pub struct RepoActor {
|
|||
forge: Box<dyn git::ForgeLike>,
|
||||
log: Option<ActorLog>,
|
||||
notify_user_recipient: Option<Recipient<NotifyUser>>,
|
||||
server_addr: Option<Addr<ServerActor>>,
|
||||
}
|
||||
impl RepoActor {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -71,6 +79,7 @@ impl RepoActor {
|
|||
repository_factory: Box<dyn RepositoryFactory>,
|
||||
sleep_duration: std::time::Duration,
|
||||
notify_user_recipient: Option<Recipient<NotifyUser>>,
|
||||
server_addr: Option<Addr<ServerActor>>,
|
||||
) -> Self {
|
||||
let message_token = messages::MessageToken::default();
|
||||
Self {
|
||||
|
@ -90,6 +99,58 @@ impl RepoActor {
|
|||
sleep_duration,
|
||||
log: None,
|
||||
notify_user_recipient,
|
||||
server_addr,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_tui_branches(&self) {
|
||||
#[cfg(feature = "tui")]
|
||||
{
|
||||
use crate::server::actor::messages::RepoUpdate;
|
||||
let Some(repo_config) = &self.repo_details.repo_config else {
|
||||
return;
|
||||
};
|
||||
let Some(open_repo) = self.open_repository.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let branches = repo_config.branches().clone();
|
||||
let histories = get_commit_histories(&**open_repo, repo_config).ok();
|
||||
self.update_tui(RepoUpdate::Branches {
|
||||
branches,
|
||||
histories,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn update_tui_log(&self, log: git::graph::Log) {
|
||||
#[cfg(feature = "tui")]
|
||||
{
|
||||
self.update_tui(RepoUpdate::Log { log });
|
||||
}
|
||||
}
|
||||
|
||||
fn alert_tui(&self, alert: impl Into<String>) {
|
||||
#[cfg(feature = "tui")]
|
||||
{
|
||||
self.update_tui(RepoUpdate::Alert {
|
||||
alert: alert.into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn update_tui(&self, repo_update: RepoUpdate) {
|
||||
#[cfg(feature = "tui")]
|
||||
{
|
||||
let Some(server_addr) = &self.server_addr else {
|
||||
return;
|
||||
};
|
||||
|
||||
let update = ServerUpdate::RepoUpdate {
|
||||
forge_alias: self.repo_details.forge.forge_alias().clone(),
|
||||
repo_alias: self.repo_details.repo_alias.clone(),
|
||||
repo_update,
|
||||
};
|
||||
server_addr.do_send(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,28 @@
|
|||
//
|
||||
use crate::repo::branch::find_next_commit_on_dev;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn advance_next_sut(
|
||||
next: &Commit,
|
||||
main: &Commit,
|
||||
dev_commit_history: &[Commit],
|
||||
repo_details: RepoDetails,
|
||||
repo_config: RepoConfig,
|
||||
open_repository: &dyn OpenRepositoryLike,
|
||||
message_token: MessageToken,
|
||||
) -> branch::Result<MessageToken> {
|
||||
let (commit, force) = find_next_commit_on_dev(next, main, dev_commit_history);
|
||||
branch::advance_next(
|
||||
commit,
|
||||
force,
|
||||
repo_details,
|
||||
repo_config,
|
||||
open_repository,
|
||||
message_token,
|
||||
)
|
||||
}
|
||||
|
||||
mod when_at_dev {
|
||||
// next and dev branches are the same
|
||||
use super::*;
|
||||
|
@ -16,7 +38,7 @@ mod when_at_dev {
|
|||
// no on_push defined - so any call to push will cause an error
|
||||
let message_token = given::a_message_token();
|
||||
let_assert!(
|
||||
Err(err) = branch::advance_next(
|
||||
Err(err) = advance_next_sut(
|
||||
&next,
|
||||
main,
|
||||
dev_commit_history,
|
||||
|
@ -51,7 +73,7 @@ mod can_advance {
|
|||
// no on_push defined - so any call to push will cause an error
|
||||
let message_token = given::a_message_token();
|
||||
let_assert!(
|
||||
Err(err) = branch::advance_next(
|
||||
Err(err) = advance_next_sut(
|
||||
&next,
|
||||
main,
|
||||
dev_commit_history,
|
||||
|
@ -82,7 +104,7 @@ mod can_advance {
|
|||
// no on_push defined - so any call to push will cause an error
|
||||
let message_token = given::a_message_token();
|
||||
let_assert!(
|
||||
Err(err) = branch::advance_next(
|
||||
Err(err) = advance_next_sut(
|
||||
&next,
|
||||
main,
|
||||
dev_commit_history,
|
||||
|
@ -122,7 +144,7 @@ mod can_advance {
|
|||
expect::push(&mut open_repository, Err(git::push::Error::Lock));
|
||||
let message_token = given::a_message_token();
|
||||
let_assert!(
|
||||
Err(err) = branch::advance_next(
|
||||
Err(err) = advance_next_sut(
|
||||
&next,
|
||||
main,
|
||||
dev_commit_history,
|
||||
|
@ -154,7 +176,7 @@ mod can_advance {
|
|||
expect::push_ok(&mut open_repository);
|
||||
let message_token = given::a_message_token();
|
||||
let_assert!(
|
||||
Ok(mt) = branch::advance_next(
|
||||
Ok(mt) = advance_next_sut(
|
||||
&next,
|
||||
main,
|
||||
dev_commit_history,
|
||||
|
|
|
@ -195,6 +195,7 @@ pub fn a_repo_actor(
|
|||
repository_factory,
|
||||
std::time::Duration::from_nanos(1),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.with_log(actors_log),
|
||||
log,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod file_updated;
|
||||
mod receive_app_config;
|
||||
mod receive_valid_app_config;
|
||||
mod server_update;
|
||||
mod shutdown;
|
||||
mod subscribe_updates;
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
alerts::messages::UpdateShout,
|
||||
repo::{messages::CloneRepo, RepoActor},
|
||||
server::actor::{
|
||||
messages::{ReceiveValidAppConfig, ValidAppConfig},
|
||||
messages::{ReceiveValidAppConfig, ServerUpdate, ValidAppConfig},
|
||||
ServerActor,
|
||||
},
|
||||
webhook::{
|
||||
|
@ -21,7 +21,7 @@ use crate::{
|
|||
impl Handler<ReceiveValidAppConfig> for ServerActor {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: ReceiveValidAppConfig, _ctx: &mut Self::Context) -> Self::Result {
|
||||
fn handle(&mut self, msg: ReceiveValidAppConfig, ctx: &mut Self::Context) -> Self::Result {
|
||||
let ValidAppConfig {
|
||||
app_config,
|
||||
socket_address,
|
||||
|
@ -37,6 +37,7 @@ impl Handler<ReceiveValidAppConfig> for ServerActor {
|
|||
let webhook_router = WebhookRouterActor::default().start();
|
||||
let listen_url = app_config.listen().url();
|
||||
let notify_user_recipient = self.alerts.clone().recipient();
|
||||
let server_addr = Some(ctx.address());
|
||||
// Forge Actors
|
||||
for (forge_alias, forge_config) in app_config.forges() {
|
||||
let repo_actors = self
|
||||
|
@ -46,6 +47,7 @@ impl Handler<ReceiveValidAppConfig> for ServerActor {
|
|||
&server_storage,
|
||||
listen_url,
|
||||
¬ify_user_recipient,
|
||||
server_addr.clone(),
|
||||
)
|
||||
.into_iter()
|
||||
.map(start_repo_actor)
|
||||
|
@ -69,9 +71,18 @@ impl Handler<ReceiveValidAppConfig> for ServerActor {
|
|||
WebhookActor::new(socket_address, webhook_router.recipient()).start();
|
||||
self.webhook_actor_addr.replace(webhook_actor_addr);
|
||||
let shout = app_config.shout().clone();
|
||||
self.app_config.replace(app_config);
|
||||
self.app_config.replace(app_config.clone());
|
||||
self.do_send(
|
||||
ServerUpdate::AppConfigLoaded {
|
||||
app_config: ValidAppConfig {
|
||||
app_config,
|
||||
socket_address,
|
||||
storage: server_storage,
|
||||
},
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
self.alerts.do_send(UpdateShout::new(shout));
|
||||
self.send_server_updates();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
crates/cli/src/server/actor/handlers/server_update.rs
Normal file
14
crates/cli/src/server/actor/handlers/server_update.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use actix::Handler;
|
||||
|
||||
//
|
||||
use crate::server::{actor::messages::ServerUpdate, ServerActor};
|
||||
|
||||
impl Handler<ServerUpdate> for ServerActor {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: ServerUpdate, _ctx: &mut Self::Context) -> Self::Result {
|
||||
self.subscribers.iter().for_each(move |subscriber| {
|
||||
subscriber.do_send(msg.clone());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
//
|
||||
use actix::{Message, Recipient};
|
||||
//-
|
||||
use derive_more::Constructor;
|
||||
|
||||
use git_next_core::{
|
||||
git::graph::Log,
|
||||
git::{self, forge::commit::Status, graph::Log},
|
||||
message,
|
||||
server::{AppConfig, Storage},
|
||||
ForgeAlias, RepoAlias, RepoBranches,
|
||||
webhook::{push::Branch, Push},
|
||||
ForgeAlias, RepoAlias, RepoBranches, RepoConfig,
|
||||
};
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
@ -40,20 +41,54 @@ message!(Shutdown, "Notification to shutdown the server actor");
|
|||
#[derive(Clone, Debug, PartialEq, Eq, Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub enum ServerUpdate {
|
||||
/// Status of a repo
|
||||
UpdateRepoSummary {
|
||||
/// List of all configured forges and aliases
|
||||
AppConfigLoaded { app_config: ValidAppConfig },
|
||||
|
||||
RepoUpdate {
|
||||
forge_alias: ForgeAlias,
|
||||
repo_alias: RepoAlias,
|
||||
repo_update: RepoUpdate,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RepoUpdate {
|
||||
Branches {
|
||||
branches: RepoBranches,
|
||||
histories: Option<git::commit::Histories>,
|
||||
},
|
||||
Log {
|
||||
log: Log,
|
||||
},
|
||||
/// remove a repo
|
||||
RemoveRepo {
|
||||
forge_alias: ForgeAlias,
|
||||
repo_alias: RepoAlias,
|
||||
ValidateRepo,
|
||||
Okay,
|
||||
Alert {
|
||||
alert: String,
|
||||
},
|
||||
/// test message
|
||||
Ping,
|
||||
CheckingCI,
|
||||
AdvancingNext {
|
||||
commit: Option<git::Commit>,
|
||||
force: git::push::Force,
|
||||
},
|
||||
AdvancingMain {
|
||||
commit: git::Commit,
|
||||
},
|
||||
Opening,
|
||||
LoadingConfigFromRepo,
|
||||
ReceiveCIStatus {
|
||||
status: Status,
|
||||
},
|
||||
ReceiveRepoConfig {
|
||||
repo_config: RepoConfig,
|
||||
},
|
||||
RegisteringWebhook,
|
||||
UnregisteringWebhook,
|
||||
WebhookReceived {
|
||||
branch: Branch,
|
||||
push: Push,
|
||||
},
|
||||
RegisteredWebhook,
|
||||
Opened,
|
||||
}
|
||||
|
||||
message!(
|
||||
|
|
|
@ -118,6 +118,7 @@ impl ServerActor {
|
|||
server_storage: &Storage,
|
||||
listen_url: &ListenUrl,
|
||||
notify_user_recipient: &Recipient<NotifyUser>,
|
||||
server_addr: Option<Addr<Self>>,
|
||||
) -> Vec<(ForgeAlias, RepoAlias, RepoActor)> {
|
||||
let span =
|
||||
tracing::info_span!("create_forge_repos", name = %forge_name, config = %forge_config);
|
||||
|
@ -125,8 +126,13 @@ impl ServerActor {
|
|||
let _guard = span.enter();
|
||||
tracing::info!("Creating Forge");
|
||||
let mut repos = vec![];
|
||||
let creator =
|
||||
self.create_actor(forge_name, forge_config.clone(), server_storage, listen_url);
|
||||
let creator = self.create_actor(
|
||||
forge_name,
|
||||
forge_config.clone(),
|
||||
server_storage,
|
||||
listen_url,
|
||||
server_addr,
|
||||
);
|
||||
for (repo_alias, server_repo_config) in forge_config.repos() {
|
||||
let forge_repo = creator((
|
||||
repo_alias,
|
||||
|
@ -148,6 +154,7 @@ impl ServerActor {
|
|||
forge_config: ForgeConfig,
|
||||
server_storage: &Storage,
|
||||
listen_url: &ListenUrl,
|
||||
server_addr: Option<Addr<Self>>,
|
||||
) -> impl Fn(
|
||||
(RepoAlias, &ServerRepoConfig, Recipient<NotifyUser>),
|
||||
) -> (ForgeAlias, RepoAlias, RepoActor) {
|
||||
|
@ -194,6 +201,7 @@ impl ServerActor {
|
|||
repository_factory.duplicate(),
|
||||
sleep_duration,
|
||||
Some(notify_user_recipient),
|
||||
server_addr.clone(),
|
||||
);
|
||||
(forge_name.clone(), repo_alias, actor)
|
||||
}
|
||||
|
@ -242,10 +250,4 @@ impl ServerActor {
|
|||
ctx.address().do_send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
fn send_server_updates(&self) {
|
||||
self.subscribers.iter().for_each(|subscriber| {
|
||||
subscriber.do_send(ServerUpdate::Ping);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ pub use actor::ServerActor;
|
|||
|
||||
use git_next_core::git::RepositoryFactory;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use color_eyre::{eyre::Context, Result};
|
||||
use kxio::{fs::FileSystem, network::Network};
|
||||
use tracing::info;
|
||||
|
||||
|
@ -48,7 +48,12 @@ pub fn start(
|
|||
repo: Box<dyn RepositoryFactory>,
|
||||
sleep_duration: std::time::Duration,
|
||||
) -> Result<()> {
|
||||
if !ui {
|
||||
if ui {
|
||||
#[cfg(feature = "tui")]
|
||||
{
|
||||
crate::tui::logging::initialize_logging()?;
|
||||
}
|
||||
} else {
|
||||
init_logging();
|
||||
}
|
||||
|
||||
|
@ -59,7 +64,6 @@ pub fn start(
|
|||
info!("Starting Server...");
|
||||
let server =
|
||||
ServerActor::new(fs.clone(), net.clone(), alerts_addr, repo, sleep_duration).start();
|
||||
server.do_send(FileUpdated);
|
||||
|
||||
info!("Starting File Watcher...");
|
||||
#[allow(clippy::expect_used)]
|
||||
|
@ -75,18 +79,18 @@ pub fn start(
|
|||
|
||||
let (tx_shutdown, rx_shutdown) = channel::<()>();
|
||||
let tui_addr = tui::Tui::new(tx_shutdown).start();
|
||||
// tui_addr.do_send(tui::Tick);
|
||||
let _ = tui_addr.send(tui::Tick).await;
|
||||
server.do_send(SubscribeToUpdates::new(tui_addr.clone().recipient()));
|
||||
server.do_send(FileUpdated); // update file after ui subscription in place
|
||||
loop {
|
||||
let _ = tui_addr.send(tui::Tick).await;
|
||||
if rx_shutdown.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
// actix_rt::task::yield_now().await;
|
||||
actix_rt::time::sleep(Duration::from_millis(16)).await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
server.do_send(FileUpdated);
|
||||
info!("Server running - Press Ctrl-C to stop...");
|
||||
let _ = signal::ctrl_c().await;
|
||||
info!("Ctrl-C received, shutting down...");
|
||||
|
@ -95,7 +99,7 @@ pub fn start(
|
|||
// shutdown
|
||||
fw_shutdown.store(true, Ordering::Relaxed);
|
||||
server.do_send(crate::server::actor::messages::Shutdown);
|
||||
actix_rt::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
actix_rt::time::sleep(std::time::Duration::from_millis(10)).await;
|
||||
System::current().stop();
|
||||
};
|
||||
|
||||
|
|
121
crates/cli/src/tui/README.md
Normal file
121
crates/cli/src/tui/README.md
Normal file
|
@ -0,0 +1,121 @@
|
|||
# TUI Actor
|
||||
|
||||
- Maintains it's own copy of data for holding state
|
||||
- Is notified of update via actor messages from the Server and Repo Actors
|
||||
- State is somewhat heirarchical
|
||||
|
||||
## State
|
||||
|
||||
```rust
|
||||
enum TuiState {
|
||||
Initial,
|
||||
Configured {
|
||||
app_config: ValidAppConfig,
|
||||
forges: BTreeMap<ForgeAlias, RepoState>,
|
||||
},
|
||||
}
|
||||
enum RepoState {
|
||||
Identified { repo_alias: RepoAlias },
|
||||
Configured { repo_alias: RepoAlias, branches: RepoBranches },
|
||||
Ready { repo_alias: RepoAlias, branches: RepoBranches, main: Commit, next: Commit, dev: Commit, log: Log },
|
||||
Alert { repo_alias: RepoAlias, message: String, branches: RepoBranches, main: Commit, next: Commit, dev: Commit, log: Log },
|
||||
}
|
||||
```
|
||||
|
||||
## State Transitions:
|
||||
|
||||
### `TuiState`
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
* --> Initial
|
||||
Initial --> Configured
|
||||
```
|
||||
|
||||
- `message!(Configure, ValidAppConfig, "Initialise UI with valid config");`
|
||||
|
||||
### `RepoState`
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
* --> Identified
|
||||
Identified --> Configured
|
||||
Identified --> Ready
|
||||
Configured --> Ready
|
||||
Ready --> Alert
|
||||
Configured --> Alert
|
||||
Identified --> Alert
|
||||
Alert --> Ready
|
||||
```
|
||||
|
||||
- `Identified` - from AppConfig where a repo alias is listed, but repo config needs to be loaded from `.git-next.toml`
|
||||
- `Configured` - as `Identified` but either branches are identified in server config, OR the `.git-next.toml` file has been loaded
|
||||
- `Ready` - as `Configured` but the branch positions have been validated and do not require user intervention
|
||||
- `Alert` - as `Ready` but user intervention is required
|
||||
|
||||
## Widget
|
||||
|
||||
Initial mock up of UI. Possibly add some borders and padding as it looks a little squached together.
|
||||
|
||||
```
|
||||
+ gh
|
||||
- jo
|
||||
+ test (main/next/dev)
|
||||
- tasyn (main/next/dev)
|
||||
* 12ab32f (dev) added feature X
|
||||
* bce43b1 (next) added feature Y
|
||||
* f43e379 (main) added feature Z
|
||||
- git-next (main/next/dev) DEV NOT AHEAD OF MAIN - rebase onto main
|
||||
* 239cefd (main/next) fix bug A
|
||||
* b4c290a (dev)
|
||||
```
|
||||
|
||||
Adding a border around open forges:
|
||||
|
||||
```
|
||||
+ gh
|
||||
- jo --------------------------------------------------------------------+
|
||||
| + test (main/next/dev) |
|
||||
| - tasyn (main/next/dev) |
|
||||
| * 12ab32f (dev) added feature X |
|
||||
| * bce43b1 (next) added feature Y |
|
||||
| * f43e379 (main) added feature Z |
|
||||
| - git-next (main/next/dev) DEV NOT AHEAD OF MAIN - rebase onto main |
|
||||
| * 239cefd (main/next) fix bug A |
|
||||
| * b4c290a (dev) |
|
||||
+------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
Adding a border around open forges and repos (I like this one the best):
|
||||
|
||||
```
|
||||
+ gh
|
||||
- jo --------------------------------------------------------------------+
|
||||
| + test (main/next/dev) |
|
||||
| - tasyn (main/next/dev) ---------------------------------------------+ |
|
||||
| | * 12ab32f (dev) added feature X | |
|
||||
| | * bce43b1 (next) added feature Y | |
|
||||
| | * f43e379 (main) added feature Z | |
|
||||
| +--------------------------------------------------------------------+ |
|
||||
| - git-next (main/next/dev) DEV NOT AHEAD OF MAIN - rebase onto main -+ |
|
||||
| | * 239cefd (main/next) fix bug A | |
|
||||
| | * b4c290a (dev) | |
|
||||
| +--------------------------------------------------------------------+ |
|
||||
+------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
- tui-logger to create an optional panel to show the normal server logs
|
||||
|
||||
## Branch Graph
|
||||
|
||||
- tui-nodes ?
|
||||
|
||||
## Scrolling
|
||||
|
||||
- tui-scrollview
|
||||
|
||||
## Tree View
|
||||
|
||||
- tui-tree-widget
|
|
@ -1,27 +1,90 @@
|
|||
use std::time::Instant;
|
||||
|
||||
//
|
||||
use actix::Handler;
|
||||
|
||||
use crate::{server::actor::messages::ServerUpdate, tui::Tui};
|
||||
use crate::{
|
||||
server::actor::messages::{RepoUpdate, ServerUpdate},
|
||||
tui::{actor::ServerState, Tui},
|
||||
};
|
||||
|
||||
//
|
||||
impl Handler<ServerUpdate> for Tui {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: ServerUpdate, _ctx: &mut Self::Context) -> Self::Result {
|
||||
self.state.tap();
|
||||
match msg {
|
||||
ServerUpdate::UpdateRepoSummary {
|
||||
ServerUpdate::AppConfigLoaded { app_config } => {
|
||||
self.state.mode = ServerState::from(app_config);
|
||||
}
|
||||
|
||||
ServerUpdate::RepoUpdate {
|
||||
forge_alias,
|
||||
repo_alias,
|
||||
branches,
|
||||
log,
|
||||
} => todo!(),
|
||||
ServerUpdate::RemoveRepo {
|
||||
forge_alias,
|
||||
repo_alias,
|
||||
} => todo!(),
|
||||
ServerUpdate::Ping => {
|
||||
self.last_ping = Instant::now();
|
||||
repo_update,
|
||||
} => {
|
||||
if let ServerState::Configured { forges } = &mut self.state.mode {
|
||||
let Some(forge_state) = forges.get_mut(&forge_alias) else {
|
||||
return;
|
||||
};
|
||||
let Some(repo_state) = forge_state.repos.get_mut(&repo_alias) else {
|
||||
return;
|
||||
};
|
||||
match repo_update {
|
||||
RepoUpdate::Branches {
|
||||
branches,
|
||||
histories,
|
||||
} => {
|
||||
repo_state.update_branches(branches, histories);
|
||||
}
|
||||
RepoUpdate::Log { log } => {
|
||||
repo_state.update_log(log);
|
||||
}
|
||||
RepoUpdate::ValidateRepo => repo_state.update_message("polling..."),
|
||||
RepoUpdate::Okay => {
|
||||
repo_state.update_message("okay");
|
||||
repo_state.clear_alert();
|
||||
}
|
||||
RepoUpdate::Alert { alert } => {
|
||||
repo_state.alert(alert);
|
||||
}
|
||||
RepoUpdate::CheckingCI => {
|
||||
repo_state.update_message("Checking CI status");
|
||||
}
|
||||
RepoUpdate::AdvancingNext {
|
||||
commit: _,
|
||||
force: _,
|
||||
} => (),
|
||||
RepoUpdate::AdvancingMain { commit } => {
|
||||
repo_state.update_message(format!("advancing main to {commit}"));
|
||||
}
|
||||
RepoUpdate::Opening => {
|
||||
repo_state.update_message("opening...");
|
||||
}
|
||||
RepoUpdate::Opened => {
|
||||
repo_state.update_message("opened");
|
||||
}
|
||||
RepoUpdate::LoadingConfigFromRepo => {
|
||||
repo_state.update_message("loading config from repo...");
|
||||
}
|
||||
RepoUpdate::ReceiveCIStatus { status } => {
|
||||
repo_state.update_message(format!("ci status: {status:?}"));
|
||||
}
|
||||
RepoUpdate::ReceiveRepoConfig { repo_config: _ } => {
|
||||
repo_state.update_message("loaded config from repo");
|
||||
}
|
||||
RepoUpdate::RegisteringWebhook => {
|
||||
repo_state.update_message("registering webhook...");
|
||||
}
|
||||
RepoUpdate::UnregisteringWebhook => {
|
||||
repo_state.update_message("unregistering webhook...");
|
||||
}
|
||||
RepoUpdate::WebhookReceived { branch, push: _ } => {
|
||||
repo_state.update_message(format!("webhook update: {branch:?}"));
|
||||
}
|
||||
RepoUpdate::RegisteredWebhook => {
|
||||
repo_state.update_message("registered webhook");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
//
|
||||
use std::{borrow::BorrowMut, time::Instant};
|
||||
|
||||
use actix::{ActorContext, Handler};
|
||||
use ratatui::{
|
||||
crossterm::event::{self, KeyCode, KeyEventKind},
|
||||
style::Stylize as _,
|
||||
widgets::Paragraph,
|
||||
};
|
||||
use actix::Handler;
|
||||
|
||||
use crate::tui::actor::{messages::Tick, Tui};
|
||||
|
||||
|
@ -14,44 +7,9 @@ impl Handler<Tick> for Tui {
|
|||
type Result = std::io::Result<()>;
|
||||
|
||||
fn handle(&mut self, _msg: Tick, ctx: &mut Self::Context) -> Self::Result {
|
||||
if let Some(terminal) = self.terminal.borrow_mut() {
|
||||
terminal.draw(|frame| {
|
||||
let area = frame.area();
|
||||
frame.render_widget(
|
||||
Paragraph::new(format!(
|
||||
"(press 'q' to quit) Ping:[{:?}] UI:[{:?}]",
|
||||
self.last_ping,
|
||||
Instant::now()
|
||||
))
|
||||
.white()
|
||||
.on_blue(),
|
||||
area,
|
||||
);
|
||||
})?;
|
||||
} else {
|
||||
eprintln!("No terminal setup");
|
||||
}
|
||||
if event::poll(std::time::Duration::from_millis(16))? {
|
||||
if let event::Event::Key(key) = event::read()? {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => {
|
||||
// execute!(stderr(), LeaveAlternateScreen)?;
|
||||
// disable_raw_mode()?;
|
||||
ctx.stop();
|
||||
if let Err(err) = self.signal_shutdown.send(()) {
|
||||
tracing::error!(?err, "Failed to signal shutdown");
|
||||
}
|
||||
}
|
||||
KeyCode::Esc => {
|
||||
//
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.state.tap();
|
||||
self.draw()?;
|
||||
self.handle_input(ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//
|
||||
use git_next_core::message;
|
||||
|
||||
message!(Tick => std::io::Result<()>, "Start the TUI");
|
||||
message!(Tick => std::io::Result<()>, "Update the TUI");
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
//
|
||||
mod handlers;
|
||||
pub mod messages;
|
||||
mod model;
|
||||
|
||||
use std::{
|
||||
io::{stderr, Stderr},
|
||||
sync::mpsc::Sender,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use actix::{Actor, Context};
|
||||
use actix::{Actor, ActorContext as _, Context};
|
||||
|
||||
pub use model::*;
|
||||
|
||||
use ratatui::{
|
||||
crossterm::{
|
||||
event::{self, KeyCode, KeyEventKind},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
},
|
||||
|
@ -23,7 +26,7 @@ use ratatui::{
|
|||
pub struct Tui {
|
||||
terminal: Option<Terminal<CrosstermBackend<Stderr>>>,
|
||||
signal_shutdown: Sender<()>,
|
||||
last_ping: Instant,
|
||||
pub state: State,
|
||||
}
|
||||
impl Actor for Tui {
|
||||
type Context = Context<Self>;
|
||||
|
@ -58,9 +61,48 @@ impl Tui {
|
|||
Self {
|
||||
terminal: None,
|
||||
signal_shutdown,
|
||||
last_ping: Instant::now(),
|
||||
state: State::initial(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn state(&self) -> &State {
|
||||
&self.state
|
||||
}
|
||||
|
||||
fn draw(&mut self) -> std::io::Result<()> {
|
||||
let t = self.terminal.take();
|
||||
if let Some(mut terminal) = t {
|
||||
terminal.draw(|frame| {
|
||||
frame.render_widget(self.state(), frame.area());
|
||||
})?;
|
||||
self.terminal = Some(terminal);
|
||||
} else {
|
||||
eprintln!("No terminal setup");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_input(&self, ctx: &mut <Self as actix::Actor>::Context) -> std::io::Result<()> {
|
||||
if event::poll(std::time::Duration::from_millis(16))? {
|
||||
if let event::Event::Key(key) = event::read()? {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => {
|
||||
ctx.stop();
|
||||
if let Err(err) = self.signal_shutdown.send(()) {
|
||||
tracing::error!(?err, "Failed to signal shutdown");
|
||||
}
|
||||
}
|
||||
KeyCode::Esc => {
|
||||
//
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn init() -> std::io::Result<Terminal<CrosstermBackend<Stderr>>> {
|
||||
|
|
315
crates/cli/src/tui/actor/model.rs
Normal file
315
crates/cli/src/tui/actor/model.rs
Normal file
|
@ -0,0 +1,315 @@
|
|||
//
|
||||
use ratatui::{
|
||||
layout::Alignment,
|
||||
prelude::{Buffer, Rect},
|
||||
style::Stylize as _,
|
||||
symbols::border,
|
||||
text::Line,
|
||||
widgets::{block::Title, Block, Paragraph, Widget},
|
||||
};
|
||||
|
||||
use git_next_core::{
|
||||
git::{self, graph::Log, Commit},
|
||||
ForgeAlias, RepoAlias, RepoBranches,
|
||||
};
|
||||
|
||||
use std::{collections::BTreeMap, fmt::Display, time::Instant};
|
||||
|
||||
use crate::{server::actor::messages::ValidAppConfig, tui::components::ConfiguredAppWidget};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct State {
|
||||
last_update: Instant,
|
||||
started: Instant,
|
||||
pub mode: ServerState,
|
||||
}
|
||||
impl State {
|
||||
pub fn initial() -> Self {
|
||||
Self {
|
||||
last_update: Instant::now(),
|
||||
started: Instant::now(),
|
||||
mode: ServerState::Initial { tick: 0 },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tap(&mut self) {
|
||||
self.last_update = Instant::now();
|
||||
if let ServerState::Initial { tick } = &mut self.mode {
|
||||
*tick += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ServerState {
|
||||
/// UI has started but has no information on the state of the server
|
||||
Initial { tick: usize }, // NOTE: for use with throbber-widgets-tui ?
|
||||
|
||||
/// The application configuration has been loaded, individual forges and repos have their own
|
||||
/// states
|
||||
Configured {
|
||||
forges: BTreeMap<ForgeAlias, ForgeState>,
|
||||
},
|
||||
}
|
||||
impl ServerState {
|
||||
pub fn update_branches(
|
||||
&mut self,
|
||||
forge_alias: &ForgeAlias,
|
||||
repo_alias: &RepoAlias,
|
||||
branches: RepoBranches,
|
||||
) {
|
||||
if let Self::Configured { forges } = self {
|
||||
let Some(forge_state) = forges.get_mut(forge_alias) else {
|
||||
return;
|
||||
};
|
||||
let Some(repo_state) = forge_state.repos.get_mut(repo_alias) else {
|
||||
return;
|
||||
};
|
||||
match repo_state {
|
||||
RepoState::Configured {
|
||||
branches: state_branches,
|
||||
..
|
||||
}
|
||||
| RepoState::Ready {
|
||||
branches: state_branches,
|
||||
..
|
||||
} => *state_branches = branches,
|
||||
|
||||
RepoState::Identified { .. } => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_log(&mut self, forge_alias: &ForgeAlias, repo_alias: &RepoAlias, log: Log) {
|
||||
if let Self::Configured { forges } = self {
|
||||
let Some(forge_state) = forges.get_mut(forge_alias) else {
|
||||
return;
|
||||
};
|
||||
let Some(repo_state) = forge_state.repos.get_mut(repo_alias) else {
|
||||
return;
|
||||
};
|
||||
match repo_state {
|
||||
RepoState::Ready { log: state_log, .. } => *state_log = log,
|
||||
|
||||
RepoState::Identified { .. } | RepoState::Configured { .. } => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<ValidAppConfig> for ServerState {
|
||||
fn from(app_config: ValidAppConfig) -> Self {
|
||||
Self::Configured {
|
||||
forges: app_config
|
||||
.app_config
|
||||
.forges()
|
||||
.map(|(forge_alias, config)| {
|
||||
(
|
||||
forge_alias,
|
||||
config
|
||||
.repos()
|
||||
.map(|(repo_alias, server_repo_config)| {
|
||||
(repo_alias, server_repo_config.repo_config())
|
||||
})
|
||||
.map(
|
||||
|(repo_alias, option_repo_config)| match option_repo_config {
|
||||
Some(rc) => (
|
||||
repo_alias.clone(),
|
||||
RepoState::Configured {
|
||||
repo_alias,
|
||||
message: "configured".into(),
|
||||
messages: Vec::new(),
|
||||
alert: None,
|
||||
branches: rc.branches().clone(),
|
||||
histories: None,
|
||||
},
|
||||
),
|
||||
None => (
|
||||
repo_alias.clone(),
|
||||
RepoState::Identified {
|
||||
repo_alias,
|
||||
message: "identified".into(),
|
||||
messages: Vec::new(),
|
||||
alert: None,
|
||||
},
|
||||
),
|
||||
},
|
||||
)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.map(|(forge_alias, vec_repo_alias_state)| {
|
||||
let forge_state: ForgeState = ForgeState {
|
||||
alias: forge_alias.clone(),
|
||||
view_state: ViewState::default(),
|
||||
repos: vec_repo_alias_state.into_iter().collect::<BTreeMap<_, _>>(),
|
||||
};
|
||||
(forge_alias, forge_state)
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ViewState {
|
||||
Collapsed,
|
||||
#[default]
|
||||
Expanded,
|
||||
}
|
||||
impl Display for ViewState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let view_state = match self {
|
||||
Self::Collapsed => "+",
|
||||
Self::Expanded => "-",
|
||||
};
|
||||
write!(f, "{view_state}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ForgeState {
|
||||
pub alias: ForgeAlias,
|
||||
pub view_state: ViewState,
|
||||
pub repos: BTreeMap<RepoAlias, RepoState>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RepoState {
|
||||
Identified {
|
||||
repo_alias: RepoAlias,
|
||||
message: String,
|
||||
messages: Vec<String>,
|
||||
alert: Option<String>,
|
||||
},
|
||||
Configured {
|
||||
repo_alias: RepoAlias,
|
||||
message: String,
|
||||
messages: Vec<String>,
|
||||
alert: Option<String>,
|
||||
branches: RepoBranches,
|
||||
histories: Option<git::commit::Histories>,
|
||||
},
|
||||
Ready {
|
||||
repo_alias: RepoAlias,
|
||||
message: String,
|
||||
messages: Vec<String>,
|
||||
alert: Option<String>,
|
||||
branches: RepoBranches,
|
||||
histories: Option<git::commit::Histories>,
|
||||
view_state: ViewState,
|
||||
main: Commit,
|
||||
next: Commit,
|
||||
dev: Commit,
|
||||
log: Log,
|
||||
},
|
||||
}
|
||||
impl RepoState {
|
||||
#[tracing::instrument]
|
||||
pub fn update_branches(
|
||||
&mut self,
|
||||
branches: RepoBranches,
|
||||
histories: Option<git::commit::Histories>,
|
||||
) {
|
||||
match self {
|
||||
Self::Configured {
|
||||
branches: state_branches,
|
||||
histories: state_histories,
|
||||
..
|
||||
}
|
||||
| Self::Ready {
|
||||
branches: state_branches,
|
||||
histories: state_histories,
|
||||
..
|
||||
} => {
|
||||
*state_branches = branches;
|
||||
*state_histories = histories;
|
||||
}
|
||||
|
||||
Self::Identified { .. } => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn update_log(&mut self, log: Log) {
|
||||
match self {
|
||||
Self::Ready { log: state_log, .. } => {
|
||||
*state_log = log;
|
||||
}
|
||||
|
||||
Self::Identified { .. } | Self::Configured { .. } => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn update_message(&mut self, msg: impl Into<String> + std::fmt::Debug) {
|
||||
tracing::info!("new tui message");
|
||||
let msg: String = msg.into();
|
||||
match self {
|
||||
Self::Identified {
|
||||
message, messages, ..
|
||||
}
|
||||
| Self::Configured {
|
||||
message, messages, ..
|
||||
}
|
||||
| Self::Ready {
|
||||
message, messages, ..
|
||||
} => {
|
||||
messages.push(message.clone());
|
||||
*message = msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn clear_alert(&mut self) {
|
||||
match self {
|
||||
Self::Identified { .. } | Self::Configured { .. } => (),
|
||||
Self::Ready { alert, .. } => *alert = None,
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn alert(&mut self, msg: impl Into<String> + std::fmt::Debug) {
|
||||
let msg: String = msg.into();
|
||||
tracing::info!(%msg, "new tui alert");
|
||||
self.update_message("ALERT");
|
||||
match self {
|
||||
Self::Identified { alert, .. }
|
||||
| Self::Configured { alert, .. }
|
||||
| Self::Ready { alert, .. } => *alert = Some(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &State {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let block = Block::bordered()
|
||||
.title(Title::from(" Git-Next ".bold()).alignment(Alignment::Center))
|
||||
.title(
|
||||
Title::from(Line::from(vec![
|
||||
" [q]uit ".into(),
|
||||
format!(
|
||||
"{}s ",
|
||||
self.last_update.duration_since(self.started).as_secs()
|
||||
)
|
||||
.into(),
|
||||
]))
|
||||
.alignment(Alignment::Center)
|
||||
.position(ratatui::widgets::block::Position::Bottom),
|
||||
)
|
||||
.border_set(border::THICK);
|
||||
let interior = block.inner(area);
|
||||
block.render(area, buf);
|
||||
match &self.mode {
|
||||
ServerState::Initial { tick } => Paragraph::new(format!("Loading...{tick}"))
|
||||
.centered()
|
||||
.render(interior, buf),
|
||||
ServerState::Configured { forges } => {
|
||||
ConfiguredAppWidget { forges }.render(interior, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
crates/cli/src/tui/components/configured_app.rs
Normal file
52
crates/cli/src/tui/components/configured_app.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use git_next_core::ForgeAlias;
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
text::Line,
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
use crate::tui::actor::{ForgeState, ViewState};
|
||||
|
||||
use super::forge::ForgeWidget;
|
||||
|
||||
pub struct ConfiguredAppWidget<'a> {
|
||||
pub forges: &'a BTreeMap<ForgeAlias, ForgeState>,
|
||||
}
|
||||
impl<'a> Widget for ConfiguredAppWidget<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let layout_app = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(vec![Constraint::Length(1), Constraint::Fill(1)])
|
||||
.split(area);
|
||||
|
||||
Line::from(format!("Forges: ({})", self.forges.keys().len())).render(layout_app[0], buf);
|
||||
|
||||
let layout_forge_list = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
self.forges
|
||||
.iter()
|
||||
.map(|(_alias, state)| match state.view_state {
|
||||
ViewState::Collapsed => Constraint::Length(1),
|
||||
ViewState::Expanded => Constraint::Fill(1),
|
||||
}),
|
||||
)
|
||||
.split(layout_app[1]);
|
||||
|
||||
self.forges
|
||||
.iter()
|
||||
.map(|(forge_alias, state)| ForgeWidget {
|
||||
forge_alias,
|
||||
repos: &state.repos,
|
||||
view_state: state.view_state,
|
||||
})
|
||||
.enumerate()
|
||||
.for_each(|(i, w)| w.render(layout_forge_list[i], buf));
|
||||
}
|
||||
}
|
15
crates/cli/src/tui/components/forge/collapsed.rs
Normal file
15
crates/cli/src/tui/components/forge/collapsed.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
use git_next_core::ForgeAlias;
|
||||
use ratatui::{buffer::Buffer, layout::Rect, text::Text, widgets::Widget};
|
||||
|
||||
pub struct CollapsedForgeWidget<'a> {
|
||||
pub forge_alias: &'a ForgeAlias,
|
||||
}
|
||||
impl<'a> Widget for CollapsedForgeWidget<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Text::from(format!("- {}", self.forge_alias)).render(area, buf);
|
||||
}
|
||||
}
|
46
crates/cli/src/tui/components/forge/expanded.rs
Normal file
46
crates/cli/src/tui/components/forge/expanded.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use git_next_core::{ForgeAlias, RepoAlias};
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||
widgets::{block::Title, Block, Borders, Widget},
|
||||
};
|
||||
|
||||
use crate::tui::{actor::RepoState, components::repo::RepoWidget};
|
||||
|
||||
pub struct ExpandedForgeWidget<'a> {
|
||||
pub forge_alias: &'a ForgeAlias,
|
||||
pub repos: &'a BTreeMap<RepoAlias, RepoState>,
|
||||
}
|
||||
impl<'a> Widget for ExpandedForgeWidget<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let block = Block::default()
|
||||
.title(Title::from(self.forge_alias.to_string()).alignment(Alignment::Left))
|
||||
.borders(Borders::ALL);
|
||||
let layout_forge = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(vec![Constraint::Length(1), Constraint::Fill(1)])
|
||||
.split(block.inner(area));
|
||||
block.render(area, buf);
|
||||
|
||||
let layout_repo_list = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
self.repos
|
||||
.iter()
|
||||
.map(|(_alias, _state)| Constraint::Fill(1)),
|
||||
)
|
||||
.split(layout_forge[1]);
|
||||
|
||||
self.repos
|
||||
.values()
|
||||
.map(|repo_state| RepoWidget { repo_state })
|
||||
.enumerate()
|
||||
.for_each(|(i, w)| w.render(layout_repo_list[i], buf));
|
||||
}
|
||||
}
|
34
crates/cli/src/tui/components/forge/mod.rs
Normal file
34
crates/cli/src/tui/components/forge/mod.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
mod collapsed;
|
||||
mod expanded;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use git_next_core::{ForgeAlias, RepoAlias};
|
||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||
|
||||
use crate::tui::actor::{RepoState, ViewState};
|
||||
|
||||
pub struct ForgeWidget<'a> {
|
||||
pub forge_alias: &'a ForgeAlias,
|
||||
pub repos: &'a BTreeMap<RepoAlias, RepoState>,
|
||||
pub view_state: ViewState,
|
||||
}
|
||||
impl<'a> Widget for ForgeWidget<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match self.view_state {
|
||||
ViewState::Collapsed => collapsed::CollapsedForgeWidget {
|
||||
forge_alias: self.forge_alias,
|
||||
}
|
||||
.render(area, buf),
|
||||
ViewState::Expanded => expanded::ExpandedForgeWidget {
|
||||
forge_alias: self.forge_alias,
|
||||
repos: self.repos,
|
||||
}
|
||||
.render(area, buf),
|
||||
}
|
||||
}
|
||||
}
|
17
crates/cli/src/tui/components/history.rs
Normal file
17
crates/cli/src/tui/components/history.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use ratatui::{
|
||||
text::Line,
|
||||
widgets::{Paragraph, Widget},
|
||||
};
|
||||
|
||||
//
|
||||
pub struct CommitLog<'a> {
|
||||
pub histories: Option<&'a crate::git::commit::Histories>,
|
||||
}
|
||||
impl<'a> Widget for CommitLog<'a> {
|
||||
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Paragraph::new(Line::from(vec!["todo".into()])).render(area, buf);
|
||||
}
|
||||
}
|
7
crates/cli/src/tui/components/mod.rs
Normal file
7
crates/cli/src/tui/components/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod configured_app;
|
||||
mod forge;
|
||||
mod history;
|
||||
mod repo;
|
||||
|
||||
pub use configured_app::ConfiguredAppWidget;
|
||||
pub use history::CommitLog;
|
49
crates/cli/src/tui/components/repo/configured.rs
Normal file
49
crates/cli/src/tui/components/repo/configured.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
use git_next_core::{RepoAlias, RepoBranches};
|
||||
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
text::{Line, Text},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
pub struct ConfiguredRepoWidget<'a> {
|
||||
pub repo_alias: &'a RepoAlias,
|
||||
pub message: &'a str,
|
||||
pub messages: &'a Vec<String>,
|
||||
pub alert: Option<&'a String>,
|
||||
pub branches: &'a RepoBranches,
|
||||
}
|
||||
impl<'a> Widget for ConfiguredRepoWidget<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let repo_alias = &self.repo_alias;
|
||||
let main = self.branches.main();
|
||||
let next = self.branches.next();
|
||||
let dev = self.branches.dev();
|
||||
let message = self.message;
|
||||
let messages = self.messages;
|
||||
let alert = self
|
||||
.alert
|
||||
.map_or_else(String::new, |alert| format!("[alert: {alert}]"));
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(vec![Constraint::Length(1), Constraint::Fill(1)])
|
||||
.split(area);
|
||||
Text::from(format!(
|
||||
"c- {repo_alias} {alert} ({main}/{next}/{dev}) [{message}]"
|
||||
))
|
||||
.render(layout[0], buf);
|
||||
|
||||
Text::from(
|
||||
messages
|
||||
.iter()
|
||||
.map(|m| Line::from(format!("- {m}")))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.render(layout[1], buf);
|
||||
}
|
||||
}
|
33
crates/cli/src/tui/components/repo/identified.rs
Normal file
33
crates/cli/src/tui/components/repo/identified.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
use git_next_core::RepoAlias;
|
||||
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
text::{Line, Text},
|
||||
widgets::{Paragraph, Widget},
|
||||
};
|
||||
|
||||
pub struct IdentifiedRepoWidget<'a> {
|
||||
pub repo_alias: &'a RepoAlias,
|
||||
pub message: &'a str,
|
||||
pub messages: &'a Vec<String>,
|
||||
pub alert: Option<&'a String>,
|
||||
}
|
||||
impl<'a> Widget for IdentifiedRepoWidget<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let repo_alias = self.repo_alias;
|
||||
let message = self.message;
|
||||
let alert = self
|
||||
.alert
|
||||
.map_or_else(String::new, |alert| format!("[alert: {alert}]"));
|
||||
Paragraph::new(Text::from(vec![
|
||||
Line::from(vec![format!("i- {repo_alias} (loading...) {alert} [{message}]").into()]),
|
||||
Line::from(self.messages.iter().map(Into::into).collect::<Vec<_>>())
|
||||
]))
|
||||
.render(area, buf);
|
||||
}
|
||||
}
|
80
crates/cli/src/tui/components/repo/mod.rs
Normal file
80
crates/cli/src/tui/components/repo/mod.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
mod configured;
|
||||
mod identified;
|
||||
mod ready;
|
||||
|
||||
use configured::ConfiguredRepoWidget;
|
||||
use identified::IdentifiedRepoWidget;
|
||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||
use ready::ReadyRepoWidget;
|
||||
|
||||
use crate::tui::actor::RepoState;
|
||||
|
||||
pub struct RepoWidget<'a> {
|
||||
pub repo_state: &'a RepoState,
|
||||
}
|
||||
impl<'a> Widget for RepoWidget<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match self.repo_state {
|
||||
RepoState::Identified {
|
||||
repo_alias,
|
||||
message,
|
||||
messages,
|
||||
alert,
|
||||
} => {
|
||||
IdentifiedRepoWidget {
|
||||
repo_alias,
|
||||
message,
|
||||
messages,
|
||||
alert: alert.as_ref(),
|
||||
}
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
RepoState::Configured {
|
||||
repo_alias,
|
||||
message,
|
||||
messages,
|
||||
alert,
|
||||
branches,
|
||||
..
|
||||
} => ConfiguredRepoWidget {
|
||||
repo_alias,
|
||||
message,
|
||||
messages,
|
||||
alert: alert.as_ref(),
|
||||
branches,
|
||||
}
|
||||
.render(area, buf),
|
||||
|
||||
RepoState::Ready {
|
||||
repo_alias,
|
||||
message,
|
||||
messages,
|
||||
alert,
|
||||
branches,
|
||||
histories,
|
||||
view_state,
|
||||
main,
|
||||
next,
|
||||
dev,
|
||||
..
|
||||
} => ReadyRepoWidget {
|
||||
repo_alias,
|
||||
message,
|
||||
messages,
|
||||
alert: alert.as_ref(),
|
||||
branches,
|
||||
histories: histories.as_ref(),
|
||||
view_state,
|
||||
main,
|
||||
next,
|
||||
dev,
|
||||
}
|
||||
.render(area, buf),
|
||||
};
|
||||
}
|
||||
}
|
82
crates/cli/src/tui/components/repo/ready.rs
Normal file
82
crates/cli/src/tui/components/repo/ready.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
use git_next_core::{git::Commit, RepoAlias, RepoBranches};
|
||||
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
text::{Line, Text},
|
||||
widgets::{Paragraph, Widget},
|
||||
};
|
||||
use warp::filters::path::param;
|
||||
|
||||
use crate::{
|
||||
git,
|
||||
tui::{actor::ViewState, components::CommitLog},
|
||||
};
|
||||
|
||||
pub struct ReadyRepoWidget<'a> {
|
||||
pub repo_alias: &'a RepoAlias,
|
||||
pub message: &'a str,
|
||||
pub messages: &'a Vec<String>,
|
||||
pub alert: Option<&'a String>,
|
||||
pub branches: &'a RepoBranches,
|
||||
pub histories: Option<&'a git::commit::Histories>,
|
||||
pub view_state: &'a ViewState,
|
||||
pub main: &'a Commit,
|
||||
pub next: &'a Commit,
|
||||
pub dev: &'a Commit,
|
||||
}
|
||||
impl<'a> Widget for ReadyRepoWidget<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(vec![
|
||||
Constraint::Length(1),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.split(area);
|
||||
let alert = self
|
||||
.alert
|
||||
.map_or_else(String::new, |alert| format!("[alert: {alert}]"));
|
||||
Paragraph::new(Text::from(vec![
|
||||
Line::from(vec![
|
||||
"r".into(),
|
||||
self.view_state.to_string().into(),
|
||||
" ".into(),
|
||||
self.repo_alias.to_string().into(),
|
||||
alert.into(),
|
||||
" (".into(),
|
||||
self.branches.main().to_string().into(),
|
||||
"/".into(),
|
||||
self.branches.next().to_string().into(),
|
||||
"/".into(),
|
||||
self.branches.dev().to_string().into(),
|
||||
") [".into(),
|
||||
self.message.into(),
|
||||
"]".into(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
self.main.to_string().into(),
|
||||
" , ".into(),
|
||||
self.next.to_string().into(),
|
||||
" , ".into(),
|
||||
self.dev.to_string().into(),
|
||||
"}".into(),
|
||||
]),
|
||||
]))
|
||||
.render(layout[0], buf);
|
||||
|
||||
Paragraph::new(Text::from(vec![Line::from(
|
||||
self.messages.iter().map(Into::into).collect::<Vec<_>>(),
|
||||
)]))
|
||||
.render(layout[1], buf);
|
||||
CommitLog {
|
||||
histories: self.histories,
|
||||
}
|
||||
.render(layout[2], buf);
|
||||
}
|
||||
}
|
86
crates/cli/src/tui/logging.rs
Normal file
86
crates/cli/src/tui/logging.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
use std::path::PathBuf;
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use directories::ProjectDirs;
|
||||
use lazy_static::lazy_static;
|
||||
use tracing_error::ErrorLayer;
|
||||
use tracing_subscriber::{self, layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref PROJECT_NAME: String = env!("CARGO_CRATE_NAME").to_uppercase();
|
||||
pub static ref DATA_FOLDER: Option<PathBuf> =
|
||||
std::env::var(format!("{}_DATA", PROJECT_NAME.clone()))
|
||||
.ok()
|
||||
.map(PathBuf::from);
|
||||
pub static ref LOG_ENV: String = format!("{}_LOGLEVEL", PROJECT_NAME.clone());
|
||||
pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME"));
|
||||
}
|
||||
|
||||
fn project_directory() -> Option<ProjectDirs> {
|
||||
ProjectDirs::from("net", "kemitix", env!("CARGO_PKG_NAME"))
|
||||
}
|
||||
|
||||
pub fn get_data_dir() -> PathBuf {
|
||||
let directory = DATA_FOLDER.clone().map_or_else(
|
||||
|| {
|
||||
project_directory().map_or_else(
|
||||
|| PathBuf::from(".").join(".data"),
|
||||
|proj_dirs| proj_dirs.data_local_dir().to_path_buf(),
|
||||
)
|
||||
},
|
||||
|data_folder| data_folder,
|
||||
);
|
||||
directory
|
||||
}
|
||||
|
||||
pub fn initialize_logging() -> Result<()> {
|
||||
let directory = get_data_dir();
|
||||
std::fs::create_dir_all(directory.clone())?;
|
||||
let log_path = directory.join(LOG_FILE.clone());
|
||||
let log_file = std::fs::File::create(log_path)?;
|
||||
std::env::set_var(
|
||||
"RUST_LOG",
|
||||
std::env::var("RUST_LOG")
|
||||
.or_else(|_| std::env::var(LOG_ENV.clone()))
|
||||
.unwrap_or_else(|_| format!("{}=info", env!("CARGO_CRATE_NAME"))),
|
||||
);
|
||||
let file_subscriber = tracing_subscriber::fmt::layer()
|
||||
.with_file(true)
|
||||
.with_line_number(true)
|
||||
.with_writer(log_file)
|
||||
.with_target(false)
|
||||
.with_ansi(false)
|
||||
.with_filter(tracing_subscriber::filter::EnvFilter::from_default_env());
|
||||
tracing_subscriber::registry()
|
||||
.with(file_subscriber)
|
||||
.with(ErrorLayer::default())
|
||||
.init();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Similar to the `std::dbg!` macro, but generates `tracing` events rather
|
||||
/// than printing to stdout.
|
||||
///
|
||||
/// By default, the verbosity level for the generated events is `DEBUG`, but
|
||||
/// this can be customized.
|
||||
#[macro_export]
|
||||
macro_rules! trace_dbg {
|
||||
(target: $target:expr, level: $level:expr, $ex:expr) => {{
|
||||
match $ex {
|
||||
value => {
|
||||
tracing::event!(target: $target, $level, ?value, stringify!($ex));
|
||||
value
|
||||
}
|
||||
}
|
||||
}};
|
||||
(level: $level:expr, $ex:expr) => {
|
||||
trace_dbg!(target: module_path!(), level: $level, $ex)
|
||||
};
|
||||
(target: $target:expr, $ex:expr) => {
|
||||
trace_dbg!(target: $target, level: tracing::Level::DEBUG, $ex)
|
||||
};
|
||||
($ex:expr) => {
|
||||
trace_dbg!(level: tracing::Level::DEBUG, $ex)
|
||||
};
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
//
|
||||
mod actor;
|
||||
pub mod components;
|
||||
pub mod logging;
|
||||
|
||||
pub use actor::messages::Tick;
|
||||
pub use actor::Tui;
|
||||
|
|
|
@ -47,7 +47,8 @@ impl ServerRepoConfig {
|
|||
}
|
||||
|
||||
/// Returns a `RepoConfig` from the server configuration if ALL THREE branches were provided
|
||||
pub(crate) fn repo_config(&self) -> Option<RepoConfig> {
|
||||
#[must_use]
|
||||
pub fn repo_config(&self) -> Option<RepoConfig> {
|
||||
match (&self.main, &self.next, &self.dev) {
|
||||
(Some(main), Some(next), Some(dev)) => Some(RepoConfig::new(
|
||||
RepoBranches::new(main.to_string(), next.to_string(), dev.to_string()),
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use crate::config::{BranchName, RepoBranches};
|
||||
use derive_more::Constructor;
|
||||
|
||||
#[derive(Debug, Constructor, derive_with::With)]
|
||||
#[derive(Clone, Debug, Constructor, PartialEq, Eq, derive_with::With)]
|
||||
pub struct Push {
|
||||
branch: BranchName,
|
||||
sha: String,
|
||||
|
@ -34,7 +34,7 @@ impl Push {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Branch {
|
||||
Main,
|
||||
Next,
|
||||
|
|
|
@ -62,7 +62,7 @@ newtype!(
|
|||
"The commit message for a git commit."
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Histories {
|
||||
pub main: Vec<Commit>,
|
||||
pub next: Vec<Commit>,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//
|
||||
|
||||
use std::borrow::ToOwned;
|
||||
|
||||
use take_until::TakeUntilExt;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
use crate::{git, git::repository::open::OpenRepositoryLike, BranchName};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Force {
|
||||
No,
|
||||
From(git::GitRef),
|
||||
|
|
|
@ -94,7 +94,8 @@ pub trait OpenRepositoryLike: std::fmt::Debug + Sync {
|
|||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` if there are any network connectivity issues with the remote server.
|
||||
/// Will return `Err` if there are any problems with the branch name being invalid, or any
|
||||
/// corruption of the git repository.
|
||||
fn commit_log(
|
||||
&self,
|
||||
branch_name: &BranchName,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
//
|
||||
use crate::{git::Commit, BranchName, ForgeAlias, RepoAlias};
|
||||
use serde_json::json;
|
||||
|
@ -115,3 +117,18 @@ impl UserNotification {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl Display for UserNotification {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let message = match self {
|
||||
Self::CICheckFailed { commit, .. } => format!("CI Check Failed [{commit}]"),
|
||||
Self::RepoConfigLoadFailure { reason, .. } => {
|
||||
format!("Failed to load repo config: {reason}")
|
||||
}
|
||||
Self::WebhookRegistration { reason, .. } => {
|
||||
format!("Failed to register webhook: {reason}")
|
||||
}
|
||||
Self::DevNotBasedOnMain { .. } => "Dev not based on main".to_string(),
|
||||
};
|
||||
write!(f, "{message}")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,7 +129,13 @@ fn is_based_on(commits: &[git::commit::Commit], needle: &git::Commit) -> bool {
|
|||
commits.iter().any(|commit| commit == needle)
|
||||
}
|
||||
|
||||
fn get_commit_histories(
|
||||
/// Returns the commit logs for the main, next and dev branches
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` if there are any problems with the branch names being invalid, or any
|
||||
/// corruption of the git repository.
|
||||
pub fn get_commit_histories(
|
||||
open_repository: &dyn OpenRepositoryLike,
|
||||
repo_config: &RepoConfig,
|
||||
) -> git::commit::log::Result<git::commit::Histories> {
|
||||
|
|
|
@ -35,7 +35,7 @@ pub async fn register(
|
|||
"active": true,
|
||||
"events": ["push"],
|
||||
"config": {
|
||||
"url": repo_listen_url.as_ref(),
|
||||
"url": repo_listen_url.to_string(),
|
||||
"content_type": "json",
|
||||
"secret": authorisation.to_string(),
|
||||
"insecure_ssl": "0",
|
||||
|
|
Loading…
Reference in a new issue