├── .envrc ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .idea ├── .gitignore ├── discord.xml ├── jade.iml ├── modules.xml └── vcs.xml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── example_config.json ├── flake.lock ├── flake.nix └── src ├── args.rs ├── functions ├── base.rs ├── desktops.rs ├── locale.rs ├── mod.rs ├── network.rs ├── partition.rs ├── unakite.rs └── users.rs ├── internal ├── config.rs ├── exec.rs ├── files.rs ├── install.rs ├── mod.rs ├── returncode_eval.rs └── strings.rs ├── logging.rs └── main.rs /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Lint Code 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | formatting: 11 | name: cargo fmt 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions-rust-lang/setup-rust-toolchain@v1 16 | with: 17 | components: rustfmt 18 | - name: Format 19 | uses: actions-rust-lang/rustfmt@v1.0.0 20 | clippy: 21 | name: cargo clippy 22 | runs-on: ubuntu-latest 23 | container: ghcr.io/crystal-linux/crystal:latest 24 | steps: 25 | - uses: actions/checkout@v1 26 | - run: | 27 | sudo pacman -Syu --needed --noconfirm 28 | sudo pacman -S --noconfirm rust 29 | - uses: actions-rs/clippy-check@v1 30 | with: 31 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.direnv -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/jade.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.17.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "0.7.19" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "ansi_term" 31 | version = "0.12.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 34 | dependencies = [ 35 | "winapi", 36 | ] 37 | 38 | [[package]] 39 | name = "atty" 40 | version = "0.2.14" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 43 | dependencies = [ 44 | "hermit-abi", 45 | "libc", 46 | "winapi", 47 | ] 48 | 49 | [[package]] 50 | name = "autocfg" 51 | version = "1.1.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 54 | 55 | [[package]] 56 | name = "backtrace" 57 | version = "0.3.66" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" 60 | dependencies = [ 61 | "addr2line", 62 | "cc", 63 | "cfg-if", 64 | "libc", 65 | "miniz_oxide", 66 | "object", 67 | "rustc-demangle", 68 | ] 69 | 70 | [[package]] 71 | name = "bitflags" 72 | version = "1.3.2" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 75 | 76 | [[package]] 77 | name = "cc" 78 | version = "1.0.73" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 81 | 82 | [[package]] 83 | name = "cfg-if" 84 | version = "1.0.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 87 | 88 | [[package]] 89 | name = "clap" 90 | version = "3.2.22" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" 93 | dependencies = [ 94 | "atty", 95 | "bitflags", 96 | "clap_derive", 97 | "clap_lex", 98 | "indexmap", 99 | "once_cell", 100 | "strsim", 101 | "termcolor", 102 | "textwrap", 103 | ] 104 | 105 | [[package]] 106 | name = "clap_derive" 107 | version = "3.2.18" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" 110 | dependencies = [ 111 | "heck", 112 | "proc-macro-error", 113 | "proc-macro2", 114 | "quote", 115 | "syn", 116 | ] 117 | 118 | [[package]] 119 | name = "clap_lex" 120 | version = "0.2.4" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 123 | dependencies = [ 124 | "os_str_bytes", 125 | ] 126 | 127 | [[package]] 128 | name = "flexi_logger" 129 | version = "0.22.6" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "0c76a80dd14a27fc3d8bc696502132cb52b3f227256fd8601166c3a35e45f409" 132 | dependencies = [ 133 | "ansi_term", 134 | "atty", 135 | "glob", 136 | "lazy_static", 137 | "log", 138 | "regex", 139 | "rustversion", 140 | "thiserror", 141 | "time", 142 | ] 143 | 144 | [[package]] 145 | name = "getrandom" 146 | version = "0.2.7" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 149 | dependencies = [ 150 | "cfg-if", 151 | "libc", 152 | "wasi", 153 | ] 154 | 155 | [[package]] 156 | name = "gimli" 157 | version = "0.26.2" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" 160 | 161 | [[package]] 162 | name = "glob" 163 | version = "0.3.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 166 | 167 | [[package]] 168 | name = "hashbrown" 169 | version = "0.12.3" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 172 | 173 | [[package]] 174 | name = "heck" 175 | version = "0.4.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 178 | 179 | [[package]] 180 | name = "hermit-abi" 181 | version = "0.1.19" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 184 | dependencies = [ 185 | "libc", 186 | ] 187 | 188 | [[package]] 189 | name = "human-panic" 190 | version = "1.0.3" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "39f357a500abcbd7c5f967c1d45c8838585b36743823b9d43488f24850534e36" 193 | dependencies = [ 194 | "backtrace", 195 | "os_type", 196 | "serde", 197 | "serde_derive", 198 | "termcolor", 199 | "toml", 200 | "uuid", 201 | ] 202 | 203 | [[package]] 204 | name = "indexmap" 205 | version = "1.9.1" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 208 | dependencies = [ 209 | "autocfg", 210 | "hashbrown", 211 | ] 212 | 213 | [[package]] 214 | name = "itoa" 215 | version = "1.0.4" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 218 | 219 | [[package]] 220 | name = "jade" 221 | version = "1.2.1" 222 | dependencies = [ 223 | "clap", 224 | "flexi_logger", 225 | "human-panic", 226 | "lazy_static", 227 | "log", 228 | "serde", 229 | "serde_json", 230 | ] 231 | 232 | [[package]] 233 | name = "lazy_static" 234 | version = "1.4.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 237 | 238 | [[package]] 239 | name = "libc" 240 | version = "0.2.135" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" 243 | 244 | [[package]] 245 | name = "log" 246 | version = "0.4.17" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 249 | dependencies = [ 250 | "cfg-if", 251 | ] 252 | 253 | [[package]] 254 | name = "memchr" 255 | version = "2.5.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 258 | 259 | [[package]] 260 | name = "miniz_oxide" 261 | version = "0.5.4" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" 264 | dependencies = [ 265 | "adler", 266 | ] 267 | 268 | [[package]] 269 | name = "num_threads" 270 | version = "0.1.6" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" 273 | dependencies = [ 274 | "libc", 275 | ] 276 | 277 | [[package]] 278 | name = "object" 279 | version = "0.29.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" 282 | dependencies = [ 283 | "memchr", 284 | ] 285 | 286 | [[package]] 287 | name = "once_cell" 288 | version = "1.15.0" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 291 | 292 | [[package]] 293 | name = "os_str_bytes" 294 | version = "6.3.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" 297 | 298 | [[package]] 299 | name = "os_type" 300 | version = "2.6.0" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "e24d44c0eea30167516ed8f6daca4b5e3eebcde1bde1e4e6e08b809fb02c7ba5" 303 | dependencies = [ 304 | "regex", 305 | ] 306 | 307 | [[package]] 308 | name = "proc-macro-error" 309 | version = "1.0.4" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 312 | dependencies = [ 313 | "proc-macro-error-attr", 314 | "proc-macro2", 315 | "quote", 316 | "syn", 317 | "version_check", 318 | ] 319 | 320 | [[package]] 321 | name = "proc-macro-error-attr" 322 | version = "1.0.4" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 325 | dependencies = [ 326 | "proc-macro2", 327 | "quote", 328 | "version_check", 329 | ] 330 | 331 | [[package]] 332 | name = "proc-macro2" 333 | version = "1.0.47" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 336 | dependencies = [ 337 | "unicode-ident", 338 | ] 339 | 340 | [[package]] 341 | name = "quote" 342 | version = "1.0.21" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 345 | dependencies = [ 346 | "proc-macro2", 347 | ] 348 | 349 | [[package]] 350 | name = "regex" 351 | version = "1.6.0" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 354 | dependencies = [ 355 | "aho-corasick", 356 | "memchr", 357 | "regex-syntax", 358 | ] 359 | 360 | [[package]] 361 | name = "regex-syntax" 362 | version = "0.6.27" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 365 | 366 | [[package]] 367 | name = "rustc-demangle" 368 | version = "0.1.21" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 371 | 372 | [[package]] 373 | name = "rustversion" 374 | version = "1.0.9" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" 377 | 378 | [[package]] 379 | name = "ryu" 380 | version = "1.0.11" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 383 | 384 | [[package]] 385 | name = "serde" 386 | version = "1.0.145" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 389 | dependencies = [ 390 | "serde_derive", 391 | ] 392 | 393 | [[package]] 394 | name = "serde_derive" 395 | version = "1.0.145" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" 398 | dependencies = [ 399 | "proc-macro2", 400 | "quote", 401 | "syn", 402 | ] 403 | 404 | [[package]] 405 | name = "serde_json" 406 | version = "1.0.87" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" 409 | dependencies = [ 410 | "itoa", 411 | "ryu", 412 | "serde", 413 | ] 414 | 415 | [[package]] 416 | name = "strsim" 417 | version = "0.10.0" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 420 | 421 | [[package]] 422 | name = "syn" 423 | version = "1.0.102" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" 426 | dependencies = [ 427 | "proc-macro2", 428 | "quote", 429 | "unicode-ident", 430 | ] 431 | 432 | [[package]] 433 | name = "termcolor" 434 | version = "1.1.3" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 437 | dependencies = [ 438 | "winapi-util", 439 | ] 440 | 441 | [[package]] 442 | name = "textwrap" 443 | version = "0.15.1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" 446 | 447 | [[package]] 448 | name = "thiserror" 449 | version = "1.0.37" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 452 | dependencies = [ 453 | "thiserror-impl", 454 | ] 455 | 456 | [[package]] 457 | name = "thiserror-impl" 458 | version = "1.0.37" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 461 | dependencies = [ 462 | "proc-macro2", 463 | "quote", 464 | "syn", 465 | ] 466 | 467 | [[package]] 468 | name = "time" 469 | version = "0.3.9" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" 472 | dependencies = [ 473 | "itoa", 474 | "libc", 475 | "num_threads", 476 | "time-macros", 477 | ] 478 | 479 | [[package]] 480 | name = "time-macros" 481 | version = "0.2.4" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" 484 | 485 | [[package]] 486 | name = "toml" 487 | version = "0.5.9" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 490 | dependencies = [ 491 | "serde", 492 | ] 493 | 494 | [[package]] 495 | name = "unicode-ident" 496 | version = "1.0.5" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 499 | 500 | [[package]] 501 | name = "uuid" 502 | version = "0.8.2" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" 505 | dependencies = [ 506 | "getrandom", 507 | ] 508 | 509 | [[package]] 510 | name = "version_check" 511 | version = "0.9.4" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 514 | 515 | [[package]] 516 | name = "wasi" 517 | version = "0.11.0+wasi-snapshot-preview1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 520 | 521 | [[package]] 522 | name = "winapi" 523 | version = "0.3.9" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 526 | dependencies = [ 527 | "winapi-i686-pc-windows-gnu", 528 | "winapi-x86_64-pc-windows-gnu", 529 | ] 530 | 531 | [[package]] 532 | name = "winapi-i686-pc-windows-gnu" 533 | version = "0.4.0" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 536 | 537 | [[package]] 538 | name = "winapi-util" 539 | version = "0.1.5" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 542 | dependencies = [ 543 | "winapi", 544 | ] 545 | 546 | [[package]] 547 | name = "winapi-x86_64-pc-windows-gnu" 548 | version = "0.4.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 551 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jade" 3 | description = "The new installer for crystal linux, contains backend and TUI" 4 | authors = ["Amy "] 5 | version = "1.2.1" 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | 11 | [dependencies] 12 | clap = {version = "3.1.10", features = ["derive"] } 13 | serde_json = "1.0.59" 14 | serde = { version = "1.0.117", features = [ "derive" ] } 15 | log = "0.4.16" 16 | flexi_logger = {version = "0.22.3", features = ["colors"] } 17 | lazy_static = "1.4.0" 18 | human-panic = "1.0.3" 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **THIS PROJECT HAS BEEN MIGRATED TO GITLAB** 2 | Please make any PRs here, rather than to the GitHub: https://git.getcryst.al/crystal 3 | 4 | # REPOSITORIES HERE ARE **OUTDATED**, GO TO THE LINK BELOW 5 | # ---- > https://git.getcryst.al/crystal < ---- 6 | 7 |

8 | 9 | Logo 10 | 11 |

12 |

Jade

13 |

14 | License 15 | GitHub isses 16 | GitHub pull requests
17 | Discord 18 | The 19 | Mastodon Follow 20 | Twitter Follow 21 |

22 | 23 |

Jade is an installer backend for crystal linux.

24 | 25 | ## Backend usage 26 | 27 | ### autopartition the drive 28 | ```sh 29 | # autopartition /dev/sda with efi enabled 30 | jade partition auto /dev/sda --efi 31 | 32 | # autopartition /dev/nvmen0 with efi disabled 33 | jade partition auto /dev/nvmen0 34 | ``` 35 | 36 | ### install base packages 37 | ```sh 38 | jade install-base 39 | ``` 40 | 41 | ### install bootloader 42 | ```sh 43 | # install as efi with esp being /boot/efi 44 | jade bootloader grub-efi /boot/efi 45 | 46 | # install as legacy on /dev/sda 47 | jade bootloader grub-legacy /dev/sda 48 | ``` 49 | 50 | ### generate fstab 51 | ```sh 52 | jade genfstab 53 | ``` 54 | 55 | ### configuring locale settings 56 | ```sh 57 | # set the keyboard layout to colemak, the timezone to Europe/Berlin and set en_US.UTF-8 as the locale 58 | jade locale colemak Europe/Berlin en_US.UTF-8 UTF-8 59 | ``` 60 | 61 | ### configure network settings 62 | ```sh 63 | # set the hostname to getcryst.al with ipv6 disabled 64 | jade networking getcryst.al 65 | 66 | # set the hostname to getcryst.al with ipv6 enabled 67 | jade networking getcryst.al --ipv6 68 | ``` 69 | 70 | ### setup zramd 71 | ```sh 72 | # install and enable zramd 73 | jade zramd 74 | ``` 75 | 76 | ### configure users 77 | ```sh 78 | # make a new user called nonRootHaver, without sudo, easytohack as the password and bash as the default shell 79 | jade users new-user nonRootHaver easytohack bash 80 | 81 | # make a user called rootHaver, with sudo, omgsosuperhardtohack as the password and fish as the default shell 82 | jade users new-user rootHaver omgsuperhardtohack fish --hasroot 83 | ``` 84 | 85 | ### set root password 86 | ```sh 87 | # set the root password to 'muchSecurity,veryHardToHack' 88 | jade users root-password muchSecurity,veryHardToHack 89 | ``` 90 | 91 | ### install a desktop environment 92 | ```sh 93 | # install onyx 94 | jade desktops onyx 95 | 96 | # install gnome 97 | jade desktops gnome 98 | ``` 99 | 100 | ### setup timeshift 101 | ```sh 102 | jade setup-timeshift 103 | ``` 104 | 105 | ### setup flatpak 106 | ```sh 107 | jade flatpak 108 | ``` 109 | 110 | ### debug logging 111 | 112 | debug messages: 113 | ```sh 114 | jade -v 115 | ``` 116 | 117 | traces: 118 | ```sh 119 | jade -vv 120 | ``` 121 | 122 | ## How to build: 123 | 124 | Tested on latest Cargo (1.60.0-nightly) 125 | 126 |
127 | 128 | #### Debug/development builds 129 | 130 | - `cargo build` 131 | 132 | #### Optimised/release builds 133 | 134 | - `cargo build --release` 135 | 136 | ## Non-secret Secret 137 | echo "JADE_UWU=true" >> ~/.zshrc
138 | echo "JADE_UWU=true" >> ~/.bashrc
139 | set -Ux JADE_UWU true
140 |
141 | if you want to have your log and crash output be “cute” 142 | -------------------------------------------------------------------------------- /example_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "partition": { 3 | "device": "sda", 4 | "mode": "Auto", 5 | "efi": true, 6 | "partitions": [ 7 | "/mnt/home:/dev/sdb2:btrfs" // This would be partition /dev/sdb2, formatted with btrfs mounted at /home 8 | ] // this is only needed for manual partitioning, it would contain all the partitions for jade to use and the filesystem as well as mountpoint 9 | }, 10 | "bootloader": { 11 | "type": "grub-efi", // for legacy this would be grub-legacy 12 | "location": "/boot/efi" // for efi this is the esp directory, for legacy boot this would be the device on which to install grub on 13 | }, 14 | "locale": { 15 | "locale": [ 16 | "en_US.UTF-8 UTF-8" 17 | ], 18 | "keymap": "colemak", 19 | "timezone": "Europe/Berlin" 20 | }, 21 | "networking": { 22 | "hostname": "jade-test", 23 | "ipv6": false 24 | }, 25 | "users": [ 26 | { 27 | "name": "jade", 28 | "password": "TaCVRgYCAHag6", // The password has to be encrypted with `openssl passwd -crypt ` 29 | "hasroot": true, 30 | "shell": "bash" // this can be either bash, csh, fish, tcsh or zsh. If a value is not recognized the default will be bash 31 | }, 32 | { // Multiple users can be specified by just following this format 33 | "name": "jade2", 34 | "password": "TzSMi3EezsXZM", 35 | "hasroot": false, 36 | "shell": "fish" 37 | } 38 | ], 39 | "rootpass": "3IwCDE/t39wuQ", // Same as other passwords, this has to be encrypted with `openssl passwd -crypt ` 40 | "desktop": "onyx", // The desktop environment to install can be onyx, gnome, kde, mate, cinnamon, xfce, budgie, enlightenment, etc. for a full list check https://github.com/crystal-linux/jade/blob/main/src/internal/config.rs#L162 41 | "timeshift": true, // Whether to enable timeshift as well as timeshift-autosnap, note that this may only work with root on btrfs 42 | "zramd": true, // Whether to enable zramd 43 | "extra_packages": [ 44 | "firefox", 45 | "vim", 46 | "git", 47 | "tmux" 48 | ], 49 | "unakite": { 50 | "enable": false, // Whether to install the recorvery partition, note that this currently is just a secondary smaller crystal installation 51 | "root": "/dev/sda2", // The root partition for unakite 52 | "oldroot": "/dev/sda3", // The root partition that the main crystal installation uses 53 | "efidir": "/boot/efi", // The esp mountpoint in unakite, note that this is only read when using it on an efi system 54 | "bootdev": "/dev/sda1" // the partition for the boot/efi partition 55 | }, 56 | "kernel": "linux" // which kernel to install, available options are linux, linux-zen, linux-lts, linux-hardened. When an unknown option is passed it will default to linux 57 | } 58 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "fenix": { 4 | "inputs": { 5 | "nixpkgs": [ 6 | "nixpkgs" 7 | ], 8 | "rust-analyzer-src": "rust-analyzer-src" 9 | }, 10 | "locked": { 11 | "lastModified": 1662533965, 12 | "narHash": "sha256-56td0N02VeIOuzyesNWLCwTcoVwP0wuQauUbzh6RpMg=", 13 | "owner": "nix-community", 14 | "repo": "fenix", 15 | "rev": "b5b70debeb3c2f93186eaed7de13674b9783b9d2", 16 | "type": "github" 17 | }, 18 | "original": { 19 | "owner": "nix-community", 20 | "repo": "fenix", 21 | "type": "github" 22 | } 23 | }, 24 | "naersk": { 25 | "inputs": { 26 | "nixpkgs": [ 27 | "nixpkgs" 28 | ] 29 | }, 30 | "locked": { 31 | "lastModified": 1662220400, 32 | "narHash": "sha256-9o2OGQqu4xyLZP9K6kNe1pTHnyPz0Wr3raGYnr9AIgY=", 33 | "owner": "nix-community", 34 | "repo": "naersk", 35 | "rev": "6944160c19cb591eb85bbf9b2f2768a935623ed3", 36 | "type": "github" 37 | }, 38 | "original": { 39 | "owner": "nix-community", 40 | "repo": "naersk", 41 | "type": "github" 42 | } 43 | }, 44 | "nixpkgs": { 45 | "locked": { 46 | "lastModified": 1662410893, 47 | "narHash": "sha256-jmZjdA/n92OpUdaEjU7QJNsa1np+u+JWDj300x3K3XY=", 48 | "owner": "nixos", 49 | "repo": "nixpkgs", 50 | "rev": "680818a51b7fb6886db8900e2fb1582305b45f77", 51 | "type": "github" 52 | }, 53 | "original": { 54 | "owner": "nixos", 55 | "repo": "nixpkgs", 56 | "type": "github" 57 | } 58 | }, 59 | "root": { 60 | "inputs": { 61 | "fenix": "fenix", 62 | "naersk": "naersk", 63 | "nixpkgs": "nixpkgs", 64 | "utils": "utils" 65 | } 66 | }, 67 | "rust-analyzer-src": { 68 | "flake": false, 69 | "locked": { 70 | "lastModified": 1662377094, 71 | "narHash": "sha256-0bvOQxEe8nzk/VlhHBrUn/Mz3DlE92Us7JqveVjTe0A=", 72 | "owner": "rust-lang", 73 | "repo": "rust-analyzer", 74 | "rev": "6dfd8aebdfa1ee1824446f01daf5bdb229b32f92", 75 | "type": "github" 76 | }, 77 | "original": { 78 | "owner": "rust-lang", 79 | "ref": "nightly", 80 | "repo": "rust-analyzer", 81 | "type": "github" 82 | } 83 | }, 84 | "utils": { 85 | "locked": { 86 | "lastModified": 1659877975, 87 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", 88 | "owner": "numtide", 89 | "repo": "flake-utils", 90 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", 91 | "type": "github" 92 | }, 93 | "original": { 94 | "owner": "numtide", 95 | "repo": "flake-utils", 96 | "type": "github" 97 | } 98 | } 99 | }, 100 | "root": "root", 101 | "version": 7 102 | } 103 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:nixos/nixpkgs"; 4 | utils.url = "github:numtide/flake-utils"; 5 | naersk = { 6 | url = "github:nix-community/naersk"; 7 | inputs.nixpkgs.follows = "nixpkgs"; 8 | }; 9 | fenix = { 10 | url = "github:nix-community/fenix"; 11 | inputs.nixpkgs.follows = "nixpkgs"; 12 | }; 13 | }; 14 | 15 | outputs = { 16 | self, 17 | nixpkgs, 18 | utils, 19 | naersk, 20 | fenix, 21 | ... 22 | }: 23 | utils.lib.eachDefaultSystem (system: let 24 | pkgs = nixpkgs.legacyPackages."${system}"; 25 | toolchain = with fenix.packages."${system}"; 26 | combine [ 27 | latest.rustc 28 | latest.cargo 29 | latest.clippy 30 | latest.rustfmt 31 | latest.rust-analyzer 32 | latest.rust-src 33 | ]; 34 | naersk-lib = naersk.lib."${system}".override { 35 | cargo = toolchain; 36 | rustc = toolchain; 37 | clippy = toolchain; 38 | rustfmt = toolchain; 39 | rust-analyzer = toolchain; 40 | rust-src = toolchain; 41 | }; 42 | in rec 43 | { 44 | packages.jade = naersk-lib.buildPackage { 45 | pname = "Jade"; 46 | root = ./.; 47 | }; 48 | 49 | packages.default = packages.jade; 50 | 51 | apps.jade = utils.lib.mkApp { 52 | drv = packages.jade; 53 | }; 54 | 55 | apps.default = apps.jade; 56 | 57 | devShells.default = pkgs.mkShell { 58 | nativeBuildInputs = [ 59 | toolchain 60 | ]; 61 | # For rust-analyzer 62 | RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; 63 | }; 64 | 65 | formatter = pkgs.alejandra; 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use clap::{ArgEnum, Args, Parser, Subcommand}; 2 | use serde::{Deserialize, Serialize}; 3 | use std::path::PathBuf; 4 | 5 | #[derive(Debug, Parser)] 6 | #[clap(name="jade", version=env!("CARGO_PKG_VERSION"), about=env!("CARGO_PKG_DESCRIPTION"), author=env!("CARGO_PKG_AUTHORS"))] 7 | pub struct Opt { 8 | #[clap(subcommand)] 9 | pub command: Command, 10 | 11 | #[clap(long, short, parse(from_occurrences))] 12 | pub verbose: usize, 13 | } 14 | 15 | #[derive(Debug, Subcommand)] 16 | pub enum Command { 17 | /// Partition the install destination 18 | #[clap(name = "partition")] 19 | Partition(PartitionArgs), 20 | 21 | /// Install base packages, optionally define a different kernel 22 | #[clap(name = "install-base")] 23 | InstallBase(InstallBaseArgs), 24 | 25 | /// Generate fstab file for mounting partitions 26 | #[clap(name = "genfstab")] 27 | GenFstab, 28 | 29 | /// Setup Timeshift 30 | #[clap(name = "setup-timeshift")] 31 | SetupTimeshift, 32 | 33 | /// Install the bootloader 34 | #[clap(name = "bootloader")] 35 | Bootloader { 36 | #[clap(subcommand)] 37 | subcommand: BootloaderSubcommand, 38 | }, 39 | 40 | /// Set locale 41 | #[clap(name = "locale")] 42 | Locale(LocaleArgs), 43 | 44 | /// Set up networking 45 | #[clap(name = "networking")] 46 | Networking(NetworkingArgs), 47 | 48 | /// Set up zramd 49 | #[clap(name = "zramd")] 50 | Zram, 51 | 52 | /// Configure users and passwords 53 | #[clap(name = "users")] 54 | Users { 55 | #[clap(subcommand)] 56 | subcommand: UsersSubcommand, 57 | }, 58 | 59 | /// Install the Nix package manager 60 | #[clap(name = "nix")] 61 | Nix, 62 | 63 | /// Install Flatpak and enable FlatHub 64 | #[clap(name = "flatpak")] 65 | Flatpak, 66 | 67 | /// Setup Unakite 68 | #[clap(name = "unakite")] 69 | Unakite(UnakiteArgs), 70 | 71 | /// Read Jade installation config 72 | #[clap(name = "config")] 73 | Config { 74 | /// The config file to read 75 | config: PathBuf, 76 | }, 77 | 78 | /// Install a graphical desktop 79 | #[clap(name = "desktops")] 80 | Desktops { 81 | /// The desktop setup to use 82 | #[clap(arg_enum)] 83 | desktop: DesktopSetup, 84 | }, 85 | } 86 | 87 | #[derive(Debug, Args)] 88 | pub struct PartitionArgs { 89 | /// If jade should automatically partition (mode = auto) 90 | /// or the user manually partitioned it (mode = manual) 91 | #[clap(arg_enum)] 92 | pub mode: PartitionMode, 93 | 94 | /// The device to partition 95 | #[clap(required_if_eq("mode", "PartitionMode::Auto"))] 96 | pub device: PathBuf, 97 | 98 | /// If the install destination should be partitioned with EFI 99 | #[clap(long)] 100 | pub efi: bool, 101 | 102 | #[clap(long)] 103 | pub unakite: bool, 104 | 105 | /// The partitions to use for manual partitioning 106 | #[clap(required_if_eq("mode", "Partition::Manual"), parse(try_from_str = parse_partitions))] 107 | pub partitions: Vec, 108 | } 109 | 110 | #[derive(Debug, Args)] 111 | pub struct InstallBaseArgs { 112 | #[clap(long)] 113 | pub kernel: String, 114 | } 115 | 116 | #[derive(Debug, Args)] 117 | pub struct UnakiteArgs { 118 | /// Root device of Unakite 119 | #[clap(long)] 120 | pub root: String, 121 | /// Root device of Crystal 122 | #[clap(long)] 123 | pub oldroot: String, 124 | /// Whether the system is an EFI system 125 | #[clap(long)] 126 | pub efi: bool, 127 | /// Boot directory (if not EFI), or EFI directory 128 | #[clap(long)] 129 | pub efidir: String, 130 | /// Blockdev of boot device 131 | #[clap(long)] 132 | pub bootdev: String, 133 | } 134 | 135 | #[derive(Debug)] 136 | pub struct Partition { 137 | pub mountpoint: String, 138 | pub blockdevice: String, 139 | pub filesystem: String, 140 | } 141 | 142 | impl Partition { 143 | pub fn new(mountpoint: String, blockdevice: String, filesystem: String) -> Self { 144 | Self { 145 | mountpoint, 146 | blockdevice, 147 | filesystem, 148 | } 149 | } 150 | } 151 | 152 | pub fn parse_partitions(s: &str) -> Result { 153 | println!("{}", s); 154 | Ok(Partition::new( 155 | s.split(':').collect::>()[0].to_string(), 156 | s.split(':').collect::>()[1].to_string(), 157 | s.split(':').collect::>()[2].to_string(), 158 | )) 159 | } 160 | 161 | #[derive(Debug, ArgEnum, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] 162 | pub enum PartitionMode { 163 | #[clap(name = "auto")] 164 | Auto, 165 | #[clap(name = "manual")] 166 | Manual, 167 | } 168 | 169 | #[derive(Debug, Subcommand)] 170 | pub enum BootloaderSubcommand { 171 | /// Install GRUB in EFI mode 172 | #[clap(name = "grub-efi")] 173 | GrubEfi { 174 | /// The directory to install the EFI bootloader to 175 | efidir: PathBuf, 176 | }, 177 | 178 | /// Install GRUB in legacy (BIOS) mode 179 | #[clap(name = "grub-legacy")] 180 | GrubLegacy { 181 | /// The device to install the bootloader to 182 | device: PathBuf, 183 | }, 184 | } 185 | 186 | #[derive(Debug, Args)] 187 | pub struct LocaleArgs { 188 | /// The keyboard layout to use 189 | pub keyboard: String, 190 | 191 | /// The timezone to use 192 | pub timezone: String, 193 | 194 | /// The locales to set 195 | pub locales: Vec, 196 | } 197 | 198 | #[derive(Debug, Args)] 199 | pub struct NetworkingArgs { 200 | /// The hostname to assign to the system 201 | pub hostname: String, 202 | 203 | /// Whether IPv6 loopback should be enabled 204 | #[clap(long)] 205 | pub ipv6: bool, 206 | } 207 | 208 | #[derive(Debug, Subcommand)] 209 | pub enum UsersSubcommand { 210 | /// Create a new user 211 | #[clap(name="new-user", aliases=&["newUser"])] 212 | NewUser(NewUserArgs), 213 | 214 | /// Set the password of the root user 215 | #[clap(name="root-password", aliases=&["root-pass", "rootPass"])] 216 | RootPass { 217 | /// The password to set. NOTE: Takes hashed password, use `openssl passwd -1 ` to generate the hash. 218 | password: String, 219 | }, 220 | } 221 | 222 | #[derive(Debug, Args)] 223 | pub struct NewUserArgs { 224 | /// The name of the user to create 225 | pub username: String, 226 | 227 | /// If the user should have root privileges 228 | #[clap(long, aliases=&["has-root", "sudoer", "root"])] 229 | pub hasroot: bool, 230 | 231 | /// The password to set. NOTE: Takes hashed password, use `openssl passwd -6 ` to generate the hash. 232 | /// When not providing a password openssl jumps into an interactive masked input mode allowing you to hide your password 233 | /// from the terminal history. 234 | pub password: String, 235 | 236 | /// The shell to use for the user. The current options are bash, csh, fish, tcsh, and zsh. 237 | /// If a shell is not specified or unknown, it defaults to fish. 238 | pub shell: String, 239 | } 240 | 241 | #[derive(Debug, ArgEnum, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] 242 | pub enum DesktopSetup { 243 | #[clap(name = "onyx")] 244 | Onyx, 245 | 246 | #[clap(name = "gnome")] 247 | Gnome, 248 | 249 | #[clap(name = "kde", aliases = ["plasma"])] 250 | Kde, 251 | 252 | #[clap(name = "budgie")] 253 | Budgie, 254 | 255 | #[clap(name = "cinnamon")] 256 | Cinnamon, 257 | 258 | #[clap(name = "mate")] 259 | Mate, 260 | 261 | #[clap(name = "xfce")] 262 | Xfce, 263 | 264 | #[clap(name = "enlightenment")] 265 | Enlightenment, 266 | 267 | #[clap(name = "lxqt")] 268 | Lxqt, 269 | 270 | #[clap(name = "sway")] 271 | Sway, 272 | 273 | #[clap(name = "i3gaps")] 274 | I3gaps, 275 | 276 | #[clap(name = "herbstluftwm")] 277 | Herbstluftwm, 278 | 279 | #[clap(name = "awesome")] 280 | Awesome, 281 | 282 | #[clap(name = "bspwm")] 283 | Bspwm, 284 | 285 | #[clap(name = "None/DIY")] 286 | None, 287 | } 288 | -------------------------------------------------------------------------------- /src/functions/base.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::exec::*; 2 | use crate::internal::files::append_file; 3 | use crate::internal::*; 4 | use log::warn; 5 | use std::path::PathBuf; 6 | 7 | pub fn install_base_packages(kernel: String) { 8 | std::fs::create_dir_all("/mnt/etc").unwrap(); 9 | let kernel_to_install = if kernel.is_empty() { 10 | "linux" 11 | } else { 12 | match kernel.as_str() { 13 | "linux" => "linux", 14 | "linux-lts" => "linux-lts", 15 | "linux-zen" => "linux-zen", 16 | "linux-hardened" => "linux-hardened", 17 | _ => { 18 | warn!("Unknown kernel: {}, using default instead", kernel); 19 | "linux" 20 | } 21 | } 22 | }; 23 | install::install(vec![ 24 | // Base Arch 25 | "base", 26 | kernel_to_install, 27 | "linux-firmware", 28 | "systemd-sysvcompat", 29 | "networkmanager", 30 | "man-db", 31 | "man-pages", 32 | "texinfo", 33 | "nano", 34 | "sudo", 35 | "curl", 36 | "archlinux-keyring", 37 | // Base Crystal 38 | "crystal-core", 39 | "crystal-branding", 40 | // Extra goodies 41 | "neofetch", 42 | "btrfs-progs", 43 | "which", 44 | "pkg-warner", 45 | "base-devel", 46 | // Fonts 47 | "noto-fonts", 48 | "noto-fonts-emoji", 49 | "noto-fonts-cjk", 50 | "noto-fonts-extra", 51 | "ttf-nerd-fonts-symbols-common", 52 | // Common packages for all desktops 53 | "xterm", 54 | "pipewire", 55 | "pipewire-pulse", 56 | "pipewire-alsa", 57 | "pipewire-jack", 58 | ]); 59 | files::copy_file("/etc/pacman.conf", "/mnt/etc/pacman.conf"); 60 | } 61 | 62 | pub fn genfstab() { 63 | exec_eval( 64 | exec( 65 | "bash", 66 | vec![ 67 | String::from("-c"), 68 | String::from("genfstab -U /mnt >> /mnt/etc/fstab"), 69 | ], 70 | ), 71 | "Generate fstab", 72 | ); 73 | } 74 | 75 | pub fn install_bootloader_efi(efidir: PathBuf) { 76 | install::install(vec![ 77 | "grub", 78 | "efibootmgr", 79 | "grub-btrfs", 80 | "crystal-grub-theme", 81 | "os-prober", 82 | "crystal-branding", 83 | ]); 84 | let efidir = std::path::Path::new("/mnt").join(efidir); 85 | let efi_str = efidir.to_str().unwrap(); 86 | if !std::path::Path::new(&format!("/mnt{efi_str}")).exists() { 87 | crash(format!("The efidir {efidir:?} doesn't exist"), 1); 88 | } 89 | exec_eval( 90 | exec_chroot( 91 | "grub-install", 92 | vec![ 93 | String::from("--target=x86_64-efi"), 94 | format!("--efi-directory={}", efi_str), 95 | String::from("--bootloader-id=crystal"), 96 | String::from("--removable"), 97 | ], 98 | ), 99 | "install grub as efi with --removable", 100 | ); 101 | exec_eval( 102 | exec_chroot( 103 | "grub-install", 104 | vec![ 105 | String::from("--target=x86_64-efi"), 106 | format!("--efi-directory={}", efi_str), 107 | String::from("--bootloader-id=crystal"), 108 | ], 109 | ), 110 | "install grub as efi without --removable", 111 | ); 112 | files_eval( 113 | append_file( 114 | "/mnt/etc/default/grub", 115 | "GRUB_THEME=\"/usr/share/grub/themes/crystal/theme.txt\"", 116 | ), 117 | "enable crystal grub theme", 118 | ); 119 | exec_eval( 120 | exec_chroot( 121 | "grub-mkconfig", 122 | vec![String::from("-o"), String::from("/boot/grub/grub.cfg")], 123 | ), 124 | "create grub.cfg", 125 | ); 126 | } 127 | 128 | pub fn install_bootloader_legacy(device: PathBuf) { 129 | install::install(vec![ 130 | "grub", 131 | "grub-btrfs", 132 | "crystal-grub-theme", 133 | "os-prober", 134 | "crystal-branding", 135 | ]); 136 | if !device.exists() { 137 | crash(format!("The device {device:?} does not exist"), 1); 138 | } 139 | let device = device.to_string_lossy().to_string(); 140 | exec_eval( 141 | exec_chroot( 142 | "grub-install", 143 | vec![String::from("--target=i386-pc"), device], 144 | ), 145 | "install grub as legacy", 146 | ); 147 | files_eval( 148 | append_file( 149 | "/mnt/etc/default/grub", 150 | "GRUB_THEME=\"/usr/share/grub/themes/crystal/theme.txt\"", 151 | ), 152 | "enable crystal grub theme", 153 | ); 154 | exec_eval( 155 | exec_chroot( 156 | "grub-mkconfig", 157 | vec![String::from("-o"), String::from("/boot/grub/grub.cfg")], 158 | ), 159 | "create grub.cfg", 160 | ); 161 | } 162 | 163 | pub fn setup_timeshift() { 164 | install(vec!["timeshift", "timeshift-autosnap"]); 165 | exec_eval( 166 | exec_chroot("timeshift", vec![String::from("--btrfs")]), 167 | "setup timeshift", 168 | ) 169 | } 170 | 171 | pub fn install_homemgr() { 172 | install(vec!["nix"]); 173 | } 174 | 175 | pub fn install_flatpak() { 176 | install(vec!["flatpak"]); 177 | exec_eval( 178 | exec_chroot( 179 | "flatpak", 180 | vec![ 181 | String::from("remote-add"), 182 | String::from("--if-not-exists"), 183 | String::from("flathub"), 184 | String::from("https://flathub.org/repo/flathub.flatpakrepo"), 185 | ], 186 | ), 187 | "add flathub remote", 188 | ) 189 | } 190 | 191 | pub fn install_zram() { 192 | install(vec!["zramd"]); 193 | exec_eval( 194 | exec_chroot( 195 | "systemctl", 196 | vec![String::from("enable"), String::from("zramd")], 197 | ), 198 | "Enable zramd service", 199 | ); 200 | } 201 | -------------------------------------------------------------------------------- /src/functions/desktops.rs: -------------------------------------------------------------------------------- 1 | use crate::args::DesktopSetup; 2 | use crate::internal::exec::*; 3 | use crate::internal::*; 4 | 5 | pub fn install_desktop_setup(desktop_setup: DesktopSetup) { 6 | log::debug!("Installing {:?}", desktop_setup); 7 | match desktop_setup { 8 | DesktopSetup::Onyx => install_onyx(), 9 | DesktopSetup::Gnome => install_gnome(), 10 | DesktopSetup::Kde => install_kde(), 11 | DesktopSetup::Budgie => install_budgie(), 12 | DesktopSetup::Cinnamon => install_cinnamon(), 13 | DesktopSetup::Mate => install_mate(), 14 | DesktopSetup::Xfce => install_xfce(), 15 | DesktopSetup::Enlightenment => install_enlightenment(), 16 | DesktopSetup::Lxqt => install_lxqt(), 17 | DesktopSetup::Sway => install_sway(), 18 | DesktopSetup::I3gaps => install_i3gaps(), 19 | DesktopSetup::Herbstluftwm => install_herbstluftwm(), 20 | DesktopSetup::Awesome => install_awesome(), 21 | DesktopSetup::Bspwm => install_bspwm(), 22 | DesktopSetup::None => log::debug!("No desktop setup selected"), 23 | } 24 | install_networkmanager(); 25 | } 26 | 27 | fn install_networkmanager() { 28 | install(vec!["networkmanager"]); 29 | exec_eval( 30 | exec_chroot( 31 | "systemctl", 32 | vec![String::from("enable"), String::from("NetworkManager")], 33 | ), 34 | "Enable network manager", 35 | ); 36 | } 37 | 38 | fn install_bspwm() { 39 | install(vec![ 40 | "xorg", 41 | "bspwm", 42 | "sxhkd", 43 | "xdo", 44 | "lightdm", 45 | "lightdm-gtk-greeter", 46 | "lightdm-gtk-greeter-settings", 47 | ]); 48 | files_eval( 49 | files::append_file( 50 | "/mnt/etc/lightdm/lightdm.conf", 51 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 52 | ), 53 | "Add lightdm greeter", 54 | ); 55 | enable_dm("lightdm"); 56 | } 57 | 58 | fn install_awesome() { 59 | install(vec![ 60 | "xorg", 61 | "awesome", 62 | "dex", 63 | "rlwrap", 64 | "vicious", 65 | "lightdm", 66 | "lightdm-gtk-greeter", 67 | "lightdm-gtk-greeter-settings", 68 | ]); 69 | files_eval( 70 | files::append_file( 71 | "/mnt/etc/lightdm/lightdm.conf", 72 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 73 | ), 74 | "Add lightdm greeter", 75 | ); 76 | enable_dm("lightdm"); 77 | } 78 | 79 | fn install_herbstluftwm() { 80 | install(vec![ 81 | "xorg", 82 | "herbstluftwm", 83 | "dmenu", 84 | "dzen2", 85 | "xorg-xsetroot", 86 | "lightdm", 87 | "lightdm-gtk-greeter", 88 | "lightdm-gtk-greeter-settings", 89 | ]); 90 | files_eval( 91 | files::append_file( 92 | "/mnt/etc/lightdm/lightdm.conf", 93 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 94 | ), 95 | "Add lightdm greeter", 96 | ); 97 | enable_dm("lightdm"); 98 | } 99 | 100 | fn install_i3gaps() { 101 | install(vec![ 102 | "xorg", 103 | "i3-gaps", 104 | "dmenu", 105 | "i3lock", 106 | "i3status", 107 | "rxvt-unicode", 108 | "lightdm", 109 | "lightdm-gtk-greeter", 110 | "lightdm-gtk-greeter-settings", 111 | ]); 112 | files_eval( 113 | files::append_file( 114 | "/mnt/etc/lightdm/lightdm.conf", 115 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 116 | ), 117 | "Add lightdm greeter", 118 | ); 119 | enable_dm("lightdm"); 120 | } 121 | 122 | fn install_sway() { 123 | install(vec![ 124 | "xorg-xwayland", 125 | "sway", 126 | "bemenu", 127 | "foot", 128 | "mako", 129 | "polkit", 130 | "swaybg", 131 | "pipewire", 132 | "pipewire-pulse", 133 | "pipewire-alsa", 134 | "pipewire-jack", 135 | "sddm", 136 | ]); 137 | enable_dm("sddm"); 138 | } 139 | 140 | fn install_lxqt() { 141 | install(vec![ 142 | "xorg", 143 | "lxqt", 144 | "breeze-icons", 145 | "nm-tray", 146 | "xscreensaver", 147 | "pipewire", 148 | "pipewire-pulse", 149 | "pipewire-alsa", 150 | "pipewire-jack", 151 | "sddm", 152 | ]); 153 | enable_dm("sddm"); 154 | } 155 | 156 | fn install_enlightenment() { 157 | install(vec![ 158 | "xorg", 159 | "enlightenment", 160 | "terminology", 161 | "pipewire", 162 | "pipewire-pulse", 163 | "pipewire-alsa", 164 | "pipewire-jack", 165 | "lightdm", 166 | "lightdm-gtk-greeter", 167 | "lightdm-gtk-greeter-settings", 168 | ]); 169 | files_eval( 170 | files::append_file( 171 | "/mnt/etc/lightdm/lightdm.conf", 172 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 173 | ), 174 | "Add lightdm greeter", 175 | ); 176 | enable_dm("lightdm"); 177 | } 178 | 179 | fn install_xfce() { 180 | install(vec![ 181 | "xorg", 182 | "xfce4", 183 | "lightdm", 184 | "lightdm-gtk-greeter", 185 | "lightdm-gtk-greeter-settings", 186 | "xfce4-goodies", 187 | "pipewire", 188 | "pipewire-pulse", 189 | "pipewire-jack", 190 | "pipewire-alsa", 191 | "pavucontrol", 192 | ]); 193 | files_eval( 194 | files::append_file( 195 | "/mnt/etc/lightdm/lightdm.conf", 196 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 197 | ), 198 | "Add lightdm greeter", 199 | ); 200 | enable_dm("lightdm"); 201 | } 202 | 203 | fn install_mate() { 204 | install(vec![ 205 | "xorg", 206 | "mate", 207 | "pipewire", 208 | "pipewire-pulse", 209 | "pipewire-alsa", 210 | "pipewire-jack", 211 | "lightdm", 212 | "lightdm-gtk-greeter", 213 | "lightdm-gtk-greeter-settings", 214 | "mate-extra", 215 | ]); 216 | files_eval( 217 | files::append_file( 218 | "/mnt/etc/lightdm/lightdm.conf", 219 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 220 | ), 221 | "Add lightdm greeter", 222 | ); 223 | enable_dm("lightdm"); 224 | } 225 | 226 | fn install_cinnamon() { 227 | install(vec![ 228 | "xorg", 229 | "cinnamon", 230 | "pipewire", 231 | "pipewire-pulse", 232 | "pipewire-alsa", 233 | "pipewire-jack", 234 | "lightdm", 235 | "lightdm-gtk-greeter", 236 | "lightdm-gtk-greeter-settings", 237 | "metacity", 238 | "gnome-shell", 239 | "gnome-terminal", 240 | ]); 241 | files_eval( 242 | files::append_file( 243 | "/mnt/etc/lightdm/lightdm.conf", 244 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 245 | ), 246 | "Add lightdm greeter", 247 | ); 248 | enable_dm("lightdm"); 249 | } 250 | 251 | fn install_budgie() { 252 | install(vec![ 253 | "xorg", 254 | "budgie-desktop", 255 | "gnome", 256 | "pipewire", 257 | "pipewire-pulse", 258 | "pipewire-alsa", 259 | "pipewire-jack", 260 | "lightdm", 261 | "lightdm-gtk-greeter", 262 | "lightdm-gtk-greeter-settings", 263 | "xdg-desktop-portal", 264 | "xdg-desktop-portal-gtk", 265 | "xdg-utils", 266 | ]); 267 | files_eval( 268 | files::append_file( 269 | "/mnt/etc/lightdm/lightdm.conf", 270 | "[SeatDefaults]\ngreeter-session=lightdm-gtk-greeter\n", 271 | ), 272 | "Add lightdm greeter", 273 | ); 274 | enable_dm("lightdm"); 275 | } 276 | 277 | fn install_kde() { 278 | install(vec![ 279 | "xorg", 280 | "plasma", 281 | "plasma-wayland-session", 282 | "kde-utilities", 283 | "kde-system", 284 | "pipewire", 285 | "pipewire-pulse", 286 | "pipewire-alsa", 287 | "pipewire-jack", 288 | "sddm", 289 | ]); 290 | enable_dm("sddm"); 291 | } 292 | 293 | fn install_gnome() { 294 | install(vec![ 295 | "xorg", 296 | "gnome", 297 | "sushi", 298 | "pipewire", 299 | "pipewire-pulse", 300 | "pipewire-alsa", 301 | "pipewire-jack", 302 | "gdm", 303 | ]); 304 | enable_dm("gdm"); 305 | } 306 | 307 | fn install_onyx() { 308 | install(vec![ 309 | "xorg", 310 | "onyx", 311 | "sushi", 312 | "pipewire", 313 | "pipewire-pulse", 314 | "pipewire-alsa", 315 | "pipewire-jack", 316 | "gdm", 317 | ]); 318 | enable_dm("gdm"); 319 | } 320 | 321 | fn enable_dm(dm: &str) { 322 | log::debug!("Enabling {}", dm); 323 | exec_eval( 324 | exec_chroot("systemctl", vec![String::from("enable"), String::from(dm)]), 325 | format!("Enable {}", dm).as_str(), 326 | ); 327 | } 328 | -------------------------------------------------------------------------------- /src/functions/locale.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::exec::*; 2 | use crate::internal::*; 3 | 4 | pub fn set_timezone(timezone: &str) { 5 | exec_eval( 6 | // Remember this should run in a chroot 7 | // not on the host, as linking to /mnt/usr/share/zoneinfo 8 | // will mean you're gonna have a bad time 9 | exec_chroot( 10 | "ln", 11 | vec![ 12 | "-sf".to_string(), 13 | format!("/usr/share/zoneinfo/{}", timezone), 14 | "/etc/localtime".to_string(), 15 | ], 16 | ), 17 | "Set timezone", 18 | ); 19 | exec_eval( 20 | exec_chroot("hwclock", vec!["--systohc".to_string()]), 21 | "Set system clock", 22 | ); 23 | } 24 | 25 | pub fn set_locale(locale: String) { 26 | files_eval( 27 | files::append_file("/mnt/etc/locale.gen", "en_US.UTF-8 UTF-8"), 28 | "add en_US.UTF-8 UTF-8 to locale.gen", 29 | ); 30 | for i in (0..locale.split(' ').count()).step_by(2) { 31 | files_eval( 32 | files::append_file( 33 | "/mnt/etc/locale.gen", 34 | &format!( 35 | "{} {}\n", 36 | locale.split(' ').collect::>()[i], 37 | locale.split(' ').collect::>()[i + 1] 38 | ), 39 | ), 40 | "add locales to locale.gen", 41 | ); 42 | } 43 | exec_eval(exec_chroot("locale-gen", vec![]), "generate locales"); 44 | files::create_file("/mnt/etc/locale.conf"); 45 | files_eval( 46 | files::append_file("/mnt/etc/locale.conf", "LANG=en_US.UTF-8"), 47 | "edit locale.conf", 48 | ); 49 | } 50 | 51 | pub fn set_keyboard(keyboard: &str) { 52 | files::create_file("/mnt/etc/vconsole.conf"); 53 | files_eval( 54 | files::append_file( 55 | "/mnt/etc/vconsole.conf", 56 | format!("KEYMAP={}", keyboard).as_str(), 57 | ), 58 | "set keyboard layout", 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod base; 2 | pub mod desktops; 3 | pub mod locale; 4 | pub mod network; 5 | pub mod partition; 6 | pub mod unakite; 7 | pub mod users; 8 | -------------------------------------------------------------------------------- /src/functions/network.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | pub fn set_hostname(hostname: &str) { 4 | println!("Setting hostname to {}", hostname); 5 | files::create_file("/mnt/etc/hostname"); 6 | files_eval( 7 | files::append_file("/mnt/etc/hostname", hostname), 8 | "set hostname", 9 | ); 10 | } 11 | 12 | pub fn create_hosts() { 13 | files::create_file("/mnt/etc/hosts"); 14 | files_eval( 15 | files::append_file("/mnt/etc/hosts", "127.0.0.1 localhost"), 16 | "create /etc/hosts", 17 | ); 18 | } 19 | 20 | pub fn enable_ipv6() { 21 | files_eval( 22 | files::append_file("/mnt/etc/hosts", "::1 localhost"), 23 | "add ipv6 localhost", 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/functions/partition.rs: -------------------------------------------------------------------------------- 1 | use crate::args; 2 | use crate::args::PartitionMode; 3 | use crate::internal::exec::*; 4 | use crate::internal::*; 5 | use std::path::{Path, PathBuf}; 6 | 7 | /*mkfs.bfs mkfs.cramfs mkfs.ext3 mkfs.fat mkfs.msdos mkfs.xfs 8 | mkfs.btrfs mkfs.ext2 mkfs.ext4 mkfs.minix mkfs.vfat mkfs.f2fs */ 9 | 10 | pub fn fmt_mount(mountpoint: &str, filesystem: &str, blockdevice: &str) { 11 | match filesystem { 12 | "vfat" => exec_eval( 13 | exec( 14 | "mkfs.vfat", 15 | vec![ 16 | String::from("-F"), 17 | String::from("32"), 18 | String::from(blockdevice), 19 | ], 20 | ), 21 | format!("Formatting {blockdevice} as vfat").as_str(), 22 | ), 23 | "bfs" => exec_eval( 24 | exec("mkfs.bfs", vec![String::from(blockdevice)]), 25 | format!("Formatting {blockdevice} as bfs").as_str(), 26 | ), 27 | "cramfs" => exec_eval( 28 | exec("mkfs.cramfs", vec![String::from(blockdevice)]), 29 | format!("Formatting {blockdevice} as cramfs").as_str(), 30 | ), 31 | "ext3" => exec_eval( 32 | exec("mkfs.ext3", vec![String::from(blockdevice)]), 33 | format!("Formatting {blockdevice} as ext3").as_str(), 34 | ), 35 | "fat" => exec_eval( 36 | exec("mkfs.fat", vec![String::from(blockdevice)]), 37 | format!("Formatting {blockdevice} as fat").as_str(), 38 | ), 39 | "msdos" => exec_eval( 40 | exec("mkfs.msdos", vec![String::from(blockdevice)]), 41 | format!("Formatting {blockdevice} as msdos").as_str(), 42 | ), 43 | "xfs" => exec_eval( 44 | exec("mkfs.xfs", vec![String::from(blockdevice)]), 45 | format!("Formatting {blockdevice} as xfs").as_str(), 46 | ), 47 | "btrfs" => exec_eval( 48 | exec( 49 | "mkfs.btrfs", 50 | vec![String::from("-f"), String::from(blockdevice)], 51 | ), 52 | format!("Formatting {blockdevice} as btrfs").as_str(), 53 | ), 54 | "ext2" => exec_eval( 55 | exec("mkfs.ext2", vec![String::from(blockdevice)]), 56 | format!("Formatting {blockdevice} as ext2").as_str(), 57 | ), 58 | "ext4" => exec_eval( 59 | exec("mkfs.ext4", vec![String::from(blockdevice)]), 60 | format!("Formatting {blockdevice} as ext4").as_str(), 61 | ), 62 | "minix" => exec_eval( 63 | exec("mkfs.minix", vec![String::from(blockdevice)]), 64 | format!("Formatting {blockdevice} as minix").as_str(), 65 | ), 66 | "f2fs" => exec_eval( 67 | exec("mkfs.f2fs", vec![String::from(blockdevice)]), 68 | format!("Formatting {blockdevice} as f2fs").as_str(), 69 | ), 70 | "don't format" => { 71 | log::debug!("Not formatting {}", blockdevice); 72 | } 73 | "noformat" => { 74 | log::debug!("Not formatting {}", blockdevice); 75 | } 76 | _ => { 77 | crash( 78 | format!("Unknown filesystem {filesystem}, used in partition {blockdevice}"), 79 | 1, 80 | ); 81 | } 82 | } 83 | exec_eval( 84 | exec("mkdir", vec![String::from("-p"), String::from(mountpoint)]), 85 | format!("Creating mountpoint {mountpoint} for {blockdevice}").as_str(), 86 | ); 87 | mount(blockdevice, mountpoint, ""); 88 | } 89 | 90 | pub fn partition( 91 | device: PathBuf, 92 | mode: PartitionMode, 93 | efi: bool, 94 | partitions: &mut Vec, 95 | unakite: bool, 96 | ) { 97 | println!("{:?}", mode); 98 | match mode { 99 | PartitionMode::Auto => { 100 | if !device.exists() { 101 | crash(format!("The device {device:?} doesn't exist"), 1); 102 | } 103 | log::debug!("automatically partitioning {device:?}"); 104 | if efi { 105 | partition_with_efi(&device, unakite); 106 | } else { 107 | partition_no_efi(&device, unakite); 108 | } 109 | if device.to_string_lossy().contains("nvme") { 110 | part_nvme(&device, efi, unakite); 111 | } else { 112 | part_disk(&device, efi, unakite); 113 | } 114 | } 115 | PartitionMode::Manual => { 116 | log::debug!("Manual partitioning"); 117 | partitions.sort_by(|a, b| a.mountpoint.len().cmp(&b.mountpoint.len())); 118 | for i in 0..partitions.len() { 119 | println!("{:?}", partitions); 120 | println!("{}", partitions.len()); 121 | println!("{}", &partitions[i].mountpoint); 122 | println!("{}", &partitions[i].filesystem); 123 | println!("{}", &partitions[i].blockdevice); 124 | fmt_mount( 125 | &partitions[i].mountpoint, 126 | &partitions[i].filesystem, 127 | &partitions[i].blockdevice, 128 | ); 129 | } 130 | } 131 | } 132 | } 133 | 134 | fn partition_no_efi(device: &Path, unakite: bool) { 135 | let device = device.to_string_lossy().to_string(); 136 | exec_eval( 137 | exec( 138 | "parted", 139 | vec![ 140 | String::from("-s"), 141 | String::from(&device), 142 | String::from("mklabel"), 143 | String::from("msdos"), 144 | ], 145 | ), 146 | format!("Create msdos label on {}", device).as_str(), 147 | ); 148 | exec_eval( 149 | exec( 150 | "parted", 151 | vec![ 152 | String::from("-s"), 153 | String::from(&device), 154 | String::from("mkpart"), 155 | String::from("primary"), 156 | String::from("ext4"), 157 | String::from("1MIB"), 158 | String::from("512MIB"), 159 | ], 160 | ), 161 | "create bios boot partition", 162 | ); 163 | if unakite { 164 | exec_eval( 165 | exec( 166 | "parted", 167 | vec![ 168 | String::from("-s"), 169 | String::from(&device), 170 | String::from("mkpart"), 171 | String::from("primary"), 172 | String::from("btrfs"), 173 | String::from("512MIB"), 174 | String::from("10048MIB"), 175 | ], 176 | ), 177 | "create btrfs Unakite root partition", 178 | ); 179 | exec_eval( 180 | exec( 181 | "parted", 182 | vec![ 183 | String::from("-s"), 184 | device, 185 | String::from("mkpart"), 186 | String::from("primary"), 187 | String::from("btrfs"), 188 | String::from("10048MIB"), 189 | String::from("100%"), 190 | ], 191 | ), 192 | "create btrfs Crystal root partition", 193 | ); 194 | } else { 195 | exec_eval( 196 | exec( 197 | "parted", 198 | vec![ 199 | String::from("-s"), 200 | device, 201 | String::from("mkpart"), 202 | String::from("primary"), 203 | String::from("btrfs"), 204 | String::from("512MIB"), 205 | String::from("100%"), 206 | ], 207 | ), 208 | "create btrfs root partition", 209 | ); 210 | } 211 | } 212 | 213 | fn partition_with_efi(device: &Path, unakite: bool) { 214 | let device = device.to_string_lossy().to_string(); 215 | exec_eval( 216 | exec( 217 | "parted", 218 | vec![ 219 | String::from("-s"), 220 | String::from(&device), 221 | String::from("mklabel"), 222 | String::from("gpt"), 223 | ], 224 | ), 225 | format!("create gpt label on {}", &device).as_str(), 226 | ); 227 | exec_eval( 228 | exec( 229 | "parted", 230 | vec![ 231 | String::from("-s"), 232 | String::from(&device), 233 | String::from("mkpart"), 234 | String::from("fat32"), 235 | String::from("0"), 236 | String::from("300"), 237 | ], 238 | ), 239 | "create EFI partition", 240 | ); 241 | if unakite { 242 | exec_eval( 243 | exec( 244 | "parted", 245 | vec![ 246 | String::from("-s"), 247 | String::from(&device), 248 | String::from("mkpart"), 249 | String::from("primary"), 250 | String::from("btrfs"), 251 | String::from("512MIB"), 252 | String::from("10048MIB"), 253 | ], 254 | ), 255 | "create btrfs Unakite root partition", 256 | ); 257 | exec_eval( 258 | exec( 259 | "parted", 260 | vec![ 261 | String::from("-s"), 262 | device, 263 | String::from("mkpart"), 264 | String::from("primary"), 265 | String::from("btrfs"), 266 | String::from("10048MIB"), 267 | String::from("100%"), 268 | ], 269 | ), 270 | "create btrfs Crystal root partition", 271 | ); 272 | } else { 273 | exec_eval( 274 | exec( 275 | "parted", 276 | vec![ 277 | String::from("-s"), 278 | device, 279 | String::from("mkpart"), 280 | String::from("primary"), 281 | String::from("btrfs"), 282 | String::from("512MIB"), 283 | String::from("100%"), 284 | ], 285 | ), 286 | "create btrfs root partition", 287 | ); 288 | } 289 | } 290 | 291 | fn part_nvme(device: &Path, efi: bool, unakite: bool) { 292 | let device = device.to_string_lossy().to_string(); 293 | if efi && !unakite { 294 | exec_eval( 295 | exec("mkfs.vfat", vec![format!("-F32, {}p1", device)]), 296 | format!("format {}p1 as fat32", device).as_str(), 297 | ); 298 | exec_eval( 299 | exec( 300 | "mkfs.btrfs", 301 | vec!["-f".to_string(), format!("{}p2", device)], 302 | ), 303 | format!("format {}p2 as btrfs", device).as_str(), 304 | ); 305 | mount(format!("{}p2", device).as_str(), "/mnt", ""); 306 | exec_eval( 307 | exec_workdir( 308 | "btrfs", 309 | "/mnt", 310 | vec![ 311 | String::from("subvolume"), 312 | String::from("create"), 313 | String::from("@"), 314 | ], 315 | ), 316 | "Create btrfs subvolume @", 317 | ); 318 | exec_eval( 319 | exec_workdir( 320 | "btrfs", 321 | "/mnt", 322 | vec![ 323 | String::from("subvolume"), 324 | String::from("create"), 325 | String::from("@home"), 326 | ], 327 | ), 328 | "Create btrfs subvolume @home", 329 | ); 330 | umount("/mnt"); 331 | mount(format!("{}p2", device).as_str(), "/mnt/", "subvol=@"); 332 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot"); 333 | files_eval( 334 | files::create_directory("/mnt/boot/efi"), 335 | "create /mnt/boot/efi", 336 | ); 337 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home"); 338 | mount( 339 | format!("{}p2", device).as_str(), 340 | "/mnt/home", 341 | "subvol=@home", 342 | ); 343 | mount(format!("{}p1", device).as_str(), "/mnt/boot/efi", ""); 344 | } else if !efi && !unakite { 345 | exec_eval( 346 | exec("mkfs.ext4", vec![format!("{}p1", device)]), 347 | format!("format {}p1 as ext4", device).as_str(), 348 | ); 349 | exec_eval( 350 | exec( 351 | "mkfs.btrfs", 352 | vec!["-f".to_string(), format!("{}p2", device)], 353 | ), 354 | format!("format {}p2 as btrfs", device).as_str(), 355 | ); 356 | mount(format!("{}p2", device).as_str(), "/mnt/", ""); 357 | exec_eval( 358 | exec_workdir( 359 | "btrfs", 360 | "/mnt", 361 | vec![ 362 | String::from("subvolume"), 363 | String::from("create"), 364 | String::from("@"), 365 | ], 366 | ), 367 | "Create btrfs subvolume @", 368 | ); 369 | exec_eval( 370 | exec_workdir( 371 | "btrfs", 372 | "/mnt", 373 | vec![ 374 | String::from("subvolume"), 375 | String::from("create"), 376 | String::from("@home"), 377 | ], 378 | ), 379 | "Create btrfs subvolume @home", 380 | ); 381 | umount("/mnt"); 382 | mount(format!("{}p2", device).as_str(), "/mnt/", "subvol=@"); 383 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot"); 384 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home"); 385 | mount( 386 | format!("{}p2", device).as_str(), 387 | "/mnt/home", 388 | "subvol=@home", 389 | ); 390 | mount(format!("{}p1", device).as_str(), "/mnt/boot", ""); 391 | } else if efi && unakite { 392 | exec_eval( 393 | exec("mkfs.vfat", vec![format!("-F32 {}p1", device)]), 394 | format!("format {}p1 as fat32", device).as_str(), 395 | ); 396 | exec_eval( 397 | exec( 398 | "mkfs.btrfs", 399 | vec!["-f".to_string(), format!("{}p2", device)], 400 | ), 401 | format!("format {}p2 as btrfs", device).as_str(), 402 | ); 403 | exec_eval( 404 | exec( 405 | "mkfs.btrfs", 406 | vec!["-f".to_string(), format!("{}p3", device)], 407 | ), 408 | format!("format {}p3 as btrfs", device).as_str(), 409 | ); 410 | mount(format!("{}p3", device).as_str(), "/mnt", ""); 411 | exec_eval( 412 | exec_workdir( 413 | "btrfs", 414 | "/mnt", 415 | vec![ 416 | String::from("subvolume"), 417 | String::from("create"), 418 | String::from("@"), 419 | ], 420 | ), 421 | "Create btrfs subvolume @", 422 | ); 423 | exec_eval( 424 | exec_workdir( 425 | "btrfs", 426 | "/mnt", 427 | vec![ 428 | String::from("subvolume"), 429 | String::from("create"), 430 | String::from("@home"), 431 | ], 432 | ), 433 | "Create btrfs subvolume @home", 434 | ); 435 | umount("/mnt"); 436 | mount(format!("{}p3", device).as_str(), "/mnt/", "subvol=@"); 437 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot"); 438 | files_eval( 439 | files::create_directory("/mnt/boot/efi"), 440 | "create /mnt/boot/efi", 441 | ); 442 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home"); 443 | mount( 444 | format!("{}p3", device).as_str(), 445 | "/mnt/home", 446 | "subvol=@home", 447 | ); 448 | mount(format!("{}p1", device).as_str(), "/mnt/boot/efi", ""); 449 | } else if !efi && unakite { 450 | exec_eval( 451 | exec("mkfs.ext4", vec![format!("{}p1", device)]), 452 | format!("format {}p1 as ext4", device).as_str(), 453 | ); 454 | exec_eval( 455 | exec( 456 | "mkfs.btrfs", 457 | vec!["-f".to_string(), format!("{}p2", device)], 458 | ), 459 | format!("format {}p2 as btrfs", device).as_str(), 460 | ); 461 | mount(format!("{}p2", device).as_str(), "/mnt/", ""); 462 | exec_eval( 463 | exec_workdir( 464 | "btrfs", 465 | "/mnt", 466 | vec![ 467 | String::from("subvolume"), 468 | String::from("create"), 469 | String::from("@"), 470 | ], 471 | ), 472 | "Create btrfs subvolume @", 473 | ); 474 | exec_eval( 475 | exec_workdir( 476 | "btrfs", 477 | "/mnt", 478 | vec![ 479 | String::from("subvolume"), 480 | String::from("create"), 481 | String::from("@home"), 482 | ], 483 | ), 484 | "Create btrfs subvolume @home", 485 | ); 486 | umount("/mnt"); 487 | mount(format!("{}p2", device).as_str(), "/mnt/", "subvol=@"); 488 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot"); 489 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home"); 490 | mount( 491 | format!("{}p2", device).as_str(), 492 | "/mnt/home", 493 | "subvol=@home", 494 | ); 495 | mount(format!("{}p1", device).as_str(), "/mnt/boot", ""); 496 | } 497 | } 498 | 499 | fn part_disk(device: &Path, efi: bool, unakite: bool) { 500 | let device = device.to_string_lossy().to_string(); 501 | if efi && !unakite { 502 | exec_eval( 503 | exec("mkfs.vfat", vec![format!("-F32 {}1", device)]), 504 | format!("format {}1 as fat32", device).as_str(), 505 | ); 506 | exec_eval( 507 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}2", device)]), 508 | format!("format {}2 as btrfs", device).as_str(), 509 | ); 510 | mount(format!("{}2", device).as_str(), "/mnt", ""); 511 | exec_eval( 512 | exec_workdir( 513 | "btrfs", 514 | "/mnt", 515 | vec![ 516 | String::from("subvolume"), 517 | String::from("create"), 518 | String::from("@"), 519 | ], 520 | ), 521 | "Create btrfs subvolume @", 522 | ); 523 | exec_eval( 524 | exec_workdir( 525 | "btrfs", 526 | "/mnt", 527 | vec![ 528 | String::from("subvolume"), 529 | String::from("create"), 530 | String::from("@home"), 531 | ], 532 | ), 533 | "Create btrfs subvolume @home", 534 | ); 535 | umount("/mnt"); 536 | mount(format!("{}2", device).as_str(), "/mnt/", "subvol=@"); 537 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot"); 538 | files_eval( 539 | files::create_directory("/mnt/boot/efi"), 540 | "create /mnt/boot/efi", 541 | ); 542 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home"); 543 | mount(format!("{}2", device).as_str(), "/mnt/home", "subvol=@home"); 544 | mount(format!("{}1", device).as_str(), "/mnt/boot/efi", ""); 545 | } else if !efi && !unakite { 546 | exec_eval( 547 | exec("mkfs.ext4", vec![format!("{}1", device)]), 548 | format!("format {}1 as ext4", device).as_str(), 549 | ); 550 | exec_eval( 551 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}2", device)]), 552 | format!("format {}2 as btrfs", device).as_str(), 553 | ); 554 | mount(format!("{}2", device).as_str(), "/mnt/", ""); 555 | exec_eval( 556 | exec_workdir( 557 | "btrfs", 558 | "/mnt", 559 | vec![ 560 | String::from("subvolume"), 561 | String::from("create"), 562 | String::from("@"), 563 | ], 564 | ), 565 | "Create btrfs subvolume @", 566 | ); 567 | exec_eval( 568 | exec_workdir( 569 | "btrfs", 570 | "/mnt", 571 | vec![ 572 | String::from("subvolume"), 573 | String::from("create"), 574 | String::from("@home"), 575 | ], 576 | ), 577 | "create btrfs subvolume @home", 578 | ); 579 | umount("/mnt"); 580 | mount(format!("{}2", device).as_str(), "/mnt/", "subvol=@"); 581 | files_eval( 582 | files::create_directory("/mnt/boot"), 583 | "create directory /mnt/boot", 584 | ); 585 | files_eval( 586 | files::create_directory("/mnt/home"), 587 | "create directory /mnt/home", 588 | ); 589 | mount(format!("{}2", device).as_str(), "/mnt/home", "subvol=@home"); 590 | mount(format!("{}1", device).as_str(), "/mnt/boot", ""); 591 | } else if efi && unakite { 592 | exec_eval( 593 | exec("mkfs.vfat", vec![format!("-F32 {}1", device)]), 594 | format!("format {}1 as fat32", device).as_str(), 595 | ); 596 | exec_eval( 597 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}2", device)]), 598 | format!("format {}2 as btrfs", device).as_str(), 599 | ); 600 | exec_eval( 601 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}3", device)]), 602 | format!("format {}3 as btrfs", device).as_str(), 603 | ); 604 | mount(format!("{}3", device).as_str(), "/mnt", ""); 605 | exec_eval( 606 | exec_workdir( 607 | "btrfs", 608 | "/mnt", 609 | vec![ 610 | String::from("subvolume"), 611 | String::from("create"), 612 | String::from("@"), 613 | ], 614 | ), 615 | "Create btrfs subvolume @", 616 | ); 617 | exec_eval( 618 | exec_workdir( 619 | "btrfs", 620 | "/mnt", 621 | vec![ 622 | String::from("subvolume"), 623 | String::from("create"), 624 | String::from("@home"), 625 | ], 626 | ), 627 | "Create btrfs subvolume @home", 628 | ); 629 | umount("/mnt"); 630 | mount(format!("{}3", device).as_str(), "/mnt/", "subvol=@"); 631 | files_eval(files::create_directory("/mnt/boot"), "create /mnt/boot"); 632 | files_eval( 633 | files::create_directory("/mnt/boot/efi"), 634 | "create /mnt/boot/efi", 635 | ); 636 | files_eval(files::create_directory("/mnt/home"), "create /mnt/home"); 637 | mount(format!("{}3", device).as_str(), "/mnt/home", "subvol=@home"); 638 | mount(format!("{}1", device).as_str(), "/mnt/boot/efi", ""); 639 | } else if !efi && unakite { 640 | exec_eval( 641 | exec("mkfs.ext4", vec![format!("{}1", device)]), 642 | format!("format {}1 as ext4", device).as_str(), 643 | ); 644 | exec_eval( 645 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}2", device)]), 646 | format!("format {}2 as btrfs", device).as_str(), 647 | ); 648 | exec_eval( 649 | exec("mkfs.btrfs", vec!["-f".to_string(), format!("{}3", device)]), 650 | format!("format {}3 as btrfs", device).as_str(), 651 | ); 652 | mount(format!("{}3", device).as_str(), "/mnt/", ""); 653 | exec_eval( 654 | exec_workdir( 655 | "btrfs", 656 | "/mnt", 657 | vec![ 658 | String::from("subvolume"), 659 | String::from("create"), 660 | String::from("@"), 661 | ], 662 | ), 663 | "Create btrfs subvolume @", 664 | ); 665 | exec_eval( 666 | exec_workdir( 667 | "btrfs", 668 | "/mnt", 669 | vec![ 670 | String::from("subvolume"), 671 | String::from("create"), 672 | String::from("@home"), 673 | ], 674 | ), 675 | "create btrfs subvolume @home", 676 | ); 677 | umount("/mnt"); 678 | mount(format!("{}3", device).as_str(), "/mnt/", "subvol=@"); 679 | files_eval( 680 | files::create_directory("/mnt/boot"), 681 | "create directory /mnt/boot", 682 | ); 683 | files_eval( 684 | files::create_directory("/mnt/home"), 685 | "create directory /mnt/home", 686 | ); 687 | mount(format!("{}3", device).as_str(), "/mnt/home", "subvol=@home"); 688 | mount(format!("{}1", device).as_str(), "/mnt/boot", ""); 689 | } 690 | } 691 | 692 | pub fn mount(partition: &str, mountpoint: &str, options: &str) { 693 | if !options.is_empty() { 694 | exec_eval( 695 | exec( 696 | "mount", 697 | vec![ 698 | String::from(partition), 699 | String::from(mountpoint), 700 | String::from("-o"), 701 | String::from(options), 702 | ], 703 | ), 704 | format!( 705 | "mount {} with options {} at {}", 706 | partition, options, mountpoint 707 | ) 708 | .as_str(), 709 | ); 710 | } else { 711 | exec_eval( 712 | exec( 713 | "mount", 714 | vec![String::from(partition), String::from(mountpoint)], 715 | ), 716 | format!("mount {} with no options at {}", partition, mountpoint).as_str(), 717 | ); 718 | } 719 | } 720 | 721 | pub fn umount(mountpoint: &str) { 722 | exec_eval( 723 | exec("umount", vec![String::from(mountpoint)]), 724 | format!("unmount {}", mountpoint).as_str(), 725 | ); 726 | } 727 | -------------------------------------------------------------------------------- /src/functions/unakite.rs: -------------------------------------------------------------------------------- 1 | use crate::args::DesktopSetup; 2 | use crate::functions::partition::mount; 3 | use crate::functions::*; 4 | use crate::internal::exec::*; 5 | use crate::internal::*; 6 | use std::path::PathBuf; 7 | pub fn install_bootloader_efi(efidir: PathBuf) { 8 | install::install(vec![ 9 | "grub", 10 | "efibootmgr", 11 | "grub-btrfs", 12 | "crystal-grub-theme", 13 | ]); 14 | let efidir = std::path::Path::new("/mnt").join(efidir); 15 | let efi_str = efidir.to_str().unwrap(); 16 | if !std::path::Path::new(&format!("/mnt{efi_str}")).exists() { 17 | crash(format!("The efidir {efidir:?} doesn't exist"), 1); 18 | } 19 | exec_eval( 20 | exec_chroot( 21 | "grub-install", 22 | vec![ 23 | String::from("--target=x86_64-efi"), 24 | format!("--efi-directory={}", efi_str), 25 | String::from("--bootloader-id=unakite"), 26 | String::from("--removable"), 27 | ], 28 | ), 29 | "install unakite grub as efi with --removable", 30 | ); 31 | exec_eval( 32 | exec_chroot( 33 | "grub-install", 34 | vec![ 35 | String::from("--target=x86_64-efi"), 36 | format!("--efi-directory={}", efi_str), 37 | String::from("--bootloader-id=unakite"), 38 | ], 39 | ), 40 | "install unakite grub as efi without --removable", 41 | ); 42 | files_eval( 43 | files::append_file( 44 | "/mnt/etc/default/grub", 45 | "GRUB_THEME=\"/usr/share/grub/themes/crystal/theme.txt\"", 46 | ), 47 | "enable crystal grub theme", 48 | ); 49 | exec_eval( 50 | exec_chroot( 51 | "grub-mkconfig", 52 | vec![String::from("-o"), String::from("/boot/grub/grub.cfg")], 53 | ), 54 | "create grub.cfg", 55 | ); 56 | } 57 | 58 | pub fn remount(root: &str, oldroot: &str, efi: bool, efidir: &str, bootdev: &str, firstrun: bool) { 59 | if efi && firstrun { 60 | exec_eval( 61 | exec("umount", vec![String::from(bootdev)]), 62 | &format!("Unmount {}", bootdev), 63 | ); 64 | exec_eval( 65 | exec("umount", vec![String::from(oldroot)]), 66 | "Unmount old root", 67 | ); 68 | mount(root, "/mnt", ""); 69 | exec_eval( 70 | exec("mkdir", vec![String::from("-p"), String::from(efidir)]), 71 | format!("Creating mountpoint {efidir} for {bootdev}").as_str(), 72 | ); 73 | mount(bootdev, efidir, ""); 74 | } else if efi && !firstrun { 75 | exec_eval( 76 | exec("umount", vec![String::from(bootdev)]), 77 | &format!("Unmount {}", bootdev), 78 | ); 79 | exec_eval( 80 | exec("umount", vec![String::from(root)]), 81 | "Unmount unakite root", 82 | ); 83 | mount(oldroot, "/mnt", ""); 84 | mount(bootdev, efidir, ""); 85 | } else if !efi && firstrun { 86 | exec_eval( 87 | exec("umount", vec![String::from(bootdev)]), 88 | &format!("Unmount {}", bootdev), 89 | ); 90 | exec_eval( 91 | exec("umount", vec![String::from(oldroot)]), 92 | "Unmount old root", 93 | ); 94 | mount(root, "/mnt", ""); 95 | exec_eval( 96 | exec("mkdir", vec![String::from("-p"), String::from("/mnt/boot")]), 97 | format!("Creating mountpoint /boot for {bootdev}").as_str(), 98 | ); 99 | mount(bootdev, "/mnt/boot", ""); 100 | } else if !efi && !firstrun { 101 | exec_eval( 102 | exec("umount", vec![String::from(bootdev)]), 103 | &format!("Unmount {}", bootdev), 104 | ); 105 | exec_eval( 106 | exec("umount", vec![String::from(root)]), 107 | "Unmount unakite root", 108 | ); 109 | mount(oldroot, "/mnt", ""); 110 | mount(bootdev, "/mnt/boot", ""); 111 | } else { 112 | panic!("Unknown state"); 113 | } 114 | } 115 | 116 | pub fn setup_unakite(root: &str, oldroot: &str, efi: bool, efidir: &str, bootdev: &str) { 117 | log::debug!("Setting up Unakite"); 118 | remount(root, oldroot, efi, efidir, bootdev, true); 119 | base::install_base_packages("linux".to_string()); 120 | base::genfstab(); 121 | locale::set_locale("en_US.UTF-8 UTF-8".to_string()); 122 | locale::set_timezone("Europe/Berlin"); // TODO: get the proper timezone 123 | network::set_hostname("unakite"); 124 | network::create_hosts(); 125 | users::new_user( 126 | "unakite", 127 | true, 128 | "Cp7oN04ZY0PsA", // unakite 129 | false, 130 | "/bin/bash", 131 | ); 132 | exec_eval( 133 | exec( 134 | "sed", 135 | vec![ 136 | String::from("-i"), 137 | String::from("-e"), 138 | String::from("s/crystal/unakite/g"), 139 | String::from("/mnt/etc/os-release"), 140 | ], 141 | ), 142 | "Change os-release", 143 | ); 144 | exec_eval( 145 | exec( 146 | "sed", 147 | vec![ 148 | String::from("-i"), 149 | String::from("-e"), 150 | String::from("s/Crystal/Unakite/g"), 151 | String::from("/mnt/etc/os-release"), 152 | ], 153 | ), 154 | "Change os-release", 155 | ); 156 | if efi { 157 | install_bootloader_efi(PathBuf::from(efidir.replace("/mnt", ""))); 158 | } 159 | users::root_pass("Cp7oN04ZY0PsA"); // unakite 160 | desktops::install_desktop_setup(DesktopSetup::Xfce); 161 | install(vec!["gparted", "firefox"]); 162 | exec_eval( 163 | exec( 164 | "cp", 165 | vec![ 166 | String::from("/tmp/jade.json"), 167 | String::from("/mnt/etc/installSettings.json"), 168 | ], 169 | ), 170 | "Copy jade.json to /etc/installSettings.json in unakite", 171 | ); 172 | remount(root, oldroot, efi, efidir, bootdev, false); 173 | exec_eval( 174 | exec_chroot( 175 | "grub-mkconfig", 176 | vec![String::from("-o"), String::from("/boot/grub/grub.cfg")], 177 | ), 178 | "Recreate grub.cfg in crystal", 179 | ); 180 | } 181 | -------------------------------------------------------------------------------- /src/functions/users.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::exec::*; 2 | use crate::internal::*; 3 | use std::process::Command; 4 | 5 | pub fn new_user(username: &str, hasroot: bool, password: &str, do_hash_pass: bool, shell: &str) { 6 | let shell: &str = shell; 7 | if do_hash_pass { 8 | let hashed_pass = &*hash_pass(password).stdout; 9 | let _password = match std::str::from_utf8(hashed_pass) { 10 | Ok(v) => v, 11 | Err(e) => panic!("Failed to hash password, invalid UTF-8 sequence {}", e), 12 | }; 13 | } 14 | let shell_to_install = match shell { 15 | "bash" => "bash", 16 | "csh" => "tcsh", 17 | "fish" => "fish", 18 | "tcsh" => "tcsh", 19 | "zsh" => "zsh", 20 | &_ => "bash", 21 | }; 22 | install::install(vec![shell_to_install]); 23 | let shell_path = match shell { 24 | "bash" => "/bin/bash", 25 | "csh" => "/usr/bin/csh", 26 | "fish" => "/usr/bin/fish", 27 | "tcsh" => "/usr/bin/tcsh", 28 | "zsh" => "/usr/bin/zsh", 29 | &_ => "/usr/bin/fish", 30 | }; 31 | exec_eval( 32 | exec_chroot( 33 | "useradd", 34 | vec![ 35 | String::from("-m"), 36 | String::from("-s"), 37 | String::from(shell_path), 38 | String::from("-p"), 39 | String::from(password).replace('\n', ""), 40 | String::from(username), 41 | ], 42 | ), 43 | format!("Create user {}", username).as_str(), 44 | ); 45 | if hasroot { 46 | exec_eval( 47 | exec_chroot( 48 | "usermod", 49 | vec![ 50 | String::from("-aG"), 51 | String::from("wheel"), 52 | String::from(username), 53 | ], 54 | ), 55 | format!("Add user {} to wheel group", username).as_str(), 56 | ); 57 | files_eval( 58 | files::sed_file( 59 | "/mnt/etc/sudoers", 60 | "# %wheel ALL=(ALL:ALL) ALL", 61 | "%wheel ALL=(ALL:ALL) ALL", 62 | ), 63 | "Add wheel group to sudoers", 64 | ); 65 | files_eval( 66 | files::append_file("/mnt/etc/sudoers", "\nDefaults pwfeedback\n"), 67 | "Add pwfeedback to sudoers", 68 | ); 69 | files_eval( 70 | files::create_directory("/mnt/var/lib/AccountsService/users/"), 71 | "Create /mnt/var/lib/AcountsService", 72 | ); 73 | files::create_file(&format!("/mnt/var/lib/AccountsService/users/{}", username)); 74 | files_eval( 75 | files::append_file( 76 | &format!("/mnt/var/lib/AccountsService/users/{}", username), 77 | r#"[User] 78 | Session=onyx"#, 79 | ), 80 | format!("Populate AccountsService user file for {}", username).as_str(), 81 | ) 82 | } 83 | } 84 | 85 | pub fn hash_pass(password: &str) -> std::process::Output { 86 | let output = Command::new("openssl") 87 | .args(["passwd", "-1", password]) 88 | .output() 89 | .expect("Failed to hash password"); 90 | output 91 | } 92 | 93 | pub fn root_pass(root_pass: &str) { 94 | exec_eval( 95 | exec_chroot( 96 | "bash", 97 | vec![ 98 | String::from("-c"), 99 | format!(r#"'usermod --password {root_pass} root'"#), 100 | ], 101 | ), 102 | "set root password", 103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /src/internal/config.rs: -------------------------------------------------------------------------------- 1 | use crate::args; 2 | use crate::args::{DesktopSetup, PartitionMode}; 3 | use crate::functions::*; 4 | use crate::internal::*; 5 | use serde::{Deserialize, Serialize}; 6 | use std::path::PathBuf; 7 | 8 | #[derive(Serialize, Deserialize)] 9 | struct Config { 10 | partition: Partition, 11 | bootloader: Bootloader, 12 | locale: Locale, 13 | networking: Networking, 14 | users: Vec, 15 | rootpass: String, 16 | desktop: String, 17 | timeshift: bool, 18 | flatpak: bool, 19 | zramd: bool, 20 | extra_packages: Vec, 21 | unakite: Unakite, 22 | kernel: String, 23 | } 24 | 25 | #[derive(Serialize, Deserialize)] 26 | struct Partition { 27 | device: String, 28 | mode: PartitionMode, 29 | efi: bool, 30 | partitions: Vec, 31 | } 32 | 33 | #[derive(Serialize, Deserialize)] 34 | struct Bootloader { 35 | r#type: String, 36 | location: String, 37 | } 38 | 39 | #[derive(Serialize, Deserialize)] 40 | struct Locale { 41 | locale: Vec, 42 | keymap: String, 43 | timezone: String, 44 | } 45 | 46 | #[derive(Serialize, Deserialize)] 47 | struct Networking { 48 | hostname: String, 49 | ipv6: bool, 50 | } 51 | 52 | #[derive(Serialize, Deserialize)] 53 | struct Users { 54 | name: String, 55 | password: String, 56 | hasroot: bool, 57 | shell: String, 58 | } 59 | 60 | #[derive(Serialize, Deserialize)] 61 | struct Unakite { 62 | enable: bool, 63 | root: String, 64 | oldroot: String, 65 | efidir: String, 66 | bootdev: String, 67 | } 68 | 69 | pub fn read_config(configpath: PathBuf) { 70 | let data = std::fs::read_to_string(&configpath); 71 | match &data { 72 | Ok(_) => { 73 | log::debug!("[ \x1b[2;1;32mOK\x1b[0m ] Read config file {configpath:?}"); 74 | } 75 | Err(e) => { 76 | crash( 77 | format!("Read config file {configpath:?} ERROR: {}", e), 78 | e.raw_os_error().unwrap(), 79 | ); 80 | } 81 | } 82 | let config: std::result::Result = 83 | serde_json::from_str(&data.unwrap()); 84 | match &config { 85 | Ok(_) => { 86 | log::debug!("[ \x1b[2;1;32mOK\x1b[0m ] Parse config file {configpath:?}",); 87 | } 88 | Err(e) => { 89 | crash(format!("Parse config file {configpath:?} ERROR: {}", e), 1); 90 | } 91 | } 92 | let config: Config = config.unwrap(); 93 | log::info!("Block device to use : /dev/{}", config.partition.device); 94 | log::info!("Partitioning mode : {:?}", config.partition.mode); 95 | log::info!("Partitioning for EFI : {}", config.partition.efi); 96 | let mut partitions: Vec = Vec::new(); 97 | for partition in config.partition.partitions { 98 | partitions.push(args::Partition::new( 99 | partition.split(':').collect::>()[0].to_string(), 100 | partition.split(':').collect::>()[1].to_string(), 101 | partition.split(':').collect::>()[2].to_string(), 102 | )); 103 | } 104 | let device = PathBuf::from("/dev/").join(config.partition.device.as_str()); 105 | partition::partition( 106 | device, 107 | config.partition.mode, 108 | config.partition.efi, 109 | &mut partitions, 110 | config.unakite.enable, 111 | ); 112 | base::install_base_packages(config.kernel); 113 | base::genfstab(); 114 | println!(); 115 | log::info!("Installing bootloader : {}", config.bootloader.r#type); 116 | log::info!("Installing bootloader to : {}", config.bootloader.location); 117 | if config.bootloader.r#type == "grub-efi" { 118 | base::install_bootloader_efi(PathBuf::from(config.bootloader.location)); 119 | } else if config.bootloader.r#type == "grub-legacy" { 120 | base::install_bootloader_legacy(PathBuf::from(config.bootloader.location)); 121 | } 122 | println!(); 123 | log::info!("Adding Locales : {:?}", config.locale.locale); 124 | log::info!("Using keymap : {}", config.locale.keymap); 125 | log::info!("Setting timezone : {}", config.locale.timezone); 126 | locale::set_locale(config.locale.locale.join(" ")); 127 | locale::set_keyboard(config.locale.keymap.as_str()); 128 | locale::set_timezone(config.locale.timezone.as_str()); 129 | println!(); 130 | log::info!("Hostname : {}", config.networking.hostname); 131 | log::info!("Enabling ipv6 : {}", config.networking.ipv6); 132 | network::set_hostname(config.networking.hostname.as_str()); 133 | network::create_hosts(); 134 | if config.networking.ipv6 { 135 | network::enable_ipv6(); 136 | } 137 | println!(); 138 | println!("---------"); 139 | log::info!("Enabling zramd : {}", config.zramd); 140 | if config.zramd { 141 | base::install_zram(); 142 | } 143 | println!(); 144 | println!("---------"); 145 | for i in 0..config.users.len() { 146 | log::info!("Creating user : {}", config.users[i].name); 147 | log::info!("Setting use password : {}", config.users[i].password); 148 | log::info!("Enabling root for user : {}", config.users[i].hasroot); 149 | log::info!("Setting user shell : {}", config.users[i].shell); 150 | users::new_user( 151 | config.users[i].name.as_str(), 152 | config.users[i].hasroot, 153 | config.users[i].password.as_str(), 154 | false, 155 | config.users[i].shell.as_str(), 156 | ); 157 | println!("---------"); 158 | } 159 | println!(); 160 | log::info!("Setting root password : {}", config.rootpass); 161 | users::root_pass(config.rootpass.as_str()); 162 | println!(); 163 | log::info!("Installing desktop : {:?}", config.desktop); 164 | /*if let Some(desktop) = &config.desktop { 165 | desktops::install_desktop_setup(*desktop); 166 | }*/ 167 | match config.desktop.to_lowercase().as_str() { 168 | "onyx" => desktops::install_desktop_setup(DesktopSetup::Onyx), 169 | "kde" => desktops::install_desktop_setup(DesktopSetup::Kde), 170 | "plasma" => desktops::install_desktop_setup(DesktopSetup::Kde), 171 | "mate" => desktops::install_desktop_setup(DesktopSetup::Mate), 172 | "gnome" => desktops::install_desktop_setup(DesktopSetup::Gnome), 173 | "cinnamon" => desktops::install_desktop_setup(DesktopSetup::Cinnamon), 174 | "xfce" => desktops::install_desktop_setup(DesktopSetup::Xfce), 175 | "budgie" => desktops::install_desktop_setup(DesktopSetup::Budgie), 176 | "enlightenment" => desktops::install_desktop_setup(DesktopSetup::Enlightenment), 177 | "lxqt" => desktops::install_desktop_setup(DesktopSetup::Lxqt), 178 | "sway" => desktops::install_desktop_setup(DesktopSetup::Sway), 179 | "i3-gaps" => desktops::install_desktop_setup(DesktopSetup::I3gaps), 180 | "herbstluftwm" => desktops::install_desktop_setup(DesktopSetup::Herbstluftwm), 181 | "awesome" => desktops::install_desktop_setup(DesktopSetup::Awesome), 182 | "bspwm" => desktops::install_desktop_setup(DesktopSetup::Bspwm), 183 | "none/diy" => desktops::install_desktop_setup(DesktopSetup::None), 184 | _ => log::info!("No desktop setup selected!"), 185 | } 186 | println!(); 187 | log::info!("Enabling timeshift : {}", config.timeshift); 188 | if config.timeshift { 189 | base::setup_timeshift(); 190 | } 191 | println!(); 192 | log::info!("Enabling flatpak : {}", config.flatpak); 193 | if config.flatpak { 194 | base::install_flatpak(); 195 | } 196 | log::info!("Extra packages : {:?}", config.extra_packages); 197 | let mut extra_packages: Vec<&str> = Vec::new(); 198 | for i in 0..config.extra_packages.len() { 199 | extra_packages.push(config.extra_packages[i].as_str()); 200 | } 201 | install(extra_packages); 202 | log::info!("Setup unakite"); 203 | if config.partition.mode == PartitionMode::Auto 204 | && !config.partition.efi 205 | && config.unakite.enable 206 | && !config.partition.device.to_string().contains("nvme") 207 | { 208 | let root = PathBuf::from("/dev/").join(config.partition.device.as_str()); 209 | unakite::setup_unakite( 210 | format!("{}2", root.to_str().unwrap()).as_str(), 211 | format!("{}3", root.to_str().unwrap()).as_str(), 212 | config.partition.efi, 213 | "/boot", 214 | format!("{}1", root.to_str().unwrap()).as_str(), 215 | ) 216 | } else if config.partition.mode == PartitionMode::Auto 217 | && config.partition.efi 218 | && config.unakite.enable 219 | && !config.partition.device.to_string().contains("nvme") 220 | { 221 | let root = PathBuf::from("/dev/").join(config.partition.device.as_str()); 222 | unakite::setup_unakite( 223 | format!("{}2", root.to_str().unwrap()).as_str(), 224 | format!("{}3", root.to_str().unwrap()).as_str(), 225 | config.partition.efi, 226 | "/boot/efi", 227 | format!("{}1", root.to_str().unwrap()).as_str(), 228 | ) 229 | } else if config.unakite.enable { 230 | unakite::setup_unakite( 231 | &config.unakite.root, 232 | &config.unakite.oldroot, 233 | config.partition.efi, 234 | &config.unakite.efidir, 235 | &config.unakite.bootdev, 236 | ); 237 | } else if config.partition.mode == PartitionMode::Auto 238 | && config.partition.efi 239 | && config.unakite.enable 240 | && config.partition.device.to_string().contains("nvme") 241 | { 242 | let root = PathBuf::from("/dev/").join(config.partition.device.as_str()); 243 | unakite::setup_unakite( 244 | format!("{}p2", root.to_str().unwrap()).as_str(), 245 | format!("{}p3", root.to_str().unwrap()).as_str(), 246 | config.partition.efi, 247 | "/boot/efi", 248 | format!("{}p1", root.to_str().unwrap()).as_str(), 249 | ) 250 | } else if config.partition.mode == PartitionMode::Auto 251 | && !config.partition.efi 252 | && config.unakite.enable 253 | && config.partition.device.to_string().contains("nvme") 254 | { 255 | let root = PathBuf::from("/dev/").join(config.partition.device.as_str()); 256 | unakite::setup_unakite( 257 | format!("{}p2", root.to_str().unwrap()).as_str(), 258 | format!("{}p3", root.to_str().unwrap()).as_str(), 259 | config.partition.efi, 260 | "/boot", 261 | format!("{}p1", root.to_str().unwrap()).as_str(), 262 | ) 263 | } else { 264 | log::info!("Unakite disabled"); 265 | } 266 | println!("Installation finished! You may reboot now!") 267 | } 268 | -------------------------------------------------------------------------------- /src/internal/exec.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | pub fn exec(command: &str, args: Vec) -> Result { 4 | let returncode = Command::new(command).args(args).status(); 5 | returncode 6 | } 7 | 8 | pub fn exec_chroot( 9 | command: &str, 10 | args: Vec, 11 | ) -> Result { 12 | let returncode = Command::new("bash") 13 | .args([ 14 | "-c", 15 | format!("arch-chroot /mnt {} {}", command, args.join(" ")).as_str(), 16 | ]) 17 | .status(); 18 | returncode 19 | } 20 | 21 | pub fn exec_workdir( 22 | command: &str, 23 | workdir: &str, 24 | args: Vec, 25 | ) -> Result { 26 | let returncode = Command::new(command) 27 | .args(args) 28 | .current_dir(workdir) 29 | .status(); 30 | returncode 31 | } 32 | -------------------------------------------------------------------------------- /src/internal/files.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | use std::fs::{self, File, OpenOptions}; 3 | use std::io::prelude::*; 4 | 5 | pub fn create_file(path: &str) { 6 | let returncode = File::create(path); 7 | match returncode { 8 | Ok(_) => { 9 | log::info!("Create {}", path); 10 | } 11 | Err(e) => { 12 | crash(format!("Create {}: Failed with error {}", path, e), 1); 13 | } 14 | } 15 | } 16 | 17 | pub fn copy_file(path: &str, destpath: &str) { 18 | let return_code = std::fs::copy(path, destpath); 19 | match return_code { 20 | Ok(_) => { 21 | log::info!("Copy {} to {}", path, destpath); 22 | } 23 | Err(e) => { 24 | crash( 25 | format!("Copy {} to {}: Failed with error {}", path, destpath, e), 26 | 1, 27 | ); 28 | } 29 | } 30 | } 31 | 32 | pub fn append_file(path: &str, content: &str) -> std::io::Result<()> { 33 | log::info!("Append '{}' to file {}", content.trim_end(), path); 34 | let mut file = OpenOptions::new().append(true).open(path)?; 35 | file.write_all(format!("\n{content}\n").as_bytes())?; 36 | Ok(()) 37 | } 38 | 39 | pub fn sed_file(path: &str, find: &str, replace: &str) -> std::io::Result<()> { 40 | log::info!("Sed '{}' to '{}' in file {}", find, replace, path); 41 | let contents = fs::read_to_string(path)?; 42 | let new_contents = contents.replace(find, replace); 43 | let mut file = OpenOptions::new().write(true).truncate(true).open(path)?; 44 | file.write_all(new_contents.as_bytes())?; 45 | Ok(()) 46 | } 47 | 48 | pub fn create_directory(path: &str) -> std::io::Result<()> { 49 | std::fs::create_dir_all(path) 50 | } 51 | -------------------------------------------------------------------------------- /src/internal/install.rs: -------------------------------------------------------------------------------- 1 | use crate::functions::partition::umount; 2 | use crate::internal::*; 3 | use std::process::Command; 4 | 5 | pub fn install(pkgs: Vec<&str>) { 6 | exec_eval( 7 | Command::new("pacstrap").arg("/mnt").args(&pkgs).status(), 8 | format!("Install packages {}", pkgs.join(", ")).as_str(), 9 | ); 10 | umount("/mnt/dev"); 11 | } 12 | -------------------------------------------------------------------------------- /src/internal/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod exec; 3 | pub mod files; 4 | pub mod install; 5 | pub mod returncode_eval; 6 | pub mod strings; 7 | 8 | pub use install::install; 9 | pub use returncode_eval::*; 10 | pub use strings::crash; 11 | 12 | #[macro_export] 13 | macro_rules! uwu { 14 | ($x:expr) => {{ 15 | let uwu: String = $x.to_string(); 16 | uwu.replace("l", "w") 17 | .replace("L", "W") 18 | .replace("r", "w") 19 | .replace("R", "W") 20 | .replace("na", "nya") 21 | .replace("Na", "Nya") 22 | .replace("NA", "NYA") 23 | }}; 24 | } 25 | -------------------------------------------------------------------------------- /src/internal/returncode_eval.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | pub fn exec_eval( 4 | return_code: std::result::Result, 5 | logmsg: &str, 6 | ) { 7 | match &return_code { 8 | Ok(_) => { 9 | log::info!("{}", logmsg); 10 | } 11 | Err(e) => { 12 | crash( 13 | format!("{} ERROR: {}", logmsg, e), 14 | return_code.unwrap_err().raw_os_error().unwrap(), 15 | ); 16 | } 17 | } 18 | } 19 | 20 | pub fn files_eval(return_code: std::result::Result<(), std::io::Error>, logmsg: &str) { 21 | match &return_code { 22 | Ok(_) => { 23 | log::info!("{}", logmsg); 24 | } 25 | Err(e) => { 26 | crash( 27 | format!("{} ERROR: {}", logmsg, e), 28 | return_code.unwrap_err().raw_os_error().unwrap(), 29 | ); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/internal/strings.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | pub fn crash>(a: S, b: i32) -> ! { 4 | log::error!("{}", a.as_ref()); 5 | exit(b); 6 | } 7 | -------------------------------------------------------------------------------- /src/logging.rs: -------------------------------------------------------------------------------- 1 | use crate::uwu; 2 | use flexi_logger::{style, DeferredNow, LogSpecification, Logger}; 3 | use lazy_static::lazy_static; 4 | use log::{Level, LevelFilter}; 5 | use std::env; 6 | use std::io::Write; 7 | 8 | lazy_static! { 9 | static ref UWU: bool = env::var("JADE_UWU").map(|v| v == "true").unwrap_or(false); 10 | static ref UWU_DEBUG: bool = env::var("JADE_UWU_DEBUG") 11 | .map(|v| v == "true") 12 | .unwrap_or(false); 13 | } 14 | 15 | pub fn init(verbosity: usize) { 16 | let log_specification = match verbosity { 17 | 0 => LogSpecification::builder() 18 | .default(LevelFilter::Info) 19 | .build(), 20 | 1 => LogSpecification::builder() 21 | .default(LevelFilter::Debug) 22 | .build(), 23 | _ => LogSpecification::builder() 24 | .default(LevelFilter::Trace) 25 | .build(), 26 | }; 27 | Logger::with(log_specification) 28 | .format(format_log_entry) 29 | .start() 30 | .unwrap(); 31 | } 32 | 33 | /// Formats a log entry with color 34 | fn format_log_entry( 35 | w: &mut dyn Write, 36 | now: &mut DeferredNow, 37 | record: &log::Record, 38 | ) -> std::io::Result<()> { 39 | let msg = record.args().to_string(); 40 | let level = record.level(); 41 | let msg = apply_uwu(level, msg); 42 | let (h, m, s) = now.now().time().as_hms(); 43 | write!( 44 | w, 45 | "[ {} ] {}:{}:{} {}", 46 | style(level).paint(level.to_string()), 47 | h, 48 | m, 49 | s, 50 | msg 51 | ) 52 | } 53 | 54 | /// Applies uwu if the required environment variables are set 55 | fn apply_uwu(level: Level, msg: String) -> String { 56 | match level { 57 | Level::Error | Level::Warn | Level::Info => { 58 | if *UWU { 59 | uwu!(msg) 60 | } else { 61 | msg 62 | } 63 | } 64 | Level::Debug | Level::Trace => { 65 | if *UWU_DEBUG { 66 | uwu!(msg) 67 | } else { 68 | msg 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod args; 2 | mod functions; 3 | mod internal; 4 | mod logging; 5 | 6 | use crate::args::{BootloaderSubcommand, Command, Opt, UsersSubcommand}; 7 | use crate::functions::*; 8 | use clap::Parser; 9 | 10 | fn main() { 11 | human_panic::setup_panic!(); 12 | let opt: Opt = Opt::parse(); 13 | logging::init(opt.verbose); 14 | match opt.command { 15 | Command::Partition(args) => { 16 | let mut partitions = args.partitions; 17 | partition::partition( 18 | args.device, 19 | args.mode, 20 | args.efi, 21 | &mut partitions, 22 | args.unakite, 23 | ); 24 | } 25 | Command::InstallBase(args) => { 26 | base::install_base_packages(args.kernel); 27 | } 28 | Command::GenFstab => { 29 | base::genfstab(); 30 | } 31 | Command::SetupTimeshift => base::setup_timeshift(), 32 | Command::Bootloader { subcommand } => match subcommand { 33 | BootloaderSubcommand::GrubEfi { efidir } => { 34 | base::install_bootloader_efi(efidir); 35 | } 36 | BootloaderSubcommand::GrubLegacy { device } => { 37 | base::install_bootloader_legacy(device); 38 | } 39 | }, 40 | Command::Locale(args) => { 41 | locale::set_locale(args.locales.join(" ")); 42 | locale::set_keyboard(&args.keyboard); 43 | locale::set_timezone(&args.timezone); 44 | } 45 | Command::Networking(args) => { 46 | if args.ipv6 { 47 | network::create_hosts(); 48 | network::enable_ipv6() 49 | } else { 50 | network::create_hosts(); 51 | } 52 | network::set_hostname(&args.hostname); 53 | } 54 | Command::Zram => { 55 | base::install_zram(); 56 | } 57 | Command::Users { subcommand } => match subcommand { 58 | UsersSubcommand::NewUser(args) => { 59 | users::new_user( 60 | &args.username, 61 | args.hasroot, 62 | &args.password, 63 | true, 64 | &args.shell, 65 | ); 66 | } 67 | UsersSubcommand::RootPass { password } => { 68 | users::root_pass(&password); 69 | } 70 | }, 71 | Command::Nix => { 72 | base::install_homemgr(); 73 | } 74 | Command::Flatpak => { 75 | base::install_flatpak(); 76 | } 77 | Command::Unakite(args) => { 78 | unakite::setup_unakite( 79 | &args.root, 80 | &args.oldroot, 81 | args.efi, 82 | &args.efidir, 83 | &args.bootdev, 84 | ); 85 | } 86 | Command::Config { config } => { 87 | crate::internal::config::read_config(config); 88 | } 89 | Command::Desktops { desktop } => { 90 | desktops::install_desktop_setup(desktop); 91 | } 92 | } 93 | } 94 | --------------------------------------------------------------------------------