max / makenotwork
18 files changed,
+1537 insertions,
-0 deletions
| @@ -0,0 +1,21 @@ | |||
| 1 | + | MIT License | |
| 2 | + | ||
| 3 | + | Copyright (c) 2026 Max Jacobson | |
| 4 | + | ||
| 5 | + | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | + | of this software and associated documentation files (the "Software"), to deal | |
| 7 | + | in the Software without restriction, including without limitation the rights | |
| 8 | + | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | + | copies of the Software, and to permit persons to whom the Software is | |
| 10 | + | furnished to do so, subject to the following conditions: | |
| 11 | + | ||
| 12 | + | The above copyright notice and this permission notice shall be included in all | |
| 13 | + | copies or substantial portions of the Software. | |
| 14 | + | ||
| 15 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | + | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | + | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 21 | + | SOFTWARE. |
| @@ -0,0 +1,2554 @@ | |||
| 1 | + | # This file is automatically @generated by Cargo. | |
| 2 | + | # It is not intended for manual editing. | |
| 3 | + | version = 4 | |
| 4 | + | ||
| 5 | + | [[package]] | |
| 6 | + | name = "aho-corasick" | |
| 7 | + | version = "1.1.4" | |
| 8 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 9 | + | checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" | |
| 10 | + | dependencies = [ | |
| 11 | + | "memchr", | |
| 12 | + | ] | |
| 13 | + | ||
| 14 | + | [[package]] | |
| 15 | + | name = "allocator-api2" | |
| 16 | + | version = "0.2.21" | |
| 17 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 18 | + | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" | |
| 19 | + | ||
| 20 | + | [[package]] | |
| 21 | + | name = "android_system_properties" | |
| 22 | + | version = "0.1.5" | |
| 23 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 24 | + | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" | |
| 25 | + | dependencies = [ | |
| 26 | + | "libc", | |
| 27 | + | ] | |
| 28 | + | ||
| 29 | + | [[package]] | |
| 30 | + | name = "anyhow" | |
| 31 | + | version = "1.0.102" | |
| 32 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 33 | + | checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" | |
| 34 | + | ||
| 35 | + | [[package]] | |
| 36 | + | name = "atoi" | |
| 37 | + | version = "2.0.0" | |
| 38 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 39 | + | checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" | |
| 40 | + | dependencies = [ | |
| 41 | + | "num-traits", | |
| 42 | + | ] | |
| 43 | + | ||
| 44 | + | [[package]] | |
| 45 | + | name = "atomic-waker" | |
| 46 | + | version = "1.1.2" | |
| 47 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 48 | + | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" | |
| 49 | + | ||
| 50 | + | [[package]] | |
| 51 | + | name = "autocfg" | |
| 52 | + | version = "1.5.1" | |
| 53 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 54 | + | checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" | |
| 55 | + | ||
| 56 | + | [[package]] | |
| 57 | + | name = "axum" | |
| 58 | + | version = "0.8.9" | |
| 59 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 60 | + | checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" | |
| 61 | + | dependencies = [ | |
| 62 | + | "axum-core", | |
| 63 | + | "axum-macros", | |
| 64 | + | "base64", | |
| 65 | + | "bytes", | |
| 66 | + | "form_urlencoded", | |
| 67 | + | "futures-util", | |
| 68 | + | "http", | |
| 69 | + | "http-body", | |
| 70 | + | "http-body-util", | |
| 71 | + | "hyper", | |
| 72 | + | "hyper-util", | |
| 73 | + | "itoa", | |
| 74 | + | "matchit", | |
| 75 | + | "memchr", | |
| 76 | + | "mime", | |
| 77 | + | "percent-encoding", | |
| 78 | + | "pin-project-lite", | |
| 79 | + | "serde_core", | |
| 80 | + | "serde_json", | |
| 81 | + | "serde_path_to_error", | |
| 82 | + | "serde_urlencoded", | |
| 83 | + | "sha1", | |
| 84 | + | "sync_wrapper", | |
| 85 | + | "tokio", | |
| 86 | + | "tokio-tungstenite", | |
| 87 | + | "tower", | |
| 88 | + | "tower-layer", | |
| 89 | + | "tower-service", | |
| 90 | + | "tracing", | |
| 91 | + | ] | |
| 92 | + | ||
| 93 | + | [[package]] | |
| 94 | + | name = "axum-core" | |
| 95 | + | version = "0.5.6" | |
| 96 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 97 | + | checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" | |
| 98 | + | dependencies = [ | |
| 99 | + | "bytes", | |
| 100 | + | "futures-core", | |
| 101 | + | "http", | |
| 102 | + | "http-body", | |
| 103 | + | "http-body-util", | |
| 104 | + | "mime", | |
| 105 | + | "pin-project-lite", | |
| 106 | + | "sync_wrapper", | |
| 107 | + | "tower-layer", | |
| 108 | + | "tower-service", | |
| 109 | + | "tracing", | |
| 110 | + | ] | |
| 111 | + | ||
| 112 | + | [[package]] | |
| 113 | + | name = "axum-macros" | |
| 114 | + | version = "0.5.1" | |
| 115 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 116 | + | checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" | |
| 117 | + | dependencies = [ | |
| 118 | + | "proc-macro2", | |
| 119 | + | "quote", | |
| 120 | + | "syn", | |
| 121 | + | ] | |
| 122 | + | ||
| 123 | + | [[package]] | |
| 124 | + | name = "base64" | |
| 125 | + | version = "0.22.1" | |
| 126 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 127 | + | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" | |
| 128 | + | ||
| 129 | + | [[package]] | |
| 130 | + | name = "base64ct" | |
| 131 | + | version = "1.8.3" | |
| 132 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 133 | + | checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" | |
| 134 | + | ||
| 135 | + | [[package]] | |
| 136 | + | name = "bitflags" | |
| 137 | + | version = "2.11.1" | |
| 138 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 139 | + | checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" | |
| 140 | + | dependencies = [ | |
| 141 | + | "serde_core", | |
| 142 | + | ] | |
| 143 | + | ||
| 144 | + | [[package]] | |
| 145 | + | name = "block-buffer" | |
| 146 | + | version = "0.10.4" | |
| 147 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 148 | + | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" | |
| 149 | + | dependencies = [ | |
| 150 | + | "generic-array", | |
| 151 | + | ] | |
| 152 | + | ||
| 153 | + | [[package]] | |
| 154 | + | name = "bumpalo" | |
| 155 | + | version = "3.20.3" | |
| 156 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 157 | + | checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" | |
| 158 | + | ||
| 159 | + | [[package]] | |
| 160 | + | name = "byteorder" | |
| 161 | + | version = "1.5.0" | |
| 162 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 163 | + | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" | |
| 164 | + | ||
| 165 | + | [[package]] | |
| 166 | + | name = "bytes" | |
| 167 | + | version = "1.11.1" | |
| 168 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 169 | + | checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" | |
| 170 | + | ||
| 171 | + | [[package]] | |
| 172 | + | name = "cc" | |
| 173 | + | version = "1.2.62" | |
| 174 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 175 | + | checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" | |
| 176 | + | dependencies = [ | |
| 177 | + | "find-msvc-tools", | |
| 178 | + | "shlex", | |
| 179 | + | ] | |
| 180 | + | ||
| 181 | + | [[package]] | |
| 182 | + | name = "cfg-if" | |
| 183 | + | version = "1.0.4" | |
| 184 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 185 | + | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" | |
| 186 | + | ||
| 187 | + | [[package]] | |
| 188 | + | name = "chrono" | |
| 189 | + | version = "0.4.44" | |
| 190 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 191 | + | checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" | |
| 192 | + | dependencies = [ | |
| 193 | + | "iana-time-zone", | |
| 194 | + | "js-sys", | |
| 195 | + | "num-traits", | |
| 196 | + | "serde", | |
| 197 | + | "wasm-bindgen", | |
| 198 | + | "windows-link", | |
| 199 | + | ] | |
| 200 | + | ||
| 201 | + | [[package]] | |
| 202 | + | name = "concurrent-queue" | |
| 203 | + | version = "2.5.0" | |
| 204 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 205 | + | checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" | |
| 206 | + | dependencies = [ | |
| 207 | + | "crossbeam-utils", | |
| 208 | + | ] | |
| 209 | + | ||
| 210 | + | [[package]] | |
| 211 | + | name = "const-oid" | |
| 212 | + | version = "0.9.6" | |
| 213 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 214 | + | checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" | |
| 215 | + | ||
| 216 | + | [[package]] | |
| 217 | + | name = "core-foundation-sys" | |
| 218 | + | version = "0.8.7" | |
| 219 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 220 | + | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" | |
| 221 | + | ||
| 222 | + | [[package]] | |
| 223 | + | name = "cpufeatures" | |
| 224 | + | version = "0.2.17" | |
| 225 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 226 | + | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" | |
| 227 | + | dependencies = [ | |
| 228 | + | "libc", | |
| 229 | + | ] | |
| 230 | + | ||
| 231 | + | [[package]] | |
| 232 | + | name = "crc" | |
| 233 | + | version = "3.4.0" | |
| 234 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 235 | + | checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" | |
| 236 | + | dependencies = [ | |
| 237 | + | "crc-catalog", | |
| 238 | + | ] | |
| 239 | + | ||
| 240 | + | [[package]] | |
| 241 | + | name = "crc-catalog" | |
| 242 | + | version = "2.5.0" | |
| 243 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 244 | + | checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" | |
| 245 | + | ||
| 246 | + | [[package]] | |
| 247 | + | name = "crossbeam-epoch" | |
| 248 | + | version = "0.9.18" | |
| 249 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 250 | + | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" | |
| 251 | + | dependencies = [ | |
| 252 | + | "crossbeam-utils", | |
| 253 | + | ] | |
| 254 | + | ||
| 255 | + | [[package]] | |
| 256 | + | name = "crossbeam-queue" | |
| 257 | + | version = "0.3.12" | |
| 258 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 259 | + | checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" | |
| 260 | + | dependencies = [ | |
| 261 | + | "crossbeam-utils", | |
| 262 | + | ] | |
| 263 | + | ||
| 264 | + | [[package]] | |
| 265 | + | name = "crossbeam-utils" | |
| 266 | + | version = "0.8.21" | |
| 267 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 268 | + | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" | |
| 269 | + | ||
| 270 | + | [[package]] | |
| 271 | + | name = "crypto-common" | |
| 272 | + | version = "0.1.7" | |
| 273 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 274 | + | checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" | |
| 275 | + | dependencies = [ | |
| 276 | + | "generic-array", | |
| 277 | + | "typenum", | |
| 278 | + | ] | |
| 279 | + | ||
| 280 | + | [[package]] | |
| 281 | + | name = "data-encoding" | |
| 282 | + | version = "2.11.0" | |
| 283 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 284 | + | checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" | |
| 285 | + | ||
| 286 | + | [[package]] | |
| 287 | + | name = "der" | |
| 288 | + | version = "0.7.10" | |
| 289 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 290 | + | checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" | |
| 291 | + | dependencies = [ | |
| 292 | + | "const-oid", | |
| 293 | + | "pem-rfc7468", | |
| 294 | + | "zeroize", | |
| 295 | + | ] | |
| 296 | + | ||
| 297 | + | [[package]] | |
| 298 | + | name = "digest" | |
| 299 | + | version = "0.10.7" | |
| 300 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 301 | + | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" | |
| 302 | + | dependencies = [ | |
| 303 | + | "block-buffer", | |
| 304 | + | "const-oid", | |
| 305 | + | "crypto-common", | |
| 306 | + | "subtle", | |
| 307 | + | ] | |
| 308 | + | ||
| 309 | + | [[package]] | |
| 310 | + | name = "displaydoc" | |
| 311 | + | version = "0.2.5" | |
| 312 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 313 | + | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" | |
| 314 | + | dependencies = [ | |
| 315 | + | "proc-macro2", | |
| 316 | + | "quote", | |
| 317 | + | "syn", | |
| 318 | + | ] | |
| 319 | + | ||
| 320 | + | [[package]] | |
| 321 | + | name = "dotenvy" | |
| 322 | + | version = "0.15.7" | |
| 323 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 324 | + | checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" | |
| 325 | + | ||
| 326 | + | [[package]] | |
| 327 | + | name = "either" | |
| 328 | + | version = "1.16.0" | |
| 329 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 330 | + | checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" | |
| 331 | + | dependencies = [ | |
| 332 | + | "serde", | |
| 333 | + | ] | |
| 334 | + | ||
| 335 | + | [[package]] | |
| 336 | + | name = "equivalent" | |
| 337 | + | version = "1.0.2" | |
| 338 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 339 | + | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" | |
| 340 | + | ||
| 341 | + | [[package]] | |
| 342 | + | name = "errno" | |
| 343 | + | version = "0.3.14" | |
| 344 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 345 | + | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" | |
| 346 | + | dependencies = [ | |
| 347 | + | "libc", | |
| 348 | + | "windows-sys 0.61.2", | |
| 349 | + | ] | |
| 350 | + | ||
| 351 | + | [[package]] | |
| 352 | + | name = "etcetera" | |
| 353 | + | version = "0.8.0" | |
| 354 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 355 | + | checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" | |
| 356 | + | dependencies = [ | |
| 357 | + | "cfg-if", | |
| 358 | + | "home", | |
| 359 | + | "windows-sys 0.48.0", | |
| 360 | + | ] | |
| 361 | + | ||
| 362 | + | [[package]] | |
| 363 | + | name = "event-listener" | |
| 364 | + | version = "5.4.1" | |
| 365 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 366 | + | checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" | |
| 367 | + | dependencies = [ | |
| 368 | + | "concurrent-queue", | |
| 369 | + | "parking", | |
| 370 | + | "pin-project-lite", | |
| 371 | + | ] | |
| 372 | + | ||
| 373 | + | [[package]] | |
| 374 | + | name = "evmap" | |
| 375 | + | version = "11.0.0" | |
| 376 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 377 | + | checksum = "1b8874945f036109c72242964c1174cf99434e30cfa45bf45fedc983f50046f8" | |
| 378 | + | dependencies = [ | |
| 379 | + | "hashbag", | |
| 380 | + | "left-right", | |
| 381 | + | "smallvec", | |
| 382 | + | ] | |
| 383 | + | ||
| 384 | + | [[package]] | |
| 385 | + | name = "find-msvc-tools" | |
| 386 | + | version = "0.1.9" | |
| 387 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 388 | + | checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" | |
| 389 | + | ||
| 390 | + | [[package]] | |
| 391 | + | name = "flume" | |
| 392 | + | version = "0.11.1" | |
| 393 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 394 | + | checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" | |
| 395 | + | dependencies = [ | |
| 396 | + | "futures-core", | |
| 397 | + | "futures-sink", | |
| 398 | + | "spin", | |
| 399 | + | ] | |
| 400 | + | ||
| 401 | + | [[package]] | |
| 402 | + | name = "foldhash" | |
| 403 | + | version = "0.1.5" | |
| 404 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 405 | + | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" | |
| 406 | + | ||
| 407 | + | [[package]] | |
| 408 | + | name = "foldhash" | |
| 409 | + | version = "0.2.0" | |
| 410 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 411 | + | checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" | |
| 412 | + | ||
| 413 | + | [[package]] | |
| 414 | + | name = "form_urlencoded" | |
| 415 | + | version = "1.2.2" | |
| 416 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 417 | + | checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" | |
| 418 | + | dependencies = [ | |
| 419 | + | "percent-encoding", | |
| 420 | + | ] | |
| 421 | + | ||
| 422 | + | [[package]] | |
| 423 | + | name = "futures-channel" | |
| 424 | + | version = "0.3.32" | |
| 425 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 426 | + | checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" | |
| 427 | + | dependencies = [ | |
| 428 | + | "futures-core", | |
| 429 | + | "futures-sink", | |
| 430 | + | ] | |
| 431 | + | ||
| 432 | + | [[package]] | |
| 433 | + | name = "futures-core" | |
| 434 | + | version = "0.3.32" | |
| 435 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 436 | + | checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" | |
| 437 | + | ||
| 438 | + | [[package]] | |
| 439 | + | name = "futures-executor" | |
| 440 | + | version = "0.3.32" | |
| 441 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 442 | + | checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" | |
| 443 | + | dependencies = [ | |
| 444 | + | "futures-core", | |
| 445 | + | "futures-task", | |
| 446 | + | "futures-util", | |
| 447 | + | ] | |
| 448 | + | ||
| 449 | + | [[package]] | |
| 450 | + | name = "futures-intrusive" | |
| 451 | + | version = "0.5.0" | |
| 452 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 453 | + | checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" | |
| 454 | + | dependencies = [ | |
| 455 | + | "futures-core", | |
| 456 | + | "lock_api", | |
| 457 | + | "parking_lot", | |
| 458 | + | ] | |
| 459 | + | ||
| 460 | + | [[package]] | |
| 461 | + | name = "futures-io" | |
| 462 | + | version = "0.3.32" | |
| 463 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 464 | + | checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" | |
| 465 | + | ||
| 466 | + | [[package]] | |
| 467 | + | name = "futures-sink" | |
| 468 | + | version = "0.3.32" | |
| 469 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 470 | + | checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" | |
| 471 | + | ||
| 472 | + | [[package]] | |
| 473 | + | name = "futures-task" | |
| 474 | + | version = "0.3.32" | |
| 475 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 476 | + | checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" | |
| 477 | + | ||
| 478 | + | [[package]] | |
| 479 | + | name = "futures-util" | |
| 480 | + | version = "0.3.32" | |
| 481 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 482 | + | checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" | |
| 483 | + | dependencies = [ | |
| 484 | + | "futures-core", | |
| 485 | + | "futures-io", | |
| 486 | + | "futures-sink", | |
| 487 | + | "futures-task", | |
| 488 | + | "memchr", | |
| 489 | + | "pin-project-lite", | |
| 490 | + | "slab", | |
| 491 | + | ] | |
| 492 | + | ||
| 493 | + | [[package]] | |
| 494 | + | name = "generator" | |
| 495 | + | version = "0.8.8" | |
| 496 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 497 | + | checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" | |
| 498 | + | dependencies = [ | |
| 499 | + | "cc", | |
| 500 | + | "cfg-if", |
Lines truncated
| @@ -0,0 +1,24 @@ | |||
| 1 | + | [package] | |
| 2 | + | name = "sando-daemon" | |
| 3 | + | version = "0.1.0" | |
| 4 | + | edition = "2024" | |
| 5 | + | license = "MIT" | |
| 6 | + | ||
| 7 | + | [[bin]] | |
| 8 | + | name = "sandod" | |
| 9 | + | path = "src/main.rs" | |
| 10 | + | ||
| 11 | + | [dependencies] | |
| 12 | + | axum = { version = "0.8.8", features = ["macros", "ws"] } | |
| 13 | + | tokio = { version = "1.50.0", features = ["macros", "rt-multi-thread", "net", "signal", "fs", "process"] } | |
| 14 | + | serde = { version = "1.0.228", features = ["derive"] } | |
| 15 | + | serde_json = "1" | |
| 16 | + | toml = "0.8" | |
| 17 | + | sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite", "chrono", "migrate"] } | |
| 18 | + | tracing = "0.1.44" | |
| 19 | + | tracing-subscriber = { version = "0.3.22", features = ["env-filter", "json"] } | |
| 20 | + | metrics = "0.24" | |
| 21 | + | metrics-exporter-prometheus = { version = "0.18.1", default-features = false } | |
| 22 | + | anyhow = "1.0.102" | |
| 23 | + | thiserror = "2.0.18" | |
| 24 | + | chrono = { version = "0.4", features = ["serde"] } |
| @@ -0,0 +1,70 @@ | |||
| 1 | + | -- Sando v0 schema. | |
| 2 | + | -- Tiers and nodes are modeled as rows so topology changes are config edits, | |
| 3 | + | -- not schema migrations. The daemon syncs (tiers, nodes) from sando.toml on | |
| 4 | + | -- startup; mutable per-tier state (current_version, burn_in_started_at) lives | |
| 5 | + | -- in tier_state and survives restarts. | |
| 6 | + | ||
| 7 | + | CREATE TABLE tiers ( | |
| 8 | + | name TEXT PRIMARY KEY, | |
| 9 | + | ord INTEGER NOT NULL, | |
| 10 | + | provisioned INTEGER NOT NULL DEFAULT 0, | |
| 11 | + | canary TEXT NOT NULL DEFAULT 'sequential' | |
| 12 | + | ); | |
| 13 | + | ||
| 14 | + | CREATE TABLE nodes ( | |
| 15 | + | name TEXT PRIMARY KEY, | |
| 16 | + | tier TEXT NOT NULL REFERENCES tiers(name), | |
| 17 | + | ssh_target TEXT NOT NULL, | |
| 18 | + | release_root TEXT NOT NULL | |
| 19 | + | ); | |
| 20 | + | ||
| 21 | + | CREATE INDEX nodes_by_tier ON nodes(tier); | |
| 22 | + | ||
| 23 | + | CREATE TABLE versions ( | |
| 24 | + | version TEXT PRIMARY KEY, | |
| 25 | + | git_sha TEXT NOT NULL, | |
| 26 | + | built_at TEXT NOT NULL, | |
| 27 | + | artifact_path TEXT NOT NULL | |
| 28 | + | ); | |
| 29 | + | ||
| 30 | + | CREATE TABLE deploys ( | |
| 31 | + | id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| 32 | + | version TEXT NOT NULL REFERENCES versions(version), | |
| 33 | + | tier TEXT NOT NULL REFERENCES tiers(name), | |
| 34 | + | node TEXT REFERENCES nodes(name), | |
| 35 | + | started_at TEXT NOT NULL, | |
| 36 | + | finished_at TEXT, | |
| 37 | + | outcome TEXT NOT NULL DEFAULT 'in_progress', | |
| 38 | + | hotfix INTEGER NOT NULL DEFAULT 0, | |
| 39 | + | reset_burn_in INTEGER NOT NULL DEFAULT 0 | |
| 40 | + | ); | |
| 41 | + | ||
| 42 | + | CREATE INDEX deploys_by_tier_version ON deploys(tier, version); | |
| 43 | + | ||
| 44 | + | CREATE TABLE gate_runs ( | |
| 45 | + | id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| 46 | + | version TEXT NOT NULL REFERENCES versions(version), | |
| 47 | + | tier TEXT NOT NULL REFERENCES tiers(name), | |
| 48 | + | gate_kind TEXT NOT NULL, | |
| 49 | + | started_at TEXT NOT NULL, | |
| 50 | + | finished_at TEXT, | |
| 51 | + | passed INTEGER, | |
| 52 | + | detail TEXT | |
| 53 | + | ); | |
| 54 | + | ||
| 55 | + | CREATE INDEX gate_runs_lookup ON gate_runs(tier, version, gate_kind); | |
| 56 | + | ||
| 57 | + | CREATE TABLE tier_state ( | |
| 58 | + | tier TEXT PRIMARY KEY REFERENCES tiers(name), | |
| 59 | + | current_version TEXT REFERENCES versions(version), | |
| 60 | + | previous_version TEXT REFERENCES versions(version), | |
| 61 | + | burn_in_started_at TEXT | |
| 62 | + | ); | |
| 63 | + | ||
| 64 | + | CREATE TABLE backups ( | |
| 65 | + | id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| 66 | + | fetched_at TEXT NOT NULL, | |
| 67 | + | source TEXT NOT NULL, | |
| 68 | + | local_path TEXT NOT NULL, | |
| 69 | + | byte_size INTEGER | |
| 70 | + | ); |
| @@ -0,0 +1,4 @@ | |||
| 1 | + | # Local dev defaults. Override path with SANDO_CONFIG. | |
| 2 | + | listen = "127.0.0.1:7766" | |
| 3 | + | db_path = "./sando.db" | |
| 4 | + | topology_path = "../sando.toml" |
| @@ -0,0 +1,19 @@ | |||
| 1 | + | use anyhow::{Context, Result}; | |
| 2 | + | use serde::Deserialize; | |
| 3 | + | use std::path::PathBuf; | |
| 4 | + | ||
| 5 | + | #[derive(Debug, Deserialize)] | |
| 6 | + | pub struct Config { | |
| 7 | + | pub listen: String, | |
| 8 | + | pub db_path: PathBuf, | |
| 9 | + | pub topology_path: PathBuf, | |
| 10 | + | } | |
| 11 | + | ||
| 12 | + | impl Config { | |
| 13 | + | pub fn load() -> Result<Self> { | |
| 14 | + | let path = std::env::var("SANDO_CONFIG").unwrap_or_else(|_| "sando-daemon.toml".into()); | |
| 15 | + | let raw = std::fs::read_to_string(&path) | |
| 16 | + | .with_context(|| format!("reading daemon config at {path}"))?; | |
| 17 | + | Ok(toml::from_str(&raw)?) | |
| 18 | + | } | |
| 19 | + | } |
| @@ -0,0 +1,19 @@ | |||
| 1 | + | use anyhow::Result; | |
| 2 | + | use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; | |
| 3 | + | use sqlx::SqlitePool; | |
| 4 | + | use std::path::Path; | |
| 5 | + | use std::str::FromStr; | |
| 6 | + | ||
| 7 | + | pub async fn connect(path: &Path) -> Result<SqlitePool> { | |
| 8 | + | let url = format!("sqlite://{}?mode=rwc", path.display()); | |
| 9 | + | let opts = SqliteConnectOptions::from_str(&url)? | |
| 10 | + | .create_if_missing(true) | |
| 11 | + | .foreign_keys(true); | |
| 12 | + | let pool = SqlitePoolOptions::new().max_connections(4).connect_with(opts).await?; | |
| 13 | + | Ok(pool) | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | pub async fn migrate(pool: &SqlitePool) -> Result<()> { | |
| 17 | + | sqlx::migrate!("./migrations").run(pool).await?; | |
| 18 | + | Ok(()) | |
| 19 | + | } |
| @@ -0,0 +1,27 @@ | |||
| 1 | + | use axum::http::StatusCode; | |
| 2 | + | use axum::response::{IntoResponse, Response}; | |
| 3 | + | ||
| 4 | + | #[derive(Debug, thiserror::Error)] | |
| 5 | + | pub enum Error { | |
| 6 | + | #[error("not found")] | |
| 7 | + | NotFound, | |
| 8 | + | #[error("gate not satisfied: {0}")] | |
| 9 | + | GateBlocked(String), | |
| 10 | + | #[error(transparent)] | |
| 11 | + | Db(#[from] sqlx::Error), | |
| 12 | + | #[error(transparent)] | |
| 13 | + | Other(#[from] anyhow::Error), | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | impl IntoResponse for Error { | |
| 17 | + | fn into_response(self) -> Response { | |
| 18 | + | let status = match &self { | |
| 19 | + | Error::NotFound => StatusCode::NOT_FOUND, | |
| 20 | + | Error::GateBlocked(_) => StatusCode::CONFLICT, | |
| 21 | + | _ => StatusCode::INTERNAL_SERVER_ERROR, | |
| 22 | + | }; | |
| 23 | + | (status, self.to_string()).into_response() | |
| 24 | + | } | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | pub type Result<T> = std::result::Result<T, Error>; |
| @@ -0,0 +1,12 @@ | |||
| 1 | + | use crate::topology::Gate; | |
| 2 | + | ||
| 3 | + | #[derive(Debug, Clone)] | |
| 4 | + | pub struct GateOutcome { | |
| 5 | + | pub gate: String, | |
| 6 | + | pub passed: bool, | |
| 7 | + | pub detail: Option<String>, | |
| 8 | + | } | |
| 9 | + | ||
| 10 | + | pub async fn run(_gate: &Gate) -> GateOutcome { | |
| 11 | + | todo!("gate execution: cargo test, migration dry-run, boot smoke, burn-in check, manual confirm") | |
| 12 | + | } |
| @@ -0,0 +1,36 @@ | |||
| 1 | + | use anyhow::Result; | |
| 2 | + | use std::net::SocketAddr; | |
| 3 | + | ||
| 4 | + | mod config; | |
| 5 | + | mod db; | |
| 6 | + | mod error; | |
| 7 | + | mod gates; | |
| 8 | + | mod metrics; | |
| 9 | + | mod routes; | |
| 10 | + | mod state; | |
| 11 | + | mod topology; | |
| 12 | + | ||
| 13 | + | #[tokio::main] | |
| 14 | + | async fn main() -> Result<()> { | |
| 15 | + | tracing_subscriber::fmt() | |
| 16 | + | .with_env_filter( | |
| 17 | + | tracing_subscriber::EnvFilter::try_from_default_env() | |
| 18 | + | .unwrap_or_else(|_| "sando_daemon=info,tower_http=info".into()), | |
| 19 | + | ) | |
| 20 | + | .init(); | |
| 21 | + | ||
| 22 | + | let cfg = config::Config::load()?; | |
| 23 | + | let topo = topology::Topology::load(&cfg.topology_path)?; | |
| 24 | + | let pool = db::connect(&cfg.db_path).await?; | |
| 25 | + | db::migrate(&pool).await?; | |
| 26 | + | ||
| 27 | + | let prom = metrics::init(); | |
| 28 | + | let app_state = state::AppState { pool, topo, prom }; | |
| 29 | + | let app = routes::router(app_state); | |
| 30 | + | ||
| 31 | + | let addr: SocketAddr = cfg.listen.parse()?; | |
| 32 | + | tracing::info!(%addr, "sando daemon listening"); | |
| 33 | + | let listener = tokio::net::TcpListener::bind(addr).await?; | |
| 34 | + | axum::serve(listener, app).await?; | |
| 35 | + | Ok(()) | |
| 36 | + | } |
| @@ -0,0 +1,12 @@ | |||
| 1 | + | use axum::{extract::State, response::IntoResponse}; | |
| 2 | + | use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle}; | |
| 3 | + | ||
| 4 | + | pub fn init() -> PrometheusHandle { | |
| 5 | + | PrometheusBuilder::new() | |
| 6 | + | .install_recorder() | |
| 7 | + | .expect("install prometheus recorder") | |
| 8 | + | } | |
| 9 | + | ||
| 10 | + | pub async fn render(State(handle): State<PrometheusHandle>) -> impl IntoResponse { | |
| 11 | + | handle.render() | |
| 12 | + | } |
| @@ -0,0 +1,75 @@ | |||
| 1 | + | use crate::error::Result; | |
| 2 | + | use crate::state::AppState; | |
| 3 | + | use axum::extract::{Path, State, WebSocketUpgrade}; | |
| 4 | + | use axum::response::IntoResponse; | |
| 5 | + | use axum::routing::{get, post}; | |
| 6 | + | use axum::{Json, Router}; | |
| 7 | + | use serde::{Deserialize, Serialize}; | |
| 8 | + | ||
| 9 | + | pub fn router(state: AppState) -> Router { | |
| 10 | + | let prom = state.prom.clone(); | |
| 11 | + | Router::new() | |
| 12 | + | .route("/state", get(get_state)) | |
| 13 | + | .route("/promote/{tier}", post(promote)) | |
| 14 | + | .route("/rollback/{tier}", post(rollback)) | |
| 15 | + | .route("/rebuild", post(rebuild)) | |
| 16 | + | .route("/backup/fetch", post(backup_fetch)) | |
| 17 | + | .route("/events", get(events_ws)) | |
| 18 | + | .with_state(state) | |
| 19 | + | .route("/metrics", get(crate::metrics::render).with_state(prom)) | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | #[derive(Serialize)] | |
| 23 | + | struct StateView { | |
| 24 | + | tiers: Vec<TierView>, | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | #[derive(Serialize)] | |
| 28 | + | struct TierView { | |
| 29 | + | name: String, | |
| 30 | + | provisioned: bool, | |
| 31 | + | current_version: Option<String>, | |
| 32 | + | gates_passed: Vec<String>, | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | async fn get_state(State(_s): State<AppState>) -> Result<Json<StateView>> { | |
| 36 | + | todo!("read tier/version/gate state from sqlite") | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | #[derive(Deserialize)] | |
| 40 | + | struct PromoteBody { | |
| 41 | + | version: String, | |
| 42 | + | #[serde(default)] | |
| 43 | + | hotfix: bool, | |
| 44 | + | #[serde(default)] | |
| 45 | + | reset_burn_in: bool, | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | async fn promote( | |
| 49 | + | State(_s): State<AppState>, | |
| 50 | + | Path(_tier): Path<String>, | |
| 51 | + | Json(_body): Json<PromoteBody>, | |
| 52 | + | ) -> Result<Json<serde_json::Value>> { | |
| 53 | + | todo!("verify gates, atomic symlink swap on each node, sequential canary per topology") | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | async fn rollback( | |
| 57 | + | State(_s): State<AppState>, | |
| 58 | + | Path(_tier): Path<String>, | |
| 59 | + | ) -> Result<Json<serde_json::Value>> { | |
| 60 | + | todo!("flip symlink to previous release, restart unit") | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | async fn rebuild(State(_s): State<AppState>) -> Result<Json<serde_json::Value>> { | |
| 64 | + | todo!("triggered by post-receive hook; build + self-deploy + run MM gates") | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | async fn backup_fetch(State(_s): State<AppState>) -> Result<Json<serde_json::Value>> { | |
| 68 | + | todo!("pull latest prod backup from configured source to local_path") | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | async fn events_ws(ws: WebSocketUpgrade, State(_s): State<AppState>) -> impl IntoResponse { | |
| 72 | + | ws.on_upgrade(|_socket| async move { | |
| 73 | + | // tail of deploy/gate events for the TUI | |
| 74 | + | }) | |
| 75 | + | } |
| @@ -0,0 +1,10 @@ | |||
| 1 | + | use crate::topology::Topology; | |
| 2 | + | use metrics_exporter_prometheus::PrometheusHandle; | |
| 3 | + | use sqlx::SqlitePool; | |
| 4 | + | ||
| 5 | + | #[derive(Clone)] | |
| 6 | + | pub struct AppState { | |
| 7 | + | pub pool: SqlitePool, | |
| 8 | + | pub topo: Topology, | |
| 9 | + | pub prom: PrometheusHandle, | |
| 10 | + | } |
| @@ -0,0 +1,80 @@ | |||
| 1 | + | use anyhow::{Context, Result}; | |
| 2 | + | use serde::{Deserialize, Serialize}; | |
| 3 | + | use std::path::Path; | |
| 4 | + | ||
| 5 | + | #[derive(Debug, Clone, Serialize, Deserialize)] | |
| 6 | + | pub struct Topology { | |
| 7 | + | pub repo: RepoConfig, | |
| 8 | + | pub backup: BackupConfig, | |
| 9 | + | #[serde(rename = "tier")] | |
| 10 | + | pub tiers: Vec<Tier>, | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | #[derive(Debug, Clone, Serialize, Deserialize)] | |
| 14 | + | pub struct RepoConfig { | |
| 15 | + | pub bare_path: String, | |
| 16 | + | pub branch: String, | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | #[derive(Debug, Clone, Serialize, Deserialize)] | |
| 20 | + | pub struct BackupConfig { | |
| 21 | + | pub source: String, | |
| 22 | + | pub local_path: String, | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | #[derive(Debug, Clone, Serialize, Deserialize)] | |
| 26 | + | pub struct Tier { | |
| 27 | + | pub name: String, | |
| 28 | + | #[serde(default)] | |
| 29 | + | pub provisioned: bool, | |
| 30 | + | pub gates: Vec<Gate>, | |
| 31 | + | #[serde(default)] | |
| 32 | + | pub canary: CanaryPolicy, | |
| 33 | + | #[serde(default, rename = "node")] | |
| 34 | + | pub nodes: Vec<Node>, | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | #[derive(Debug, Clone, Serialize, Deserialize)] | |
| 38 | + | pub struct Node { | |
| 39 | + | pub name: String, | |
| 40 | + | pub ssh_target: String, | |
| 41 | + | pub release_root: String, | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | #[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] | |
| 45 | + | #[serde(rename_all = "snake_case")] | |
| 46 | + | pub enum CanaryPolicy { | |
| 47 | + | #[default] | |
| 48 | + | Sequential, | |
| 49 | + | Parallel, | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | #[derive(Debug, Clone, Serialize, Deserialize)] | |
| 53 | + | #[serde(tag = "kind", rename_all = "snake_case")] | |
| 54 | + | pub enum Gate { | |
| 55 | + | CargoTest, | |
| 56 | + | MigrationDryRun, | |
| 57 | + | BootSmoke, | |
| 58 | + | BurnIn { hours: u32 }, | |
| 59 | + | ManualConfirm, | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | impl Topology { | |
| 63 | + | pub fn load(path: &Path) -> Result<Self> { | |
| 64 | + | let raw = std::fs::read_to_string(path) | |
| 65 | + | .with_context(|| format!("reading topology at {}", path.display()))?; | |
| 66 | + | let topo: Topology = toml::from_str(&raw)?; | |
| 67 | + | topo.validate()?; | |
| 68 | + | Ok(topo) | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | fn validate(&self) -> Result<()> { | |
| 72 | + | anyhow::ensure!(!self.tiers.is_empty(), "topology must declare at least one tier"); | |
| 73 | + | for t in &self.tiers { | |
| 74 | + | if t.provisioned && t.nodes.is_empty() && t.name != "mm" { | |
| 75 | + | anyhow::bail!("tier {} is provisioned but has no nodes", t.name); | |
| 76 | + | } | |
| 77 | + | } | |
| 78 | + | Ok(()) | |
| 79 | + | } | |
| 80 | + | } |
| @@ -0,0 +1,69 @@ | |||
| 1 | + | # Sando topology config. | |
| 2 | + | # | |
| 3 | + | # Tiers run in declaration order. Each tier lists the gates that must pass to | |
| 4 | + | # unlock promotion *to* the next tier, the nodes it ships to, and the canary | |
| 5 | + | # policy for shipping within the tier. | |
| 6 | + | # | |
| 7 | + | # Day-one wiring: MM (local) -> A (testnot.work) -> B (prod-1). C is declared | |
| 8 | + | # but not provisioned; adding the second prod node later is a config edit | |
| 9 | + | # (set provisioned = true, fill in [[tier.node]]). | |
| 10 | + | ||
| 11 | + | [repo] | |
| 12 | + | bare_path = "/srv/sando/mnw.git" | |
| 13 | + | branch = "main" | |
| 14 | + | ||
| 15 | + | [backup] | |
| 16 | + | # Source of the prod-backup clone used by migration_dry_run on MM. | |
| 17 | + | # For localhost dev this can be a file:// path to a fixture dump. | |
| 18 | + | source = "rsync://astra/var/backups/mnw/latest.sql.gz" | |
| 19 | + | local_path = "/srv/sando/backups/latest.sql.gz" | |
| 20 | + | ||
| 21 | + | # ---- MM: local pre-staging gate ---- | |
| 22 | + | [[tier]] | |
| 23 | + | name = "mm" | |
| 24 | + | provisioned = true | |
| 25 | + | canary = "sequential" | |
| 26 | + | gates = [ | |
| 27 | + | { kind = "cargo_test" }, | |
| 28 | + | { kind = "migration_dry_run" }, | |
| 29 | + | { kind = "boot_smoke" }, | |
| 30 | + | ] | |
| 31 | + | # MM is the daemon's own host; no remote node row. | |
| 32 | + | ||
| 33 | + | # ---- A: testnot.work staging ---- | |
| 34 | + | [[tier]] | |
| 35 | + | name = "a" | |
| 36 | + | provisioned = true | |
| 37 | + | canary = "sequential" | |
| 38 | + | gates = [ | |
| 39 | + | { kind = "boot_smoke" }, | |
| 40 | + | { kind = "burn_in", hours = 48 }, | |
| 41 | + | ] | |
| 42 | + | [[tier.node]] | |
| 43 | + | name = "testnot-1" | |
| 44 | + | ssh_target = "deploy@testnot.work" | |
| 45 | + | release_root = "/opt/mnw" | |
| 46 | + | ||
| 47 | + | # ---- B: prod-1 ---- | |
| 48 | + | [[tier]] | |
| 49 | + | name = "b" | |
| 50 | + | provisioned = true | |
| 51 | + | canary = "sequential" | |
| 52 | + | gates = [ | |
| 53 | + | { kind = "boot_smoke" }, | |
| 54 | + | { kind = "manual_confirm" }, | |
| 55 | + | ] | |
| 56 | + | [[tier.node]] | |
| 57 | + | name = "prod-1" | |
| 58 | + | ssh_target = "deploy@prod-1.makenot.work" | |
| 59 | + | release_root = "/opt/mnw" | |
| 60 | + | ||
| 61 | + | # ---- C: prod-2 (declared, not yet provisioned) ---- | |
| 62 | + | [[tier]] | |
| 63 | + | name = "c" | |
| 64 | + | provisioned = false | |
| 65 | + | canary = "sequential" | |
| 66 | + | gates = [ | |
| 67 | + | { kind = "boot_smoke" }, | |
| 68 | + | ] | |
| 69 | + | # [[tier.node]] entries to be added when the second prod node ships. |
| @@ -0,0 +1,1778 @@ | |||
| 1 | + | # This file is automatically @generated by Cargo. | |
| 2 | + | # It is not intended for manual editing. | |
| 3 | + | version = 4 | |
| 4 | + | ||
| 5 | + | [[package]] | |
| 6 | + | name = "allocator-api2" | |
| 7 | + | version = "0.2.21" | |
| 8 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 9 | + | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" | |
| 10 | + | ||
| 11 | + | [[package]] | |
| 12 | + | name = "anyhow" | |
| 13 | + | version = "1.0.102" | |
| 14 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 15 | + | checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" | |
| 16 | + | ||
| 17 | + | [[package]] | |
| 18 | + | name = "atomic-waker" | |
| 19 | + | version = "1.1.2" | |
| 20 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 21 | + | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" | |
| 22 | + | ||
| 23 | + | [[package]] | |
| 24 | + | name = "base64" | |
| 25 | + | version = "0.22.1" | |
| 26 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 27 | + | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" | |
| 28 | + | ||
| 29 | + | [[package]] | |
| 30 | + | name = "bitflags" | |
| 31 | + | version = "2.11.1" | |
| 32 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 33 | + | checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" | |
| 34 | + | ||
| 35 | + | [[package]] | |
| 36 | + | name = "bumpalo" | |
| 37 | + | version = "3.20.3" | |
| 38 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 39 | + | checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" | |
| 40 | + | ||
| 41 | + | [[package]] | |
| 42 | + | name = "bytes" | |
| 43 | + | version = "1.11.1" | |
| 44 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 45 | + | checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" | |
| 46 | + | ||
| 47 | + | [[package]] | |
| 48 | + | name = "cassowary" | |
| 49 | + | version = "0.3.0" | |
| 50 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 51 | + | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" | |
| 52 | + | ||
| 53 | + | [[package]] | |
| 54 | + | name = "castaway" | |
| 55 | + | version = "0.2.4" | |
| 56 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 57 | + | checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" | |
| 58 | + | dependencies = [ | |
| 59 | + | "rustversion", | |
| 60 | + | ] | |
| 61 | + | ||
| 62 | + | [[package]] | |
| 63 | + | name = "cc" | |
| 64 | + | version = "1.2.62" | |
| 65 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 66 | + | checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" | |
| 67 | + | dependencies = [ | |
| 68 | + | "find-msvc-tools", | |
| 69 | + | "shlex", | |
| 70 | + | ] | |
| 71 | + | ||
| 72 | + | [[package]] | |
| 73 | + | name = "cfg-if" | |
| 74 | + | version = "1.0.4" | |
| 75 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 76 | + | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" | |
| 77 | + | ||
| 78 | + | [[package]] | |
| 79 | + | name = "cfg_aliases" | |
| 80 | + | version = "0.2.1" | |
| 81 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 82 | + | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" | |
| 83 | + | ||
| 84 | + | [[package]] | |
| 85 | + | name = "compact_str" | |
| 86 | + | version = "0.8.1" | |
| 87 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 88 | + | checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" | |
| 89 | + | dependencies = [ | |
| 90 | + | "castaway", | |
| 91 | + | "cfg-if", | |
| 92 | + | "itoa", | |
| 93 | + | "rustversion", | |
| 94 | + | "ryu", | |
| 95 | + | "static_assertions", | |
| 96 | + | ] | |
| 97 | + | ||
| 98 | + | [[package]] | |
| 99 | + | name = "crossterm" | |
| 100 | + | version = "0.28.1" | |
| 101 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 102 | + | checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" | |
| 103 | + | dependencies = [ | |
| 104 | + | "bitflags", | |
| 105 | + | "crossterm_winapi", | |
| 106 | + | "mio", | |
| 107 | + | "parking_lot", | |
| 108 | + | "rustix", | |
| 109 | + | "signal-hook", | |
| 110 | + | "signal-hook-mio", | |
| 111 | + | "winapi", | |
| 112 | + | ] | |
| 113 | + | ||
| 114 | + | [[package]] | |
| 115 | + | name = "crossterm_winapi" | |
| 116 | + | version = "0.9.1" | |
| 117 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 118 | + | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" | |
| 119 | + | dependencies = [ | |
| 120 | + | "winapi", | |
| 121 | + | ] | |
| 122 | + | ||
| 123 | + | [[package]] | |
| 124 | + | name = "darling" | |
| 125 | + | version = "0.23.0" | |
| 126 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 127 | + | checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" | |
| 128 | + | dependencies = [ | |
| 129 | + | "darling_core", | |
| 130 | + | "darling_macro", | |
| 131 | + | ] | |
| 132 | + | ||
| 133 | + | [[package]] | |
| 134 | + | name = "darling_core" | |
| 135 | + | version = "0.23.0" | |
| 136 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 137 | + | checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" | |
| 138 | + | dependencies = [ | |
| 139 | + | "ident_case", | |
| 140 | + | "proc-macro2", | |
| 141 | + | "quote", | |
| 142 | + | "strsim", | |
| 143 | + | "syn", | |
| 144 | + | ] | |
| 145 | + | ||
| 146 | + | [[package]] | |
| 147 | + | name = "darling_macro" | |
| 148 | + | version = "0.23.0" | |
| 149 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 150 | + | checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" | |
| 151 | + | dependencies = [ | |
| 152 | + | "darling_core", | |
| 153 | + | "quote", | |
| 154 | + | "syn", | |
| 155 | + | ] | |
| 156 | + | ||
| 157 | + | [[package]] | |
| 158 | + | name = "displaydoc" | |
| 159 | + | version = "0.2.5" | |
| 160 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 161 | + | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" | |
| 162 | + | dependencies = [ | |
| 163 | + | "proc-macro2", | |
| 164 | + | "quote", | |
| 165 | + | "syn", | |
| 166 | + | ] | |
| 167 | + | ||
| 168 | + | [[package]] | |
| 169 | + | name = "either" | |
| 170 | + | version = "1.16.0" | |
| 171 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 172 | + | checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" | |
| 173 | + | ||
| 174 | + | [[package]] | |
| 175 | + | name = "equivalent" | |
| 176 | + | version = "1.0.2" | |
| 177 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 178 | + | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" | |
| 179 | + | ||
| 180 | + | [[package]] | |
| 181 | + | name = "errno" | |
| 182 | + | version = "0.3.14" | |
| 183 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 184 | + | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" | |
| 185 | + | dependencies = [ | |
| 186 | + | "libc", | |
| 187 | + | "windows-sys 0.61.2", | |
| 188 | + | ] | |
| 189 | + | ||
| 190 | + | [[package]] | |
| 191 | + | name = "find-msvc-tools" | |
| 192 | + | version = "0.1.9" | |
| 193 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 194 | + | checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" | |
| 195 | + | ||
| 196 | + | [[package]] | |
| 197 | + | name = "foldhash" | |
| 198 | + | version = "0.1.5" | |
| 199 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 200 | + | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" | |
| 201 | + | ||
| 202 | + | [[package]] | |
| 203 | + | name = "form_urlencoded" | |
| 204 | + | version = "1.2.2" | |
| 205 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 206 | + | checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" | |
| 207 | + | dependencies = [ | |
| 208 | + | "percent-encoding", | |
| 209 | + | ] | |
| 210 | + | ||
| 211 | + | [[package]] | |
| 212 | + | name = "futures-channel" | |
| 213 | + | version = "0.3.32" | |
| 214 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 215 | + | checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" | |
| 216 | + | dependencies = [ | |
| 217 | + | "futures-core", | |
| 218 | + | ] | |
| 219 | + | ||
| 220 | + | [[package]] | |
| 221 | + | name = "futures-core" | |
| 222 | + | version = "0.3.32" | |
| 223 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 224 | + | checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" | |
| 225 | + | ||
| 226 | + | [[package]] | |
| 227 | + | name = "futures-task" | |
| 228 | + | version = "0.3.32" | |
| 229 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 230 | + | checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" | |
| 231 | + | ||
| 232 | + | [[package]] | |
| 233 | + | name = "futures-util" | |
| 234 | + | version = "0.3.32" | |
| 235 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 236 | + | checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" | |
| 237 | + | dependencies = [ | |
| 238 | + | "futures-core", | |
| 239 | + | "futures-task", | |
| 240 | + | "pin-project-lite", | |
| 241 | + | "slab", | |
| 242 | + | ] | |
| 243 | + | ||
| 244 | + | [[package]] | |
| 245 | + | name = "getrandom" | |
| 246 | + | version = "0.2.17" | |
| 247 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 248 | + | checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" | |
| 249 | + | dependencies = [ | |
| 250 | + | "cfg-if", | |
| 251 | + | "js-sys", | |
| 252 | + | "libc", | |
| 253 | + | "wasi", | |
| 254 | + | "wasm-bindgen", | |
| 255 | + | ] | |
| 256 | + | ||
| 257 | + | [[package]] | |
| 258 | + | name = "getrandom" | |
| 259 | + | version = "0.3.4" | |
| 260 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 261 | + | checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" | |
| 262 | + | dependencies = [ | |
| 263 | + | "cfg-if", | |
| 264 | + | "js-sys", | |
| 265 | + | "libc", | |
| 266 | + | "r-efi", | |
| 267 | + | "wasip2", | |
| 268 | + | "wasm-bindgen", | |
| 269 | + | ] | |
| 270 | + | ||
| 271 | + | [[package]] | |
| 272 | + | name = "hashbrown" | |
| 273 | + | version = "0.15.5" | |
| 274 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 275 | + | checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" | |
| 276 | + | dependencies = [ | |
| 277 | + | "allocator-api2", | |
| 278 | + | "equivalent", | |
| 279 | + | "foldhash", | |
| 280 | + | ] | |
| 281 | + | ||
| 282 | + | [[package]] | |
| 283 | + | name = "heck" | |
| 284 | + | version = "0.5.0" | |
| 285 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 286 | + | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" | |
| 287 | + | ||
| 288 | + | [[package]] | |
| 289 | + | name = "http" | |
| 290 | + | version = "1.4.0" | |
| 291 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 292 | + | checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" | |
| 293 | + | dependencies = [ | |
| 294 | + | "bytes", | |
| 295 | + | "itoa", | |
| 296 | + | ] | |
| 297 | + | ||
| 298 | + | [[package]] | |
| 299 | + | name = "http-body" | |
| 300 | + | version = "1.0.1" | |
| 301 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 302 | + | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" | |
| 303 | + | dependencies = [ | |
| 304 | + | "bytes", | |
| 305 | + | "http", | |
| 306 | + | ] | |
| 307 | + | ||
| 308 | + | [[package]] | |
| 309 | + | name = "http-body-util" | |
| 310 | + | version = "0.1.3" | |
| 311 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 312 | + | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" | |
| 313 | + | dependencies = [ | |
| 314 | + | "bytes", | |
| 315 | + | "futures-core", | |
| 316 | + | "http", | |
| 317 | + | "http-body", | |
| 318 | + | "pin-project-lite", | |
| 319 | + | ] | |
| 320 | + | ||
| 321 | + | [[package]] | |
| 322 | + | name = "httparse" | |
| 323 | + | version = "1.10.1" | |
| 324 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 325 | + | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" | |
| 326 | + | ||
| 327 | + | [[package]] | |
| 328 | + | name = "hyper" | |
| 329 | + | version = "1.9.0" | |
| 330 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 331 | + | checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" | |
| 332 | + | dependencies = [ | |
| 333 | + | "atomic-waker", | |
| 334 | + | "bytes", | |
| 335 | + | "futures-channel", | |
| 336 | + | "futures-core", | |
| 337 | + | "http", | |
| 338 | + | "http-body", | |
| 339 | + | "httparse", | |
| 340 | + | "itoa", | |
| 341 | + | "pin-project-lite", | |
| 342 | + | "smallvec", | |
| 343 | + | "tokio", | |
| 344 | + | "want", | |
| 345 | + | ] | |
| 346 | + | ||
| 347 | + | [[package]] | |
| 348 | + | name = "hyper-rustls" | |
| 349 | + | version = "0.27.9" | |
| 350 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 351 | + | checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" | |
| 352 | + | dependencies = [ | |
| 353 | + | "http", | |
| 354 | + | "hyper", | |
| 355 | + | "hyper-util", | |
| 356 | + | "rustls", | |
| 357 | + | "tokio", | |
| 358 | + | "tokio-rustls", | |
| 359 | + | "tower-service", | |
| 360 | + | "webpki-roots", | |
| 361 | + | ] | |
| 362 | + | ||
| 363 | + | [[package]] | |
| 364 | + | name = "hyper-util" | |
| 365 | + | version = "0.1.20" | |
| 366 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 367 | + | checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" | |
| 368 | + | dependencies = [ | |
| 369 | + | "base64", | |
| 370 | + | "bytes", | |
| 371 | + | "futures-channel", | |
| 372 | + | "futures-util", | |
| 373 | + | "http", | |
| 374 | + | "http-body", | |
| 375 | + | "hyper", | |
| 376 | + | "ipnet", | |
| 377 | + | "libc", | |
| 378 | + | "percent-encoding", | |
| 379 | + | "pin-project-lite", | |
| 380 | + | "socket2", | |
| 381 | + | "tokio", | |
| 382 | + | "tower-service", | |
| 383 | + | "tracing", | |
| 384 | + | ] | |
| 385 | + | ||
| 386 | + | [[package]] | |
| 387 | + | name = "icu_collections" | |
| 388 | + | version = "2.2.0" | |
| 389 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 390 | + | checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" | |
| 391 | + | dependencies = [ | |
| 392 | + | "displaydoc", | |
| 393 | + | "potential_utf", | |
| 394 | + | "utf8_iter", | |
| 395 | + | "yoke", | |
| 396 | + | "zerofrom", | |
| 397 | + | "zerovec", | |
| 398 | + | ] | |
| 399 | + | ||
| 400 | + | [[package]] | |
| 401 | + | name = "icu_locale_core" | |
| 402 | + | version = "2.2.0" | |
| 403 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 404 | + | checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" | |
| 405 | + | dependencies = [ | |
| 406 | + | "displaydoc", | |
| 407 | + | "litemap", | |
| 408 | + | "tinystr", | |
| 409 | + | "writeable", | |
| 410 | + | "zerovec", | |
| 411 | + | ] | |
| 412 | + | ||
| 413 | + | [[package]] | |
| 414 | + | name = "icu_normalizer" | |
| 415 | + | version = "2.2.0" | |
| 416 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 417 | + | checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" | |
| 418 | + | dependencies = [ | |
| 419 | + | "icu_collections", | |
| 420 | + | "icu_normalizer_data", | |
| 421 | + | "icu_properties", | |
| 422 | + | "icu_provider", | |
| 423 | + | "smallvec", | |
| 424 | + | "zerovec", | |
| 425 | + | ] | |
| 426 | + | ||
| 427 | + | [[package]] | |
| 428 | + | name = "icu_normalizer_data" | |
| 429 | + | version = "2.2.0" | |
| 430 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 431 | + | checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" | |
| 432 | + | ||
| 433 | + | [[package]] | |
| 434 | + | name = "icu_properties" | |
| 435 | + | version = "2.2.0" | |
| 436 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 437 | + | checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" | |
| 438 | + | dependencies = [ | |
| 439 | + | "icu_collections", | |
| 440 | + | "icu_locale_core", | |
| 441 | + | "icu_properties_data", | |
| 442 | + | "icu_provider", | |
| 443 | + | "zerotrie", | |
| 444 | + | "zerovec", | |
| 445 | + | ] | |
| 446 | + | ||
| 447 | + | [[package]] | |
| 448 | + | name = "icu_properties_data" | |
| 449 | + | version = "2.2.0" | |
| 450 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 451 | + | checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" | |
| 452 | + | ||
| 453 | + | [[package]] | |
| 454 | + | name = "icu_provider" | |
| 455 | + | version = "2.2.0" | |
| 456 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 457 | + | checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" | |
| 458 | + | dependencies = [ | |
| 459 | + | "displaydoc", | |
| 460 | + | "icu_locale_core", | |
| 461 | + | "writeable", | |
| 462 | + | "yoke", | |
| 463 | + | "zerofrom", | |
| 464 | + | "zerotrie", | |
| 465 | + | "zerovec", | |
| 466 | + | ] | |
| 467 | + | ||
| 468 | + | [[package]] | |
| 469 | + | name = "ident_case" | |
| 470 | + | version = "1.0.1" | |
| 471 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 472 | + | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" | |
| 473 | + | ||
| 474 | + | [[package]] | |
| 475 | + | name = "idna" | |
| 476 | + | version = "1.1.0" | |
| 477 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 478 | + | checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" | |
| 479 | + | dependencies = [ | |
| 480 | + | "idna_adapter", | |
| 481 | + | "smallvec", | |
| 482 | + | "utf8_iter", | |
| 483 | + | ] | |
| 484 | + | ||
| 485 | + | [[package]] | |
| 486 | + | name = "idna_adapter" | |
| 487 | + | version = "1.2.2" | |
| 488 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 489 | + | checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" | |
| 490 | + | dependencies = [ | |
| 491 | + | "icu_normalizer", | |
| 492 | + | "icu_properties", | |
| 493 | + | ] | |
| 494 | + | ||
| 495 | + | [[package]] | |
| 496 | + | name = "indoc" | |
| 497 | + | version = "2.0.7" | |
| 498 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 499 | + | checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" | |
| 500 | + | dependencies = [ |
Lines truncated
| @@ -0,0 +1,18 @@ | |||
| 1 | + | [package] | |
| 2 | + | name = "sando-tui" | |
| 3 | + | version = "0.1.0" | |
| 4 | + | edition = "2024" | |
| 5 | + | license = "MIT" | |
| 6 | + | ||
| 7 | + | [[bin]] | |
| 8 | + | name = "sando" | |
| 9 | + | path = "src/main.rs" | |
| 10 | + | ||
| 11 | + | [dependencies] | |
| 12 | + | ratatui = "0.29" | |
| 13 | + | crossterm = "0.28" | |
| 14 | + | tokio = { version = "1.50.0", features = ["macros", "rt-multi-thread", "net", "signal"] } | |
| 15 | + | reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } | |
| 16 | + | serde = { version = "1.0.228", features = ["derive"] } | |
| 17 | + | serde_json = "1" | |
| 18 | + | anyhow = "1.0.102" |
| @@ -0,0 +1,41 @@ | |||
| 1 | + | use anyhow::Result; | |
| 2 | + | use crossterm::event::{self, Event, KeyCode}; | |
| 3 | + | use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}; | |
| 4 | + | use ratatui::prelude::*; | |
| 5 | + | use ratatui::widgets::{Block, Borders, Paragraph}; | |
| 6 | + | use std::io; | |
| 7 | + | use std::time::Duration; | |
| 8 | + | ||
| 9 | + | fn main() -> Result<()> { | |
| 10 | + | enable_raw_mode()?; | |
| 11 | + | let mut stdout = io::stdout(); | |
| 12 | + | crossterm::execute!(stdout, EnterAlternateScreen)?; | |
| 13 | + | let backend = CrosstermBackend::new(stdout); | |
| 14 | + | let mut term = Terminal::new(backend)?; | |
| 15 | + | ||
| 16 | + | let res = run(&mut term); | |
| 17 | + | ||
| 18 | + | disable_raw_mode()?; | |
| 19 | + | crossterm::execute!(term.backend_mut(), LeaveAlternateScreen)?; | |
| 20 | + | term.show_cursor()?; | |
| 21 | + | res | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | fn run<B: Backend>(term: &mut Terminal<B>) -> Result<()> { | |
| 25 | + | loop { | |
| 26 | + | term.draw(|f| { | |
| 27 | + | let area = f.area(); | |
| 28 | + | let block = Block::default().title("sando").borders(Borders::ALL); | |
| 29 | + | let body = Paragraph::new("v0 scaffold. press q to quit.").block(block); | |
| 30 | + | f.render_widget(body, area); | |
| 31 | + | })?; | |
| 32 | + | ||
| 33 | + | if event::poll(Duration::from_millis(200))? { | |
| 34 | + | if let Event::Key(k) = event::read()? { | |
| 35 | + | if matches!(k.code, KeyCode::Char('q') | KeyCode::Esc) { | |
| 36 | + | return Ok(()); | |
| 37 | + | } | |
| 38 | + | } | |
| 39 | + | } | |
| 40 | + | } | |
| 41 | + | } |