From 29657e511312fb821bf4d2812d0bcd394c973b2b Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 5 Oct 2022 08:53:56 -0700 Subject: [PATCH 01/21] global(refactor): init refactor --- .gitignore | 6 +- Cargo.lock | 1288 +++++++++++++++++++++++++++++++++++ Cargo.toml | 31 + src/cli.rs | 10 + src/commands/init/mod.rs | 56 ++ src/commands/install/mod.rs | 80 +++ src/commands/issue/mod.rs | 13 + src/commands/mod.rs | 20 + src/lib.rs | 25 + src/main.rs | 22 + src/util.rs | 55 ++ 11 files changed, 1605 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/cli.rs create mode 100644 src/commands/init/mod.rs create mode 100644 src/commands/install/mod.rs create mode 100644 src/commands/issue/mod.rs create mode 100644 src/commands/mod.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/util.rs diff --git a/.gitignore b/.gitignore index 49dc793..fecae32 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,8 @@ dist .popcorn # my list -**/TODO \ No newline at end of file +**/TODO + +# Added by cargo + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..411fe30 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1288 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" + +[[package]] +name = "async-compression" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "3.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "console" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "generator" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc184cace1cea8335047a471cc1da80f18acf8a76f3bab2028d499e328948ec7" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-glue" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" +dependencies = [ + "libc", + "log", + "ndk", + "ndk-context", + "ndk-macro", + "ndk-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "ndk-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ndk-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "popcorn" +version = "0.1.8" +dependencies = [ + "anyhow", + "async-compression", + "clap", + "console", + "dirs", + "state", + "tokio", + "webbrowser", +] + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" +dependencies = [ + "cty", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "state" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" +dependencies = [ + "loom", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" + +[[package]] +name = "thiserror" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +dependencies = [ + "ansi_term", + "matchers", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d62aa75495ab67cdc273d0b95cc76bcedfea2ba28338a4cf9b4137949dfac5" +dependencies = [ + "jni", + "ndk-glue", + "objc", + "raw-window-handle", + "url", + "web-sys", + "widestring", + "winapi", +] + +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec" +dependencies = [ + "windows_aarch64_msvc 0.32.0", + "windows_i686_gnu 0.32.0", + "windows_i686_msvc 0.32.0", + "windows_x86_64_gnu 0.32.0", + "windows_x86_64_msvc 0.32.0", +] + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..05fd638 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "popcorn" +version = "0.1.8" +edition = "2021" +license = "MIT" +authors = ["Matt"] +description = "An all-system package manager." +repository = "https://github.com/punctuations/popcorn" +#build = "build.rs" +# +#[package.metadata.winres] +#ProductName = "Popcorn" +#OriginalFilename = "popcorn.exe" +#LegalCopyright = "© 2022 Matt" +#FileDescription = "An all-system package manager." + +[[bin]] +name = "popcorn" + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dirs = "4.0.0" +state = "0.5.3" +anyhow = "1.0.59" +console = "0.15.1" +webbrowser = "0.8.0" +clap = { version = "3.2.16", features = ["derive"] } +async-compression = { version = "0.3.14", features = ["tokio", "gzip"] } +tokio = { version = "1.20.1", features = ["full"] } \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..a860faf --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,10 @@ +// use clap::{App, crate_authors, crate_description, crate_version}; +// +// +// pub fn build_cli() -> App<'static> { +// App::new("{{bin}}") +// .version(crate_version!()) +// .author(crate_authors!()) +// .about(crate_description!()) +// .subcommands(vec![]) +// } \ No newline at end of file diff --git a/src/commands/init/mod.rs b/src/commands/init/mod.rs new file mode 100644 index 0000000..fa4a6f9 --- /dev/null +++ b/src/commands/init/mod.rs @@ -0,0 +1,56 @@ +use clap::Parser; +use anyhow::Result; + +use std::path::Path; +use std::fs::File; +use std::io::prelude::*; +use std::env; + +use crate::util::{SEP, Print}; + +#[derive(Debug, Parser)] +pub struct Options { + #[clap( + short = 'f', + long = "force", + help = "Overwrite existing config file." + )] + force: bool, + #[clap( + short = 'p', + long = "packed", + help = "Generate a packed configuration file." + )] + packed: bool, +} + + +fn create_config(is_packed: bool) -> Result<()> { + let current = env::current_dir()?; + let binding = current.to_string_lossy(); + let cwd_name = binding.split(SEP).last().unwrap(); + let mut file = File::create(".kernelrc")?; + + let packed_json = format!("{{\n\"kernel\": \"{}\",\n\"kernel_type\": \"packed\",\n\"dev_cmd\": \"popcorn dev\",\n\"seed_cmd\": \"go build -o @dest\",\n\"advanced\": {{\n\"dev_node\": \"-dev\"\n}}\n}}", cwd_name).into_bytes(); + let unpacked_json = format!("{{\n\t\"kernel_name\": \"{}\",\n\t\"kernel_type\": \"unpacked\",\n\t\"unpacked_husk\": \"python @local/popcorn.py @args\",\n\t\"dev_cmd\": \"popcorn dev\",\n\t\"seed_cmd\": \"cp -r * @dest\",\n\t\"advanced\": {{\n\t\t\"dev_node\": \"-dev\"\n\t}}\n}}", cwd_name).into_bytes(); + + if is_packed { + file.write_all(&*packed_json)?; + } else { + file.write_all(&*unpacked_json)?; + } + + Ok(()) +} + +pub async fn handle(options: Options) -> Result<()> { + let _ = if !Path::new(".kernelrc").exists() { + create_config(options.packed) + } else if options.force { + create_config(options.packed) + } else { + Print::info(".kernelrc already exists; no changes made.") + }; + + Ok(()) +} \ No newline at end of file diff --git a/src/commands/install/mod.rs b/src/commands/install/mod.rs new file mode 100644 index 0000000..d69a927 --- /dev/null +++ b/src/commands/install/mod.rs @@ -0,0 +1,80 @@ +use clap::Parser; + +use crate::util::{DEV_DIR, PROD_DIR, Print}; + +#[derive(Debug, Parser)] +pub struct Options { + #[clap( + short = 'd', + long = "dev", + help = "Install development kernels." + )] + dev: bool, +} + +#[derive(Debug, Deserialize)] +struct AdvancedConfig { + os: [string; 3], + dev_node: string, +} + +#[derive(Debug, Deserialize)] +struct Config { + kernel_name: string, + kernel_type: string, + unpacked_husk: string, + dev_cmd: string, + seed_cmd: string, + advanced: AdvancedConfig, +} + +const CONFIG: Config = {}; + +fn initialize_globals() -> Result<(), &str> { + let kernel_path = Path::new("./kernelrc"); + + let file = File::open(kernel_path)?; + let config = serde_json::from_reader(file); + + match config { + Ok(config) => { + CONFIG = config; + Ok(()) + }, + Err(error) => { + Print::error(format!("File not found {}", error)); + Err("File not found.") + } + } +} + +pub async fn handle(options: Options) -> Result<()> { + match initialize_globals() { + () => (), + Err(err) => Ok(()) // exit after error is printed + } + + kernel_name = CONFIG.kernel_name; + + if !kernel_name { + Print::error("Please include a kernel_name in the config file."); + Ok(()) + } + + if options.dev { + if DEV_DIR == "" { + Print::error("An error occurred while loading the DEV_DIR"); + Ok(()) + } + } else { + if PROD_DIR == "" { + Print::error("An error occurred while loading the PROD_DIR"); + Ok(()) + } + } + + + // ... + + Ok(()) +} \ No newline at end of file diff --git a/src/commands/issue/mod.rs b/src/commands/issue/mod.rs new file mode 100644 index 0000000..612f7ae --- /dev/null +++ b/src/commands/issue/mod.rs @@ -0,0 +1,13 @@ +use clap::Parser; +use anyhow::Result; + +use webbrowser; + +#[derive(Debug, Parser)] +pub struct Options {} + +pub async fn handle(_options: Options) -> Result<()> { + let _ = webbrowser::open("https://github.com/punctuations/popcorn/issues/new"); + + Ok(()) +} \ No newline at end of file diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..96078e3 --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,20 @@ +pub mod issue; +pub mod init; +use anyhow::Result; + +use clap::Subcommand; + +// use crate::state::State; + +#[derive(Debug, Subcommand)] +pub enum Commands { + Issue(issue::Options), + Init(init::Options) +} + +pub async fn handle_command(command: Commands) -> Result<()> { + match command { + Commands::Issue(options) => issue::handle(options).await, + Commands::Init(options) => init::handle(options).await, + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..040865e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,25 @@ +pub mod commands; +pub mod util; + +use clap::Parser; +use commands::Commands; + +#[derive(Debug, Parser)] +#[clap( +name = "popcorn", +about = "A all-system package manager.", +version, +author +)] +pub struct CLI { + #[clap(subcommand)] + pub commands: Commands, + + #[clap( + short = 'd', + long = "debug", + help = "Print debug information", + global = true + )] + pub debug: bool, +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..dbea9fe --- /dev/null +++ b/src/main.rs @@ -0,0 +1,22 @@ +use clap::Parser; +use anyhow::Result; + +use popcorn::commands::handle_command; +use popcorn::{util, CLI}; + +mod cli; + +#[tokio::main] +async fn main() -> Result<()> { + // create a new CLI instance + let cli = CLI::parse(); + + if let Err(error) = handle_command(cli.commands).await { + // log::error!("{}", error); + std::process::exit(1); + } + + util::clear(); + + Ok(()) +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..e2c55fc --- /dev/null +++ b/src/util.rs @@ -0,0 +1,55 @@ +use std::borrow::{Cow, ToOwned}; +use anyhow::Result; +use console::style; +use std::env; +use dirs; + +use std::path::{Display, MAIN_SEPARATOR, Path, PathBuf}; + +pub const SEP: char = MAIN_SEPARATOR; + +pub const DEV_DIR: String = match env::current_dir() { + Ok(path) => path.join(".popcorn").into_os_string().into_string().unwrap(), + Err(_err) => "".to_owned(), +}; + +pub const PROD_DIR: String = match dirs::home_dir() { + Some(path) => path.join(".popcorn").into_os_string().into_string().unwrap(), + None => "".to_owned(), +}; + + +pub fn clear() { + let term = console::Term::stdout(); + term.show_cursor().ok(); +} + +pub struct Print; + +impl Print { + fn print_color(notation: &str, message: String, color: u8) -> Result<()> { + println!("{}: {}", style(notation).color256(color), message); + + Ok(()) + } + + pub fn error(message: String) -> Result<()> { + Self::print_color("[ERROR]", message, 1) + } + + pub fn success(message: String) -> Result<()> { + Self::print_color("[SUCCESS]", message, 48) + } + + pub fn warn(message: String) -> Result<()> { + Self::print_color("[WARN]", message, 10) + } + + pub fn info(message: &str) -> Result<()> { + Self::print_color("[INFO]", message.to_string(), 4) + } + + pub fn event(message: String) -> Result<()> { + Self::print_color("[INFO]", message, 57) + } +} From de7d86f1a3f1fd1bc6c49af693f33f8af14ffc5e Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 7 Oct 2022 17:57:24 -0700 Subject: [PATCH 02/21] chore: remove translated files --- _utils/styled_print.py | 22 ------- cmds/__init__.py | 0 popcorn.py | 132 ----------------------------------------- popcorn.spec | 44 -------------- requirements.txt | 3 - 5 files changed, 201 deletions(-) delete mode 100644 _utils/styled_print.py delete mode 100644 cmds/__init__.py delete mode 100644 popcorn.py delete mode 100644 popcorn.spec delete mode 100644 requirements.txt diff --git a/_utils/styled_print.py b/_utils/styled_print.py deleted file mode 100644 index 2b72850..0000000 --- a/_utils/styled_print.py +++ /dev/null @@ -1,22 +0,0 @@ -def error(text): - print(f" error -  {text}") - - -def success(text): - print(f" success -  {text}") - - -def warning(text): - print(f" warning -  {text}") - - -def info(text): - print(f" info -  {text}") - - -def event(text): - print(f" event -  {text}") - - -def debug(text): - print(f" debug -  {text}") diff --git a/cmds/__init__.py b/cmds/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/popcorn.py b/popcorn.py deleted file mode 100644 index 42566d9..0000000 --- a/popcorn.py +++ /dev/null @@ -1,132 +0,0 @@ -import json -import os -import sys - -from _utils import styled_print - -alias = ["-a", "--alias"] -dev = ["-d", "--dev"] -debug = ["--debug"] -help_flag = ["--help", "-h"] -version_flag = ["--version", "-v"] - -ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) - - -def resource_path(relative_path): - """ Get absolute path to resource, works for dev and for PyInstaller """ - try: - # PyInstaller creates a temp folder and stores path in _MEIPASS - base_path = sys._MEIPASS - except Exception: - base_path = ROOT_DIR - - return os.path.join(base_path, relative_path) - - -def load_cmds(has_debug_flag: list[str]): - styled_print.info("begin load") if has_debug_flag else None - cmds = os.listdir(resource_path("cmds")) # get all files in /cmds - cmds = [s for s in cmds if s[0] not in "_."] # remove hidden files - cmds = [s[:-3] for s in cmds] # remove '.py' - cmds_dict = {} # init cmds_dict - - styled_print.info("before command load loop") if has_debug_flag else None - for s in cmds: - cmds_dict[s] = __import__("cmds" + "." + s, fromlist=["*"]) - styled_print.info(f"imported {s}") if has_debug_flag else None - - # sort by key - styled_print.info("return cmds") if has_debug_flag else None - return cmds_dict - - -def popcorn(command): - has_alias_flag = [element for element in alias if (element in command)] - # get positional index where it is in list - has_dev_flag = [element for element in dev if (element in command)] - dev_index = command.index(has_dev_flag[0]) if len(has_dev_flag) >= 1 else 0 - has_debug_flag = [element for element in debug if (element in command)] - debug_index = command.index(has_debug_flag[0]) if len(has_debug_flag) >= 1 else 0 - has_help_flag = [element for element in help_flag if (element in command)] - help_index = command.index(has_help_flag[0]) if len(has_help_flag) >= 1 else 0 - has_version_flag = [element for element in version_flag if (element in command)] - - styled_print.info("before load") if has_debug_flag else None - cmds = load_cmds(has_debug_flag) - styled_print.info("after load") if has_debug_flag else None - - cmd_with_debug = 0 if not has_debug_flag or debug_index != 0 else debug_index + 1 - - styled_print.debug(f"ran with args {command}: {len(command)}") if has_debug_flag else None - - if has_alias_flag: - styled_print.debug("running alias command") if has_debug_flag else None - - print("function pop () { eval $(popcorn $@); }") - elif has_version_flag: - f = open(resource_path("blame.json")) - blame = json.load(f) - f.close() - styled_print.info(blame["version"]) - elif has_dev_flag and len(command) == 1: - styled_print.debug("running dev command from flag") if has_debug_flag else None - # * Run popcorn command based on .kernelrc options and pass in the path - - try: - f = open("./.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Please create a .kernelrc file.") - sys.exit(0) - - run_dev = getattr(cmds["dev"], "dev") - try: - dev_command = config["dev_cmd"] - os.system(dev_command) - except KeyError: - run_dev(command[dev_index + 1:] if len(command) >= 2 else ["-l", "."]) - elif has_help_flag: - styled_print.debug("running help command from flag") if has_debug_flag else None - - run_help = getattr(cmds["help"], "help") - run_help(command[help_index + 1:]) - elif command[cmd_with_debug] == 'theater': - # alias for theatre - styled_print.debug("running theatre command") if has_debug_flag else None - - run_install = getattr(cmds["theatre"], "theatre") - run_install(command[cmd_with_debug + 1:] if command[cmd_with_debug + 1:] != ['--debug'] else []) - elif command[cmd_with_debug] == 'uninstall': - # alias for theatre - styled_print.debug("running remove command") if has_debug_flag else None - - run_install = getattr(cmds["remove"], "remove") - run_install(command[cmd_with_debug + 1:] if command[cmd_with_debug + 1:] != ['--debug'] else []) - else: - # if argument is passed in - if len(command) >= 1: - # if command does not exist go to build command. - try: - run_command = getattr(cmds[command[cmd_with_debug]], command[cmd_with_debug]) - - styled_print.debug(f"running {command[cmd_with_debug]} command") if has_debug_flag else None - run_command(command[cmd_with_debug + 1:] if command[cmd_with_debug + 1:] != ['--debug'] else []) - except KeyError: - styled_print.debug("running build command as fallback") if has_debug_flag else None - - # command is: popcorn /path/to/files - run_build = getattr(cmds["build"], "build") - run_build(command) - else: - styled_print.debug("running install command from no args") if has_debug_flag else None - - # command is: popcorn - # * Run install command - run_install = getattr(cmds["install"], "install") - run_install([]) - - -if __name__ == '__main__': - popcorn(sys.argv[1:]) diff --git a/popcorn.spec b/popcorn.spec deleted file mode 100644 index 7e595ea..0000000 --- a/popcorn.spec +++ /dev/null @@ -1,44 +0,0 @@ -# -*- mode: python ; coding: utf-8 -*- - - -block_cipher = None - - -a = Analysis( - ['popcorn.py'], - pathex=[], - binaries=[], - datas=[('cmds', './cmds'), ('blame.json', '.')], - hiddenimports=['watchdog.events', 'watchdog.observers', 'webbrowser', 'requests'], - hookspath=[], - hooksconfig={}, - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, - noarchive=False, -) -pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) - -exe = EXE( - pyz, - a.scripts, - a.binaries, - a.zipfiles, - a.datas, - [], - name='popcorn', - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=True, - upx_exclude=[], - runtime_tmpdir=None, - console=True, - disable_windowed_traceback=False, - argv_emulation=False, - target_arch=None, - codesign_identity=None, - entitlements_file=None, -) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 72e9393..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -watchdog==2.1.8 - -requests~=2.28.1 \ No newline at end of file From beab2a6772ec3b0567fa83267ede2fa13fb88d3c Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 18 Oct 2022 16:41:50 -0700 Subject: [PATCH 03/21] =?UTF-8?q?feat(install):=20=C2=BD=20completed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Cargo.lock | 8 +- Cargo.toml | 8 +- src/cli.rs | 10 -- src/commands/init/mod.rs | 37 +++---- src/commands/install/mod.rs | 205 +++++++++++++++++++++++++++--------- src/commands/mod.rs | 5 +- src/main.rs | 6 +- src/util.rs | 99 +++++++++++++---- 9 files changed, 271 insertions(+), 108 deletions(-) delete mode 100644 src/cli.rs diff --git a/.gitignore b/.gitignore index fecae32..8200993 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # editors .idea .vscode +.fleet # python **/__pycache__ diff --git a/Cargo.lock b/Cargo.lock index 411fe30..b758646 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,6 +610,10 @@ dependencies = [ "clap", "console", "dirs", + "log", + "serde", + "serde_derive", + "serde_json", "state", "tokio", "webbrowser", @@ -776,9 +780,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" dependencies = [ "itoa", "ryu", diff --git a/Cargo.toml b/Cargo.toml index 05fd638..02bb5e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,15 @@ name = "popcorn" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +log = "0.4.17" dirs = "4.0.0" state = "0.5.3" +serde = "1.0.117" anyhow = "1.0.59" console = "0.15.1" webbrowser = "0.8.0" +serde_json = "1.0.59" +serde_derive = "1.0.117" +tokio = { version = "1.20.1", features = ["full"] } clap = { version = "3.2.16", features = ["derive"] } -async-compression = { version = "0.3.14", features = ["tokio", "gzip"] } -tokio = { version = "1.20.1", features = ["full"] } \ No newline at end of file +async-compression = { version = "0.3.14", features = ["tokio", "gzip"] } \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index a860faf..0000000 --- a/src/cli.rs +++ /dev/null @@ -1,10 +0,0 @@ -// use clap::{App, crate_authors, crate_description, crate_version}; -// -// -// pub fn build_cli() -> App<'static> { -// App::new("{{bin}}") -// .version(crate_version!()) -// .author(crate_authors!()) -// .about(crate_description!()) -// .subcommands(vec![]) -// } \ No newline at end of file diff --git a/src/commands/init/mod.rs b/src/commands/init/mod.rs index fa4a6f9..7c93b11 100644 --- a/src/commands/init/mod.rs +++ b/src/commands/init/mod.rs @@ -1,50 +1,43 @@ -use clap::Parser; use anyhow::Result; +use clap::Parser; -use std::path::Path; +use std::env; use std::fs::File; use std::io::prelude::*; -use std::env; +use std::path::Path; -use crate::util::{SEP, Print}; +use crate::util::{Print, SEP}; #[derive(Debug, Parser)] pub struct Options { - #[clap( - short = 'f', - long = "force", - help = "Overwrite existing config file." - )] + #[clap(short = 'f', long = "force", help = "Overwrite existing config file.")] force: bool, #[clap( - short = 'p', - long = "packed", - help = "Generate a packed configuration file." + short = 'p', + long = "packed", + help = "Generate a packed configuration file." )] packed: bool, } - -fn create_config(is_packed: bool) -> Result<()> { - let current = env::current_dir()?; +fn create_config(is_packed: bool) -> () { + let current = env::current_dir().unwrap(); let binding = current.to_string_lossy(); let cwd_name = binding.split(SEP).last().unwrap(); - let mut file = File::create(".kernelrc")?; + let mut file = File::create(".kernelrc").unwrap(); let packed_json = format!("{{\n\"kernel\": \"{}\",\n\"kernel_type\": \"packed\",\n\"dev_cmd\": \"popcorn dev\",\n\"seed_cmd\": \"go build -o @dest\",\n\"advanced\": {{\n\"dev_node\": \"-dev\"\n}}\n}}", cwd_name).into_bytes(); let unpacked_json = format!("{{\n\t\"kernel_name\": \"{}\",\n\t\"kernel_type\": \"unpacked\",\n\t\"unpacked_husk\": \"python @local/popcorn.py @args\",\n\t\"dev_cmd\": \"popcorn dev\",\n\t\"seed_cmd\": \"cp -r * @dest\",\n\t\"advanced\": {{\n\t\t\"dev_node\": \"-dev\"\n\t}}\n}}", cwd_name).into_bytes(); if is_packed { - file.write_all(&*packed_json)?; + file.write_all(&*packed_json).unwrap(); } else { - file.write_all(&*unpacked_json)?; + file.write_all(&*unpacked_json).unwrap(); } - - Ok(()) } pub async fn handle(options: Options) -> Result<()> { - let _ = if !Path::new(".kernelrc").exists() { + if !Path::new(".kernelrc").exists() { create_config(options.packed) } else if options.force { create_config(options.packed) @@ -53,4 +46,4 @@ pub async fn handle(options: Options) -> Result<()> { }; Ok(()) -} \ No newline at end of file +} diff --git a/src/commands/install/mod.rs b/src/commands/install/mod.rs index d69a927..50e7b91 100644 --- a/src/commands/install/mod.rs +++ b/src/commands/install/mod.rs @@ -1,80 +1,191 @@ +use anyhow::Result; use clap::Parser; -use crate::util::{DEV_DIR, PROD_DIR, Print}; +use std::env; +use std::fs::OpenOptions; +use std::io::Write; +use std::path::PathBuf; +use std::process::Command; + +use crate::util::{clear, get_config, Config, Print, DEV_DIR, PROD_DIR}; #[derive(Debug, Parser)] pub struct Options { - #[clap( - short = 'd', - long = "dev", - help = "Install development kernels." - )] + #[clap(long = "dev", help = "Install development kernels.")] dev: bool, } -#[derive(Debug, Deserialize)] -struct AdvancedConfig { - os: [string; 3], - dev_node: string, -} +fn apply_changes() -> Result<(), String> { + if cfg!(target_os = "windows") { + match Command::new(".").arg("$profile").output() { + Ok(_) => { + Print::success("Installed kernel!"); + return Ok(()); + } + Err(err) => return Err(err.to_string()), + } + } else { + // source from .shellrc and .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let profile_path = home.join(".profile"); -#[derive(Debug, Deserialize)] -struct Config { - kernel_name: string, - kernel_type: string, - unpacked_husk: string, - dev_cmd: string, - seed_cmd: string, - advanced: AdvancedConfig, + if rc_path.exists() { + // source rc_path + if let Ok(mut child) = Command::new(shell.split("/").last().unwrap()) + .arg("-c") + .arg(format!("source {}", rc_path.display())) + .spawn() + { + child.wait().unwrap(); + Print::success("Succesfully installed kernel!"); + return Ok(()); + } else { + Print::info("Please restart terminal to apply changes"); + return Ok(()); + } + } else { + // source .profile + if let Ok(mut child) = Command::new(shell.split("/").last().unwrap()) + .arg("-c") + .arg(format!("source {}", profile_path.display())) + .spawn() + { + child.wait().unwrap(); + Print::success("Succesfully installed kernel!"); + return Ok(()); + } else { + Print::info("Please restart terminal to apply changes"); + return Ok(()); + } + } + } } -const CONFIG: Config = {}; +fn install_dev() -> Result<(), String> { + if DEV_DIR() == "" { + return Err("An error occurred while loading the DEV_DIR".to_string()); + } + + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + if !PATH.contains(&DEV_DIR()) { + if cfg!(target_os = "windows") { + // add to the path + match Command::new("[Environment]::SetEnvironmentVariable('PATH',") + .args([format!("$env:PATH{},", DEV_DIR()), "'User')".to_string()]) + .output() + { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + } + Command::new("$env:PATH") + .args(["+=", format!("'{}'", DEV_DIR()).as_str()]) + .output() + .unwrap(); -fn initialize_globals() -> Result<(), &str> { - let kernel_path = Path::new("./kernelrc"); + match apply_changes() { + Ok(_) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + // edit .shellrc or .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; - let file = File::open(kernel_path)?; - let config = serde_json::from_reader(file); + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; - match config { - Ok(config) => { - CONFIG = config; - Ok(()) - }, - Err(error) => { - Print::error(format!("File not found {}", error)); - Err("File not found.") + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let profile_path = home.join(".profile"); + + if rc_path.exists() { + // edit rc_path + let mut file = match OpenOptions::new().write(true).append(true).open(rc_path) { + Ok(file) => file, + Err(_e) => return Err(".profile not found".to_string()), + }; + + file.write(format!("\nexport PATH=$PATH{}\n", DEV_DIR()).as_bytes()) + .unwrap(); + } else { + // edit .profile + let mut file = match OpenOptions::new() + .write(true) + .append(true) + .open(profile_path) + { + Ok(file) => file, + Err(_e) => return Err(".profile not found".to_string()), + }; + + file.write(format!("\nexport PATH=$PATH{}\n", DEV_DIR()).as_bytes()) + .unwrap(); + } + + match apply_changes() { + Ok(_) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } } + } else { + Print::info("Kernel already installed"); + return Ok(()); } } pub async fn handle(options: Options) -> Result<()> { - match initialize_globals() { - () => (), - Err(err) => Ok(()) // exit after error is printed + let CONFIG: Config; + + match get_config() { + Ok(loaded_config) => CONFIG = loaded_config, + Err(_) => return Ok(()), // exit after error is printed } - kernel_name = CONFIG.kernel_name; + let kernel_name: &str = &CONFIG.kernel_name; - if !kernel_name { + if kernel_name == "" { Print::error("Please include a kernel_name in the config file."); - Ok(()) + return Ok(()); } if options.dev { - if DEV_DIR == "" { - Print::error("An error occurred while loading the DEV_DIR"); - Ok(()) + match install_dev() { + Ok(_) => (), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } } } else { - if PROD_DIR == "" { + if PROD_DIR() == "" { Print::error("An error occurred while loading the PROD_DIR"); - Ok(()) + return Ok(()); } + // ... + println!("install_prod") } - - // ... - Ok(()) -} \ No newline at end of file +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 96078e3..0503076 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,5 +1,6 @@ pub mod issue; pub mod init; +pub mod install; use anyhow::Result; use clap::Subcommand; @@ -9,12 +10,14 @@ use clap::Subcommand; #[derive(Debug, Subcommand)] pub enum Commands { Issue(issue::Options), - Init(init::Options) + Init(init::Options), + Install(install::Options), } pub async fn handle_command(command: Commands) -> Result<()> { match command { Commands::Issue(options) => issue::handle(options).await, Commands::Init(options) => init::handle(options).await, + Commands::Install(options) => install::handle(options).await, } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index dbea9fe..f3c2e36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,16 @@ -use clap::Parser; use anyhow::Result; +use clap::Parser; use popcorn::commands::handle_command; use popcorn::{util, CLI}; -mod cli; - #[tokio::main] async fn main() -> Result<()> { // create a new CLI instance let cli = CLI::parse(); if let Err(error) = handle_command(cli.commands).await { - // log::error!("{}", error); + log::error!("{}", error); std::process::exit(1); } diff --git a/src/util.rs b/src/util.rs index e2c55fc..7056bf0 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,55 +1,114 @@ -use std::borrow::{Cow, ToOwned}; use anyhow::Result; use console::style; -use std::env; use dirs; +use std::env; + +use serde_derive::Deserialize; -use std::path::{Display, MAIN_SEPARATOR, Path, PathBuf}; +use std::any::{type_name, Any}; +use std::path::MAIN_SEPARATOR; pub const SEP: char = MAIN_SEPARATOR; -pub const DEV_DIR: String = match env::current_dir() { - Ok(path) => path.join(".popcorn").into_os_string().into_string().unwrap(), - Err(_err) => "".to_owned(), -}; +#[derive(Debug, Deserialize, Default)] +pub struct AdvancedConfig { + pub os: [String; 3], + pub dev_node: String, +} + +#[derive(Debug, Deserialize, Default)] +pub struct Config { + pub kernel_name: String, + pub kernel_type: String, + pub unpacked_husk: String, + pub dev_cmd: String, + pub seed_cmd: String, + pub advanced: AdvancedConfig, +} + +pub fn get_config() -> Result { + let file: String; + + match read_file(".kernelrc") { + Ok(contents) => file = contents, + Err(error) => { + Print::error(format!("File not found: {}", error).as_str()); + return Err(error); + } + } + + // OK to panic here + let config: Config = serde_json::from_str(&file).unwrap(); + + Ok(config) +} -pub const PROD_DIR: String = match dirs::home_dir() { - Some(path) => path.join(".popcorn").into_os_string().into_string().unwrap(), - None => "".to_owned(), -}; +pub fn DEV_DIR() -> String { + match env::current_dir() { + Ok(path) => { + return path + .join(".popcorn") + .into_os_string() + .into_string() + .unwrap() + } + Err(_) => return "".to_string(), + } +} +pub fn PROD_DIR() -> String { + match dirs::home_dir() { + Some(path) => { + return path + .join(".popcorn") + .into_os_string() + .into_string() + .unwrap() + } + None => return "".to_string(), + } +} pub fn clear() { let term = console::Term::stdout(); term.show_cursor().ok(); } +pub fn type_of(_: T) -> &'static str { + type_name::() +} + +pub fn read_file(file_name: &str) -> Result { + match std::fs::read_to_string(file_name) { + Ok(contents) => Ok(contents), + Err(error) => Err(error.to_string()), + } +} + pub struct Print; impl Print { - fn print_color(notation: &str, message: String, color: u8) -> Result<()> { + fn print_color(notation: &str, message: &str, color: u8) -> () { println!("{}: {}", style(notation).color256(color), message); - - Ok(()) } - pub fn error(message: String) -> Result<()> { + pub fn error(message: &str) -> () { Self::print_color("[ERROR]", message, 1) } - pub fn success(message: String) -> Result<()> { + pub fn success(message: &str) -> () { Self::print_color("[SUCCESS]", message, 48) } - pub fn warn(message: String) -> Result<()> { + pub fn warn(message: &str) -> () { Self::print_color("[WARN]", message, 10) } - pub fn info(message: &str) -> Result<()> { - Self::print_color("[INFO]", message.to_string(), 4) + pub fn info(message: &str) -> () { + Self::print_color("[INFO]", message, 4) } - pub fn event(message: String) -> Result<()> { + pub fn event(message: &str) -> () { Self::print_color("[INFO]", message, 57) } } From d93d910388ec548a8bbe00c2e0f59e65b19819bd Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 19 Oct 2022 10:04:28 -0700 Subject: [PATCH 04/21] feat(install): translate install command --- blame.json | 10 +-- src/commands/install/mod.rs | 158 ++++++++++++++++++++++++++++++++---- src/util.rs | 2 +- 3 files changed, 149 insertions(+), 21 deletions(-) diff --git a/blame.json b/blame.json index 35912c0..21ad6d0 100644 --- a/blame.json +++ b/blame.json @@ -1,6 +1,6 @@ { - "name": "🍿 Popcorn", - "description": "Say good-bye to complicated installation instructions.", - "version": "1.0.7", - "source": "https://github.com/punctuations/popcorn" -} \ No newline at end of file + "name": "🍿 Popcorn", + "description": "Say good-bye to complicated installation instructions.", + "version": "1.0.8", + "source": "https://github.com/punctuations/popcorn" +} diff --git a/src/commands/install/mod.rs b/src/commands/install/mod.rs index 50e7b91..e25f306 100644 --- a/src/commands/install/mod.rs +++ b/src/commands/install/mod.rs @@ -1,13 +1,13 @@ use anyhow::Result; use clap::Parser; -use std::env; -use std::fs::OpenOptions; +use std::fs::{create_dir_all, read_to_string, OpenOptions}; use std::io::Write; use std::path::PathBuf; use std::process::Command; +use std::{env, path::Path}; -use crate::util::{clear, get_config, Config, Print, DEV_DIR, PROD_DIR}; +use crate::util::{get_config, Config, Print, DEV_DIR, PROD_DIR}; #[derive(Debug, Parser)] pub struct Options { @@ -70,16 +70,11 @@ fn apply_changes() -> Result<(), String> { } } -fn install_dev() -> Result<(), String> { +fn install_dev(PATH: String) -> Result<(), String> { if DEV_DIR() == "" { return Err("An error occurred while loading the DEV_DIR".to_string()); } - let PATH: String = match env::var("PATH") { - Ok(path) => path, - Err(_) => "".to_string(), - }; - if !PATH.contains(&DEV_DIR()) { if cfg!(target_os = "windows") { // add to the path @@ -155,6 +150,113 @@ fn install_dev() -> Result<(), String> { } } +fn install_prod(PATH: String, butter_file: PathBuf) -> Result<(), String> { + if PROD_DIR() == "" { + Print::error("An error occurred while loading the PROD_DIR"); + return Ok(()); + } + + if cfg!(target_os = "windows") { + // add system-wide + match Command::new("[Environment]::SetEnvironmentVariable('PATH',") + .args([ + format!("$env:PATH{},", butter_file.display()), + "'User')".to_string(), + ]) + .output() + { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + } + // add for current terminal session + Command::new("$env:PATH") + .args(["+=", format!("'{}'", butter_file.display()).as_str()]) + .output() + .unwrap(); + + match apply_changes() { + Ok(_) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + // edit .shellrc or .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let rc_filepath = rc_path.clone(); + let profile_path = home.join(".profile"); + let profile_filepath = profile_path.clone(); + + if rc_path.exists() { + let contents = read_to_string(rc_path).expect("Unable to read rc file [prod:500]"); + if !contents.contains(". $HOME/.kernels/butter.sh") { + // open file in truncate + let mut file = match OpenOptions::new() + .write(true) + .truncate(true) + .open(rc_filepath) + { + Ok(file) => file, + Err(_e) => return Err("rc_path not found".to_string()), + }; + + file.write(format!(". $HOME/.kernels/butter.sh\n{}", contents).as_bytes()) + .unwrap(); + + match apply_changes() { + Ok(_) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + Print::info("Kernel already installed"); + return Ok(()); + } + } else { + let contents = + read_to_string(profile_path).expect("Unable to read profile file [prod:500]"); + if !contents.contains(". $HOME/.kernels/butter.sh") { + // open file in truncate + let mut file = match OpenOptions::new() + .write(true) + .truncate(true) + .open(profile_filepath) + { + Ok(file) => file, + Err(_e) => return Err("profile path not found".to_string()), + }; + + file.write(format!(". $HOME/.kernels/butter.sh\n{}", contents).as_bytes()) + .unwrap(); + + match apply_changes() { + Ok(_) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + Print::info("Kernel already installed!"); + return Ok(()); + } + } + } +} + pub async fn handle(options: Options) -> Result<()> { let CONFIG: Config; @@ -170,8 +272,33 @@ pub async fn handle(options: Options) -> Result<()> { return Ok(()); } + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + // CREATE BUTTER.SH FILE IF DOESNT EXIST + let dir = &PROD_DIR(); + let prod_directory = Path::new(dir); + let butter_file = prod_directory.join("butter.sh"); + + let butter_filepath = butter_file.clone(); + + if !butter_file.exists() { + if !prod_directory.exists() { + create_dir_all(PROD_DIR())?; + } + + let mut butter = OpenOptions::new() + .write(true) + .create(true) + .open(butter_file)?; + + write!(butter, "#!/bin/bash\n").unwrap(); + } + if options.dev { - match install_dev() { + match install_dev(PATH) { Ok(_) => (), Err(err) => { Print::error(err.as_str()); @@ -179,12 +306,13 @@ pub async fn handle(options: Options) -> Result<()> { } } } else { - if PROD_DIR() == "" { - Print::error("An error occurred while loading the PROD_DIR"); - return Ok(()); + match install_prod(PATH, butter_filepath) { + Ok(_) => (), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } } - // ... - println!("install_prod") } Ok(()) diff --git a/src/util.rs b/src/util.rs index 7056bf0..56171ae 100644 --- a/src/util.rs +++ b/src/util.rs @@ -60,7 +60,7 @@ pub fn PROD_DIR() -> String { match dirs::home_dir() { Some(path) => { return path - .join(".popcorn") + .join(".kernels") .into_os_string() .into_string() .unwrap() From ad207871b5f1efe571e483068278cabd92ebda0f Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 26 Oct 2022 11:54:02 -0700 Subject: [PATCH 05/21] feat(commands): add build --- .gitignore | 2 +- src/commands/build/mod.rs | 360 ++++++++++++++++++++++++++++++++++++ src/commands/install/mod.rs | 18 +- src/commands/mod.rs | 7 +- src/util.rs | 26 ++- 5 files changed, 398 insertions(+), 15 deletions(-) create mode 100644 src/commands/build/mod.rs diff --git a/.gitignore b/.gitignore index 8200993..02aa44c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ venv # pyinstaller -build +/build dist # internal diff --git a/src/commands/build/mod.rs b/src/commands/build/mod.rs new file mode 100644 index 0000000..7c22096 --- /dev/null +++ b/src/commands/build/mod.rs @@ -0,0 +1,360 @@ +use anyhow::Result; +use clap::Parser; + +use std::env::{self, current_dir}; +use std::fs::{self, File, OpenOptions}; +use std::io::{prelude::*, BufReader}; +use std::os::unix::prelude::PermissionsExt; +use std::path::PathBuf; +use std::process::Command; +use std::thread; +use std::{fs::create_dir_all, fs::remove_dir_all, path::Path}; + +use crate::util::{get_config, AdvancedConfig, Config, Print, DEV_DIR, PATHSEP, PROD_DIR, SEP}; + +use super::install::apply_changes; + +#[derive(Debug, Parser)] +#[clap(about = "Used to create production-level kernels.")] +pub struct Options { + #[clap( + short = 'o', + long = "output", + help = "Change the output directory of the kernel." + )] + output: Option, +} + +fn seed_cmd(config_seed: String, kernel_type: String, output: PathBuf) -> Result<(), &'static str> { + let seed_cmd: String; + + // initiliaze seed_cmd + if kernel_type == "unpacked" { + // make unpacked output dir and run seed cmd + create_dir_all(output.clone()); + seed_cmd = config_seed.to_lowercase().replace( + "@dest", + &output.clone().into_os_string().into_string().unwrap(), + ); + } else { + // put into prod_dir (is only one file & output is callable name) + seed_cmd = config_seed.to_lowercase().replace("@dest", &PROD_DIR()); + } + + // run new seed cmd + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + if let Ok(mut child) = Command::new(shell.split("/").last().unwrap()) + .arg("-c") + .arg(seed_cmd) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + return Err("An error occured while executing the seed_cmd"); + } + return Ok(()); + } else { + return Err("An error occured while running the seed_cmd"); + } +} + +fn ensure_butter_in_path() -> Result<(), String> { + // edit .shellrc or .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let profile_path = home.join(".profile"); + + if rc_path.exists() { + // edit rc_path + let mut file = match OpenOptions::new().read(true).append(true).open(rc_path) { + Ok(file) => file, + Err(_e) => return Err(".profile not found".to_string()), + }; + let mut reader = BufReader::new(file.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if !contents.contains(&format!(". $HOME{SEP}.kernels{SEP}butter.sh\n", SEP = SEP)) { + file.write(format!(". $HOME{SEP}.kernels{SEP}butter.sh\n", SEP = SEP).as_bytes()) + .unwrap(); + + match apply_changes() { + Ok(_) => return Ok(()), + Err(err) => { + return Err(err); + } + } + } + } else { + // edit .profile + let mut file = match OpenOptions::new() + .read(true) + .append(true) + .open(profile_path) + { + Ok(file) => file, + Err(_e) => return Err(".profile not found".to_string()), + }; + let mut reader = BufReader::new(file.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if !contents.contains(&format!(". $HOME{SEP}.kernels{SEP}butter.sh\n", SEP = SEP)) { + file.write(format!(". $HOME{SEP}.kernels{SEP}butter.sh\n", SEP = SEP).as_bytes()) + .unwrap(); + + match apply_changes() { + Ok(_) => return Ok(()), + Err(err) => { + return Err(err); + } + } + } + } + + Ok(()) +} + +fn update_path(kernel_type: String, output: String) -> Result<(), String> { + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let updated_path = if kernel_type.to_lowercase() == "packed" { + PROD_DIR() + } else { + format!("{}{}{}", &PROD_DIR(), SEP, output) + }; + + if !PATH.contains(&updated_path) { + if cfg!(target_os = "windows") { + // add to the path + match Command::new("[Environment]::SetEnvironmentVariable('PATH',") + .args([ + format!("$env:PATH{}{},", PATHSEP, &updated_path), + "'User')".to_string(), + ]) + .output() + { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + } + Command::new("$env:PATH") + .args(["+=", format!("'{}{}'", PATHSEP, &updated_path).as_str()]) + .output() + .unwrap(); + + match apply_changes() { + Ok(_) => return Ok(()), + Err(err) => { + return Err(err); + } + } + } else { + let dir = &PROD_DIR(); + let prod_directory = Path::new(dir); + let butter_file = prod_directory.join("butter.sh").clone(); + + if !butter_file.exists() { + let mut butter = OpenOptions::new() + .write(true) + .create(true) + .open(butter_file) + .unwrap(); + + write!( + butter, + "{}", + format!("#!/bin/bash\nexport PATH=$PATH{}{}", PATHSEP, &updated_path) + ) + .unwrap(); + } else { + let mut butter = OpenOptions::new() + .read(true) + .append(true) + .open(butter_file) + .unwrap(); + + let mut reader = BufReader::new(butter.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if !contents.contains(&format!("export PATH=$PATH{}{}", PATHSEP, &updated_path)) { + write!( + butter, + "{}", + format!("\nexport PATH=$PATH{}{}", PATHSEP, &updated_path) + ); + } + } + + match ensure_butter_in_path() { + Ok(_) => return Ok(()), + Err(err) => return Err(err), + } + } + } + + Ok(()) +} + +pub fn build_thread(output: String, config: Config) -> Result<(), ()> { + let output_path = Path::new(&PROD_DIR()).join(output.clone()); + let output_dir = if config.kernel_type.to_lowercase() == "unpacked" { + output_path.clone() + } else { + Path::new(&PROD_DIR()).to_path_buf() + }; + + // delete dir if already exists + if config.kernel_type.to_lowercase() == "unpacked" && output_path.exists() { + // prod dir already exists, only need to remove possible unpacked conflict. + match remove_dir_all(output_path) { + Ok(_) => (), + Err(_err) => { + Print::error("Unable to remove existing kernel."); + return Err(()); + } + } + } + + match seed_cmd( + config.seed_cmd.clone(), + config.kernel_type.clone(), + output_dir, + ) { + Ok(_) => (), + Err(err) => { + Print::error(err); + return Err(()); + } + } + + // make unpacked kernel from unpacked husk + if config.kernel_type.to_lowercase() == "unpacked" { + let stem_cmd = config + .unpacked_husk + .expect("unpacked_husk not found for unpacked kernel.") + .to_lowercase() + .replace("@args", "$@") + .replace( + "@local", + if output.clone().ends_with("/") { + output.split_at(output.len() - 1).0 + } else { + &output + }, + ); + + // enter stem command to unpacked husk (make file and enter data) + let mut husk = match File::create(format!( + "{dir}{sep}{output}{husk_name}", + dir = &PROD_DIR(), + sep = &SEP, + output = output, + husk_name = config.kernel_name.clone() + )) { + Ok(file) => file, + Err(err) => { + Print::error(format!("Could not create husk file; {}", err).as_str()); + return Err(()); + } + }; + + match husk.write_all(format!("#!/bin/bash\n{}", stem_cmd).as_bytes()) { + Ok(_) => (), + Err(_err) => { + Print::error("An error occured while writing to husk file."); + return Err(()); + } + } + } + + // change permissions of file to be accessible by all + let mut perms = fs::metadata(format!( + "{dir}{sep}{output}{husk_name}", + dir = &PROD_DIR(), + sep = &SEP, + output = output, + husk_name = config.kernel_name.clone() + )) + .unwrap() + .permissions(); + perms.set_mode(0o511); + + if perms.mode() != 0o511 { + Print::error("Unable to change file permissions."); + return Err(()); + } + + Print::info("Compiled successfully."); + + match update_path(config.kernel_type.clone(), output.clone()) { + Ok(_) => { + Print::success(format!("Successfully built {}", config.kernel_name).as_str()); + return Ok(()); + } + Err(err) => { + Print::error(err.as_str()); + return Err(()); + } + } +} + +pub async fn handle(options: Options) -> Result<()> { + let CONFIG: Config; + + match get_config() { + Ok(loaded_config) => CONFIG = loaded_config, + Err(_) => return Ok(()), // exit after error is printed + } + + let kernel_name = &CONFIG.kernel_name; + let kernel_type = &CONFIG.kernel_type; + let seed_cmd = &CONFIG.seed_cmd; + + if kernel_name == "" { + Print::error("Please include a kernel_name in the config file."); + return Ok(()); + } else if kernel_type == "" { + Print::error("Please include a kernel_type in the config file."); + return Ok(()); + } else if seed_cmd == "" { + Print::error("Please include a seed_cmd in the config file."); + return Ok(()); + } + + let dir = &PROD_DIR(); + let prod_path = Path::new(dir); + + if !prod_path.exists() { + create_dir_all(prod_path); + } + + let mut output = match options.output { + Some(path) => path, + None => kernel_name.to_string(), + }; + + if kernel_type.to_lowercase() == "unpacked" { + output = output + &SEP.to_string(); + } + + thread::spawn(|| build_thread(output, CONFIG)).join(); + + Ok(()) +} diff --git a/src/commands/install/mod.rs b/src/commands/install/mod.rs index e25f306..c5aa6e9 100644 --- a/src/commands/install/mod.rs +++ b/src/commands/install/mod.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use std::process::Command; use std::{env, path::Path}; -use crate::util::{get_config, Config, Print, DEV_DIR, PROD_DIR}; +use crate::util::{get_config, Config, Print, DEV_DIR, PATHSEP, PROD_DIR}; #[derive(Debug, Parser)] pub struct Options { @@ -15,7 +15,7 @@ pub struct Options { dev: bool, } -fn apply_changes() -> Result<(), String> { +pub fn apply_changes() -> Result<(), String> { if cfg!(target_os = "windows") { match Command::new(".").arg("$profile").output() { Ok(_) => { @@ -79,14 +79,17 @@ fn install_dev(PATH: String) -> Result<(), String> { if cfg!(target_os = "windows") { // add to the path match Command::new("[Environment]::SetEnvironmentVariable('PATH',") - .args([format!("$env:PATH{},", DEV_DIR()), "'User')".to_string()]) + .args([ + format!("$env:PATH{}{},", PATHSEP, DEV_DIR()), + "'User')".to_string(), + ]) .output() { Ok(_) => (), Err(e) => return Err(e.to_string()), } Command::new("$env:PATH") - .args(["+=", format!("'{}'", DEV_DIR()).as_str()]) + .args(["+=", format!("'{}{}'", PATHSEP, DEV_DIR()).as_str()]) .output() .unwrap(); @@ -160,7 +163,7 @@ fn install_prod(PATH: String, butter_file: PathBuf) -> Result<(), String> { // add system-wide match Command::new("[Environment]::SetEnvironmentVariable('PATH',") .args([ - format!("$env:PATH{},", butter_file.display()), + format!("$env:PATH{}{},", PATHSEP, butter_file.display()), "'User')".to_string(), ]) .output() @@ -170,7 +173,10 @@ fn install_prod(PATH: String, butter_file: PathBuf) -> Result<(), String> { } // add for current terminal session Command::new("$env:PATH") - .args(["+=", format!("'{}'", butter_file.display()).as_str()]) + .args([ + "+=", + format!("'{}{}'", PATHSEP, butter_file.display()).as_str(), + ]) .output() .unwrap(); diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 0503076..a0a178b 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,6 +1,7 @@ -pub mod issue; +pub mod build; pub mod init; pub mod install; +pub mod issue; use anyhow::Result; use clap::Subcommand; @@ -12,6 +13,7 @@ pub enum Commands { Issue(issue::Options), Init(init::Options), Install(install::Options), + Build(build::Options), } pub async fn handle_command(command: Commands) -> Result<()> { @@ -19,5 +21,6 @@ pub async fn handle_command(command: Commands) -> Result<()> { Commands::Issue(options) => issue::handle(options).await, Commands::Init(options) => init::handle(options).await, Commands::Install(options) => install::handle(options).await, + Commands::Build(options) => build::handle(options).await, } -} \ No newline at end of file +} diff --git a/src/util.rs b/src/util.rs index 56171ae..6ce175e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -9,21 +9,26 @@ use std::any::{type_name, Any}; use std::path::MAIN_SEPARATOR; pub const SEP: char = MAIN_SEPARATOR; +pub const PATHSEP: char = if cfg!(target_os = "windows") { + ';' +} else { + ':' +}; #[derive(Debug, Deserialize, Default)] pub struct AdvancedConfig { - pub os: [String; 3], - pub dev_node: String, + pub os: Option<[String; 3]>, + pub dev_node: Option, } #[derive(Debug, Deserialize, Default)] pub struct Config { pub kernel_name: String, pub kernel_type: String, - pub unpacked_husk: String, + pub unpacked_husk: Option, pub dev_cmd: String, pub seed_cmd: String, - pub advanced: AdvancedConfig, + pub advanced: Option, } pub fn get_config() -> Result { @@ -37,8 +42,17 @@ pub fn get_config() -> Result { } } - // OK to panic here - let config: Config = serde_json::from_str(&file).unwrap(); + let config: Config = match serde_json::from_str(&file) { + Ok(json) => json, + Err(_) => Config { + kernel_name: "".to_string(), + kernel_type: "".to_string(), + unpacked_husk: Some("".to_string()), + dev_cmd: "".to_string(), + seed_cmd: "".to_string(), + advanced: None, + }, + }; Ok(config) } From 41a02d56b56cdb776679deb41130f8c35bcd0b40 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 28 Oct 2022 00:25:18 -0700 Subject: [PATCH 06/21] feat(cmds): add remove command --- .kernelrc | 24 ++-- src/commands/init/mod.rs | 1 + src/commands/install/mod.rs | 15 ++- src/commands/issue/mod.rs | 5 +- src/commands/mod.rs | 3 + src/commands/remove/mod.rs | 244 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 17 +-- 7 files changed, 281 insertions(+), 28 deletions(-) create mode 100644 src/commands/remove/mod.rs diff --git a/.kernelrc b/.kernelrc index d8f9719..ba599aa 100644 --- a/.kernelrc +++ b/.kernelrc @@ -1,11 +1,15 @@ { - "kernel_name": "popcorn", - "kernel_type": "unpacked", - "unpacked_husk": "python @local/popcorn.py @args", - "dev_cmd": "popcorn dev", - "seed_cmd": "cp -r * @dest", - "advanced": { - "os": ["mac", "windows", "linux"], - "dev_node": "-dev" - } -} \ No newline at end of file + "kernel_name": "popcorn", + "kernel_type": "unpacked", + "unpacked_husk": "python @local/popcorn.py @args", + "dev_cmd": "popcorn dev", + "seed_cmd": "cp -r * @dest", + "advanced": { + "os": [ + "mac", + "windows", + "linux" + ], + "dev_node": "-dev" + } +} diff --git a/src/commands/init/mod.rs b/src/commands/init/mod.rs index 7c93b11..8597fdc 100644 --- a/src/commands/init/mod.rs +++ b/src/commands/init/mod.rs @@ -9,6 +9,7 @@ use std::path::Path; use crate::util::{Print, SEP}; #[derive(Debug, Parser)] +#[clap(about = "Initialize configuration file.")] pub struct Options { #[clap(short = 'f', long = "force", help = "Overwrite existing config file.")] force: bool, diff --git a/src/commands/install/mod.rs b/src/commands/install/mod.rs index c5aa6e9..85a0ae1 100644 --- a/src/commands/install/mod.rs +++ b/src/commands/install/mod.rs @@ -10,8 +10,9 @@ use std::{env, path::Path}; use crate::util::{get_config, Config, Print, DEV_DIR, PATHSEP, PROD_DIR}; #[derive(Debug, Parser)] +#[clap(about = "Install a kernel into PATH.")] pub struct Options { - #[clap(long = "dev", help = "Install development kernels.")] + #[clap(short = 'd', long = "dev", help = "Install development kernels.")] dev: bool, } @@ -122,8 +123,10 @@ fn install_dev(PATH: String) -> Result<(), String> { Err(_e) => return Err(".profile not found".to_string()), }; - file.write(format!("\nexport PATH=$PATH{}\n", DEV_DIR()).as_bytes()) - .unwrap(); + file.write( + format!("\nexport PATH=$PATH{SEP}{}\n", DEV_DIR(), SEP = PATHSEP).as_bytes(), + ) + .unwrap(); } else { // edit .profile let mut file = match OpenOptions::new() @@ -135,8 +138,10 @@ fn install_dev(PATH: String) -> Result<(), String> { Err(_e) => return Err(".profile not found".to_string()), }; - file.write(format!("\nexport PATH=$PATH{}\n", DEV_DIR()).as_bytes()) - .unwrap(); + file.write( + format!("\nexport PATH=$PATH{SEP}{}\n", DEV_DIR(), SEP = PATHSEP).as_bytes(), + ) + .unwrap(); } match apply_changes() { diff --git a/src/commands/issue/mod.rs b/src/commands/issue/mod.rs index 612f7ae..c895c24 100644 --- a/src/commands/issue/mod.rs +++ b/src/commands/issue/mod.rs @@ -1,13 +1,14 @@ -use clap::Parser; use anyhow::Result; +use clap::Parser; use webbrowser; #[derive(Debug, Parser)] +#[clap(about = "Report an issue.")] pub struct Options {} pub async fn handle(_options: Options) -> Result<()> { let _ = webbrowser::open("https://github.com/punctuations/popcorn/issues/new"); Ok(()) -} \ No newline at end of file +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index a0a178b..9795222 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -2,6 +2,7 @@ pub mod build; pub mod init; pub mod install; pub mod issue; +pub mod remove; use anyhow::Result; use clap::Subcommand; @@ -14,6 +15,7 @@ pub enum Commands { Init(init::Options), Install(install::Options), Build(build::Options), + Remove(remove::Options), } pub async fn handle_command(command: Commands) -> Result<()> { @@ -22,5 +24,6 @@ pub async fn handle_command(command: Commands) -> Result<()> { Commands::Init(options) => init::handle(options).await, Commands::Install(options) => install::handle(options).await, Commands::Build(options) => build::handle(options).await, + Commands::Remove(options) => remove::handle(options).await, } } diff --git a/src/commands/remove/mod.rs b/src/commands/remove/mod.rs new file mode 100644 index 0000000..7f33d22 --- /dev/null +++ b/src/commands/remove/mod.rs @@ -0,0 +1,244 @@ +use anyhow::Result; +use clap::Parser; + +use std::fmt::format; +use std::fs::{create_dir_all, read_to_string, OpenOptions}; +use std::io::{prelude::*, BufReader}; +use std::path::PathBuf; +use std::process::Command; +use std::{env, path::Path}; + +use crate::util::{Print, PATHSEP, PROD_DIR, SEP}; + +#[derive(Debug, Parser)] +#[clap(about = "Remove installed kernels.")] +pub struct Options { + #[clap(name = "name", help = "Name of kernel.")] + kernel_name: Option, + + #[clap(short = 'd', long = "dev", help = "Remove development kernels.")] + dev: bool, +} + +fn remove_windows( + removed_kernel: String, + PATH: String, + kernel_name: String, + is_dev: bool, +) -> Result { + // split paths + let paths: Vec<_> = PATH.split(PATHSEP).collect(); + + // filter array + let filter: Vec<_> = paths + .clone() + .into_iter() + .filter(|&x| !x.contains(&removed_kernel)) + .collect(); + + // join new filtered paths + let path_str = filter.join(&PATHSEP.to_string()); + + // set as the path + match Command::new("[Environment]::SetEnvironmentVariable('PATH',") + .arg(format!("'{}', 'User')", path_str)) + .output() + { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + } + Command::new("$env:PATH") + .args(["=", path_str.as_str()]) + .output() + .unwrap(); + + Ok(format!( + "Removed{dev} kernel {}!", + kernel_name, + dev = if is_dev { " dev" } else { "" } + )) +} + +fn remove_dev(kernel_name: String, PATH: String) -> Result { + if PATH.contains(&format!("{}{SEP}.popcorn", kernel_name, SEP = SEP)) { + if cfg!(target_os = "windows") { + return match remove_windows( + format!("{}{SEP}.popcorn", kernel_name, SEP = SEP), + PATH, + kernel_name, + true, + ) { + Ok(success) => Ok(success), + Err(err) => Err(err), + }; + } else { + // remove line from .shellrc or .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let profile_path = home.join(".profile"); + + // define removed kernel + let removed_kernel = format!("{}{SEP}.popcorn", kernel_name, SEP = SEP); + + if rc_path.exists() { + let mut file = match OpenOptions::new().read(true).write(true).open(rc_path) { + Ok(file) => file, + Err(_) => return Err("Unable to open rc file".to_string()), + }; + + let mut reader = BufReader::new(file.try_clone().unwrap()); + + // filter array + let filter: Vec<_> = reader + .lines() + .into_iter() + .map(|x| x.unwrap()) + .filter(|x| !x.contains(&removed_kernel)) + .collect(); + + // join contents into string + let contents_str = filter.join("\n"); + + // write to file + write!(file, "{}", contents_str); + } else { + let mut file = match OpenOptions::new().read(true).write(true).open(profile_path) { + Ok(file) => file, + Err(_) => return Err("Unable to open profile file".to_string()), + }; + + let reader = BufReader::new(file.try_clone().unwrap()); + + // filter array + let filter: Vec<_> = reader + .lines() + .into_iter() + .map(|x| x.unwrap()) + .filter(|x| !x.contains(&removed_kernel)) + .collect(); + + // join contents into string + let contents_str = filter.join("\n"); + + // write to file + write!(file, "{}", contents_str); + } + + Ok(format!("Removed dev kernel {}!", kernel_name)) + } + } else { + // kernel not in path: not installed, or has been installed but PATH not updated. + Err(format!("Kernel {} not installed.", kernel_name)) + } +} + +fn remove_prod(kernel_name: String, PATH: String) -> Result { + if PATH.contains(&format!( + "{PROD}{SEP}{}", + kernel_name, + SEP = SEP, + PROD = PROD_DIR() + )) { + if cfg!(target_os = "windows") { + return match remove_windows( + format!("{PROD}{SEP}{}", kernel_name, SEP = SEP, PROD = PROD_DIR()), + PATH, + kernel_name, + false, + ) { + Ok(success) => Ok(success), + Err(err) => Err(err), + }; + } else { + if kernel_name == "butter.sh" { + return Err("Not a kernel".to_string()); + } + + // define removed kernel + let removed_kernel = + format!("{PROD}{SEP}{}", kernel_name, SEP = SEP, PROD = PROD_DIR()); + + let dir = &PROD_DIR(); + let prod_directory = Path::new(dir); + let butter_file = prod_directory.join("butter.sh").clone(); + + let mut butter = match OpenOptions::new().read(true).write(true).open(butter_file) { + Ok(file) => file, + Err(_) => return Err("Unable to open butter file".to_string()), + }; + + let reader = BufReader::new(butter.try_clone().unwrap()); + + // filter array + let filter: Vec<_> = reader + .lines() + .into_iter() + .map(|x| x.unwrap()) + .filter(|x| !x.contains(&removed_kernel)) + .collect(); + + // join contents into string + let contents_str = filter.join("\n"); + + // write to file + write!(butter, "{}", contents_str); + + Ok(format!("Removed kernel {}!", kernel_name)) + } + } else { + // kernel not in path: not installed, or has been installed but PATH not updated. + Err(format!("Kernel {} not installed.", kernel_name)) + } +} + +pub async fn handle(options: Options) -> Result<()> { + let kernel_name = match options.kernel_name { + Some(name) => name, + None => { + Print::error("Please specify a kernel to remove."); + return Ok(()); + } + }; + + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + Print::info(format!("Removing {name}...", name = kernel_name).as_str()); + + if options.dev { + match remove_dev(kernel_name, PATH) { + Ok(success) => { + Print::success(success.as_str()); + Print::info("Please restart the terminal to apply changes."); + return Ok(()); + } + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + match remove_prod(kernel_name, PATH) { + Ok(success) => { + Print::success(success.as_str()); + Print::info("Please restart the terminal to apply changes."); + return Ok(()); + } + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 040865e..52ad03a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,20 +6,15 @@ use commands::Commands; #[derive(Debug, Parser)] #[clap( -name = "popcorn", -about = "A all-system package manager.", -version, -author + name = "popcorn", + about = "An all-system package manager.", + version, + author )] pub struct CLI { #[clap(subcommand)] pub commands: Commands, - #[clap( - short = 'd', - long = "debug", - help = "Print debug information", - global = true - )] + #[clap(long = "debug", help = "Print debug information", global = true)] pub debug: bool, -} \ No newline at end of file +} From 79633420fa40f9117abd5e5c8bfc7ba4d3456485 Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 2 Nov 2022 01:38:50 -0700 Subject: [PATCH 07/21] feat: add dev command --- Cargo.lock | 475 ++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 + src/commands/build/mod.rs | 17 +- src/commands/dev/mod.rs | 255 ++++++++++++++++++++ src/commands/mod.rs | 3 + src/util.rs | 17 +- src/util/blame.rs | 13 ++ 7 files changed, 755 insertions(+), 28 deletions(-) create mode 100644 src/commands/dev/mod.rs create mode 100644 src/util/blame.rs diff --git a/Cargo.lock b/Cargo.lock index b758646..2cdafcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,13 +8,31 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -44,7 +62,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -83,12 +101,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi 0.3.9", +] + [[package]] name = "clap" version = "3.2.22" @@ -128,6 +167,16 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "combine" version = "4.6.6" @@ -149,16 +198,22 @@ dependencies = [ "once_cell", "terminal_size", "unicode-width", - "winapi", + "winapi 0.3.9", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "crc32fast" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -167,6 +222,50 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "cxx" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "darling" version = "0.13.4" @@ -219,7 +318,7 @@ checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -228,6 +327,18 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "filetime" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "windows-sys 0.42.0", +] + [[package]] name = "flate2" version = "1.0.24" @@ -253,6 +364,41 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsevent" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" +dependencies = [ + "bitflags", + "fsevent-sys", +] + +[[package]] +name = "fsevent-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +dependencies = [ + "libc", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "futures-core" version = "0.3.24" @@ -278,9 +424,9 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -304,6 +450,30 @@ dependencies = [ "libc", ] +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi 0.3.9", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -330,6 +500,35 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inotify" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + [[package]] name = "itoa" version = "1.0.3" @@ -365,18 +564,43 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -393,7 +617,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -402,7 +626,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "generator", "scoped-tls", "serde", @@ -444,6 +668,25 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + [[package]] name = "mio" version = "0.8.4" @@ -452,8 +695,32 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi", - "windows-sys", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.36.1", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio 0.6.23", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] @@ -514,6 +781,54 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "net2" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "notify" +version = "4.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" +dependencies = [ + "bitflags", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio 0.6.23", + "mio-extras", + "walkdir", + "winapi 0.3.9", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -582,11 +897,11 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -607,10 +922,13 @@ version = "0.1.8" dependencies = [ "anyhow", "async-compression", + "chrono", "clap", "console", "dirs", "log", + "notify", + "regex", "serde", "serde_derive", "serde_json", @@ -707,6 +1025,8 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -758,6 +1078,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "serde" version = "1.0.145" @@ -807,6 +1133,15 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.9.0" @@ -820,7 +1155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -865,7 +1200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -903,6 +1238,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -928,7 +1274,7 @@ dependencies = [ "bytes", "libc", "memchr", - "mio", + "mio 0.8.4", "num_cpus", "once_cell", "parking_lot", @@ -936,7 +1282,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -965,7 +1311,7 @@ version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1078,10 +1424,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", - "winapi", + "winapi 0.3.9", "winapi-util", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1094,7 +1446,7 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] @@ -1165,7 +1517,7 @@ dependencies = [ "url", "web-sys", "widestring", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1174,6 +1526,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -1184,6 +1542,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -1196,7 +1560,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1231,6 +1595,27 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" version = "0.32.0" @@ -1243,6 +1628,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + [[package]] name = "windows_i686_gnu" version = "0.32.0" @@ -1255,6 +1646,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + [[package]] name = "windows_i686_msvc" version = "0.32.0" @@ -1267,6 +1664,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + [[package]] name = "windows_x86_64_gnu" version = "0.32.0" @@ -1279,6 +1682,18 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + [[package]] name = "windows_x86_64_msvc" version = "0.32.0" @@ -1290,3 +1705,19 @@ name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/Cargo.toml b/Cargo.toml index 02bb5e5..29bca4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,12 @@ name = "popcorn" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +regex = "1" log = "0.4.17" +chrono = "0.4" dirs = "4.0.0" state = "0.5.3" +notify = "4.0.0" serde = "1.0.117" anyhow = "1.0.59" console = "0.15.1" diff --git a/src/commands/build/mod.rs b/src/commands/build/mod.rs index 7c22096..8fd2264 100644 --- a/src/commands/build/mod.rs +++ b/src/commands/build/mod.rs @@ -25,20 +25,30 @@ pub struct Options { output: Option, } -fn seed_cmd(config_seed: String, kernel_type: String, output: PathBuf) -> Result<(), &'static str> { +pub fn seed_cmd( + config_seed: String, + kernel_type: String, + output: PathBuf, + is_dev: bool, +) -> Result<(), &'static str> { let seed_cmd: String; + let dest = if is_dev { DEV_DIR() } else { PROD_DIR() }; + // initiliaze seed_cmd if kernel_type == "unpacked" { // make unpacked output dir and run seed cmd - create_dir_all(output.clone()); + if !is_dev { + create_dir_all(output.clone()); + } + seed_cmd = config_seed.to_lowercase().replace( "@dest", &output.clone().into_os_string().into_string().unwrap(), ); } else { // put into prod_dir (is only one file & output is callable name) - seed_cmd = config_seed.to_lowercase().replace("@dest", &PROD_DIR()); + seed_cmd = config_seed.to_lowercase().replace("@dest", &dest); } // run new seed cmd @@ -236,6 +246,7 @@ pub fn build_thread(output: String, config: Config) -> Result<(), ()> { config.seed_cmd.clone(), config.kernel_type.clone(), output_dir, + false, ) { Ok(_) => (), Err(err) => { diff --git a/src/commands/dev/mod.rs b/src/commands/dev/mod.rs new file mode 100644 index 0000000..345a117 --- /dev/null +++ b/src/commands/dev/mod.rs @@ -0,0 +1,255 @@ +use anyhow::Result; +use clap::Parser; + +use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; +use regex::Regex; +use std::fs::{self, create_dir_all, remove_dir_all, File}; +use std::io::Write; +use std::os::unix::prelude::PermissionsExt; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::sync::mpsc::channel; +use std::time::Instant; +use std::{env, thread, time}; + +use crate::util::{blame::BLAME, get_config, Config, Print, DEV_DIR, SEP}; + +use super::build::seed_cmd; + +#[derive(Debug, Parser)] +#[clap(about = "Run a kernel in the dev enviorment.")] +pub struct Options { + #[clap( + short = 'l', + long = "listen", + help = "Change the directory that listens for updates." + )] + listen: Option, +} + +fn dev_compile(config: Config) -> Result<(), ()> { + // ignore error purposefully as dir may not already exist. + remove_dir_all(DEV_DIR()); + create_dir_all(DEV_DIR()); + + // run seed_cmd + match seed_cmd( + config.seed_cmd.clone(), + config.kernel_type.clone(), + Path::new(&DEV_DIR()).to_path_buf(), + true, + ) { + Ok(_) => (), + Err(err) => { + Print::error(err); + return Err(()); + } + } + + // make unpacked kernel from unpacked husk + if config.kernel_type.to_lowercase() == "unpacked" { + let stem_cmd = config + .unpacked_husk + .expect("unpacked_husk not found for unpacked kernel.") + .to_lowercase() + .replace("@args", "$@") + .replace("@local", &DEV_DIR()); + + // enter stem command to unpacked husk (make file and enter data) + let mut husk = match File::create(format!( + "{dir}{sep}{husk_name}", + dir = &DEV_DIR(), + sep = &SEP, + husk_name = config.kernel_name.clone() + )) { + Ok(file) => file, + Err(err) => { + Print::error(format!("Could not create husk file; {}", err).as_str()); + return Err(()); + } + }; + + match husk.write_all(format!("#!/bin/bash\n{}", stem_cmd).as_bytes()) { + Ok(_) => (), + Err(_err) => { + Print::error("An error occured while writing to husk file."); + return Err(()); + } + } + } + + // change permissions of file to be accessible by all + let mut perms = fs::metadata(format!( + "{dir}{sep}{husk_name}", + dir = &DEV_DIR(), + sep = &SEP, + husk_name = config.kernel_name.clone() + )) + .unwrap() + .permissions(); + perms.set_mode(0o511); + + if perms.mode() != 0o511 { + Print::error("Unable to change file permissions."); + return Err(()); + } + + Ok(()) +} + +fn thread_compile(config: Config) -> () { + let compile_start = Instant::now(); + + // compile logic -> bool success state + let completed = match dev_compile(config) { + Ok(_) => true, + Err(_) => false, + }; + + // format ending time + let compile_end = format!("{:?}", compile_start.elapsed()); + let regex = Regex::new(r"\d+").unwrap(); + let time_regex = Regex::new(r"(?i)[a-z]").unwrap(); + + let units = regex.replace_all(&compile_end, ""); + let time = time_regex.replace(&compile_end, "").parse::().unwrap(); + + if completed { + Print::success(&format!("Compiled in {:.2}{}", time, units.split_at(1).1)); + } +} + +fn check_ignorance(path: PathBuf) -> Result { + // if path is dir ignore :) + if path.is_dir() { + return Err(()); + } + + // if path is .kernelrc print warning + if path == Path::new("./.kernelrc").to_path_buf() { + Print::warn("Updates to config detected, to see up-to-date changes re-run this command."); + return Err(()); + } + + let file = match Command::new("git") + .args(["check-ignore", &format!("{}", path.display())]) + .stdout(Stdio::piped()) + .spawn() + { + Ok(output) => output.wait_with_output().unwrap(), + Err(_) => { + Print::error("Unable to check git ignore"); + return Err(()); + } + }; + + if file.stdout.is_empty() { + return Ok(path); + } else { + return Err(()); + } +} + +fn event_handler(event: DebouncedEvent, config: Config) -> () { + // filter events + match event { + DebouncedEvent::Rescan + | DebouncedEvent::NoticeRemove(_) + | DebouncedEvent::NoticeWrite(_) => return, + DebouncedEvent::Error(err, path) => { + Print::error(&format!("{} with path {:?}", err, path)); + return; + } + DebouncedEvent::Chmod(event) + | DebouncedEvent::Create(event) + | DebouncedEvent::Remove(event) + | DebouncedEvent::Rename(_, event) + | DebouncedEvent::Write(event) => { + match check_ignorance(Path::new(&format!("{}", event.display())).to_path_buf()) { + Ok(path) => format!("{}", path.display()), + Err(_) => return, + } + } + }; + + Print::event("Received compile event."); + thread::spawn(|| thread_compile(config)).join(); +} + +pub async fn handle(options: Options) -> Result<()> { + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + if !PATH.contains(&DEV_DIR()) { + Print::warn("Development kernel not installed.\n\t\tplease try: popcorn install --dev"); + return Ok(()); + } + + let CONFIG: Config; + + match get_config() { + Ok(loaded_config) => CONFIG = loaded_config, + Err(_) => return Ok(()), // exit after error is printed + } + + let kernel_name = &CONFIG.kernel_name; + let kernel_type = &CONFIG.kernel_type; + let seed_cmd = &CONFIG.seed_cmd; + + if kernel_name == "" { + Print::error("Please include a kernel_name in the config file."); + return Ok(()); + } else if kernel_type == "" { + Print::error("Please include a kernel_type in the config file."); + return Ok(()); + } else if seed_cmd == "" { + Print::error("Please include a seed_cmd in the config file."); + return Ok(()); + } + + // define dir to watch + let dir = match options.listen { + Some(dir) => dir, + None => ".".to_string(), + }; + + let listen_dir = Path::new(dir.as_str()); + + if !listen_dir.exists() { + Print::error("File or directory does not exist."); + return Ok(()); + } + + Print::bold(&format!( + "{popcorn} dev v{ver}", + popcorn = BLAME.name, + ver = BLAME.version + )); + + // Create a channel to receive the events. + let (sender, receiver) = channel(); + + // Create a watcher object, delivering debounced events. + // The notification back-end is selected based on the platform. + let mut watcher = watcher(sender, time::Duration::from_secs(2)).unwrap(); + + // Add a path to be watched. All files and directories at that path and + // below will be monitored for changes. + watcher.watch(listen_dir, RecursiveMode::Recursive).unwrap(); + + Print::info(&format!("Listening to {}", dir)); + + let config = CONFIG.clone(); + + // initial run w/o waiting for event + thread::spawn(|| thread_compile(config)).join(); + + loop { + match receiver.recv() { + Ok(event) => event_handler(event, CONFIG.clone()), + Err(e) => println!("watch error: {:?}", e), + } + } +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 9795222..a60f36c 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,4 +1,5 @@ pub mod build; +pub mod dev; pub mod init; pub mod install; pub mod issue; @@ -16,6 +17,7 @@ pub enum Commands { Install(install::Options), Build(build::Options), Remove(remove::Options), + Dev(dev::Options), } pub async fn handle_command(command: Commands) -> Result<()> { @@ -25,5 +27,6 @@ pub async fn handle_command(command: Commands) -> Result<()> { Commands::Install(options) => install::handle(options).await, Commands::Build(options) => build::handle(options).await, Commands::Remove(options) => remove::handle(options).await, + Commands::Dev(options) => dev::handle(options).await, } } diff --git a/src/util.rs b/src/util.rs index 6ce175e..1d936a1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use chrono::Local; use console::style; use dirs; use std::env; @@ -8,6 +9,8 @@ use serde_derive::Deserialize; use std::any::{type_name, Any}; use std::path::MAIN_SEPARATOR; +pub mod blame; + pub const SEP: char = MAIN_SEPARATOR; pub const PATHSEP: char = if cfg!(target_os = "windows") { ';' @@ -15,13 +18,13 @@ pub const PATHSEP: char = if cfg!(target_os = "windows") { ':' }; -#[derive(Debug, Deserialize, Default)] +#[derive(Debug, Deserialize, Default, Clone)] pub struct AdvancedConfig { pub os: Option<[String; 3]>, pub dev_node: Option, } -#[derive(Debug, Deserialize, Default)] +#[derive(Debug, Deserialize, Default, Clone)] pub struct Config { pub kernel_name: String, pub kernel_type: String, @@ -106,6 +109,10 @@ impl Print { println!("{}: {}", style(notation).color256(color), message); } + pub fn bold(message: &str) -> () { + println!("{}", style(message).bold()); + } + pub fn error(message: &str) -> () { Self::print_color("[ERROR]", message, 1) } @@ -123,6 +130,10 @@ impl Print { } pub fn event(message: &str) -> () { - Self::print_color("[INFO]", message, 57) + Self::print_color( + &format!("[EVENT @ {}]", Local::now().format("%H:%M:%S")), + message, + 13, + ) } } diff --git a/src/util/blame.rs b/src/util/blame.rs new file mode 100644 index 0000000..fd18ad2 --- /dev/null +++ b/src/util/blame.rs @@ -0,0 +1,13 @@ +pub struct Blame<'a> { + pub name: &'a str, + pub description: &'a str, + pub version: &'a str, + pub source: &'a str, +} + +pub const BLAME: Blame = Blame { + name: "🍿 Popcorn", + description: "Say good-bye to complicated installation instructions.", + version: "1.0.8", + source: "https://github.com/punctuations/popcorn", +}; From a47fd74a63cfb5089826db20c5c8f721282dfd8a Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 2 Nov 2022 23:27:12 -0700 Subject: [PATCH 08/21] feat(help): change default help --- Cargo.toml | 2 +- src/commands/mod.rs | 9 +++++++++ src/lib.rs | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 29bca4d..1c98401 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "popcorn" version = "0.1.8" edition = "2021" license = "MIT" -authors = ["Matt"] +authors = ["Matt "] description = "An all-system package manager." repository = "https://github.com/punctuations/popcorn" #build = "build.rs" diff --git a/src/commands/mod.rs b/src/commands/mod.rs index a60f36c..2851b62 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -10,13 +10,22 @@ use clap::Subcommand; // use crate::state::State; +static SUBCOMMAND_HELP: &str = "\ + \n 🍿 Popcorn  -> {name}\n {about}\n\n 💻 https://github.com/punctuations/popcorn\n\n  Usage \n\n {usage}\n\n  Options \n\n{options}"; + #[derive(Debug, Subcommand)] pub enum Commands { + #[clap(help_template = SUBCOMMAND_HELP)] Issue(issue::Options), + #[clap(help_template = SUBCOMMAND_HELP)] Init(init::Options), + #[clap(help_template = SUBCOMMAND_HELP)] Install(install::Options), + #[clap(help_template = SUBCOMMAND_HELP)] Build(build::Options), + #[clap(help_template = SUBCOMMAND_HELP)] Remove(remove::Options), + #[clap(help_template = SUBCOMMAND_HELP)] Dev(dev::Options), } diff --git a/src/lib.rs b/src/lib.rs index 52ad03a..ad9ba6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,9 @@ use commands::Commands; version, author )] +#[clap(help_template = "\ +\n 🍿 Popcorn  v{version}\n {about}\n\n 💻 https://github.com/punctuations/popcorn\n\n  Usage \n\n {usage}\n\n  Options \n\n{options}\n\n  Subcommands \n\n{subcommands} +")] pub struct CLI { #[clap(subcommand)] pub commands: Commands, From f18c6ce1608021a577b24919cd3f4ebbe00b760d Mon Sep 17 00:00:00 2001 From: matt Date: Thu, 3 Nov 2022 16:34:48 -0700 Subject: [PATCH 09/21] feat: update blame.rs to pull from cargo --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/util/blame.rs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2cdafcc..8cd152d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -918,7 +918,7 @@ checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "popcorn" -version = "0.1.8" +version = "2.0.0" dependencies = [ "anyhow", "async-compression", diff --git a/Cargo.toml b/Cargo.toml index 1c98401..21fb3a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "popcorn" -version = "0.1.8" +version = "2.0.0" edition = "2021" license = "MIT" authors = ["Matt "] diff --git a/src/util/blame.rs b/src/util/blame.rs index fd18ad2..83f826b 100644 --- a/src/util/blame.rs +++ b/src/util/blame.rs @@ -7,7 +7,7 @@ pub struct Blame<'a> { pub const BLAME: Blame = Blame { name: "🍿 Popcorn", - description: "Say good-bye to complicated installation instructions.", - version: "1.0.8", - source: "https://github.com/punctuations/popcorn", + description: env!("CARGO_PKG_DESCRIPTION"), + version: env!("CARGO_PKG_VERSION"), + source: env!("CARGO_PKG_REPOSITORY"), }; From 0c806b4be85d6eb6bcb4ce5fe68d39598547b103 Mon Sep 17 00:00:00 2001 From: matt Date: Sat, 5 Nov 2022 01:50:42 -0700 Subject: [PATCH 10/21] fix(dev): add dev_node into equation --- src/commands/dev/mod.rs | 44 ++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/commands/dev/mod.rs b/src/commands/dev/mod.rs index 345a117..b332eb3 100644 --- a/src/commands/dev/mod.rs +++ b/src/commands/dev/mod.rs @@ -39,7 +39,7 @@ fn dev_compile(config: Config) -> Result<(), ()> { Path::new(&DEV_DIR()).to_path_buf(), true, ) { - Ok(_) => (), + Ok(()) => (), Err(err) => { Print::error(err); return Err(()); @@ -60,7 +60,7 @@ fn dev_compile(config: Config) -> Result<(), ()> { "{dir}{sep}{husk_name}", dir = &DEV_DIR(), sep = &SEP, - husk_name = config.kernel_name.clone() + husk_name = config.kernel_name.clone(), )) { Ok(file) => file, Err(err) => { @@ -70,7 +70,7 @@ fn dev_compile(config: Config) -> Result<(), ()> { }; match husk.write_all(format!("#!/bin/bash\n{}", stem_cmd).as_bytes()) { - Ok(_) => (), + Ok(()) => (), Err(_err) => { Print::error("An error occured while writing to husk file."); return Err(()); @@ -78,12 +78,42 @@ fn dev_compile(config: Config) -> Result<(), ()> { } } + let dev_node = if config.advanced.is_some() && config.advanced.clone().unwrap().os.is_some() { + config.advanced.unwrap().dev_node.unwrap() + } else { + "-dev".to_string() + }; + + // rename husk_file to husk_file- + match fs::rename( + format!( + "{dir}{sep}{husk_name}", + dir = &DEV_DIR(), + sep = &SEP, + husk_name = config.kernel_name.clone() + ), + format!( + "{dir}{sep}{husk_name}{dev_node}", + dir = &DEV_DIR(), + sep = &SEP, + husk_name = config.kernel_name.clone(), + dev_node = dev_node + ), + ) { + Ok(()) => (), + Err(_) => { + Print::error("Unable to create dev_node"); + return Err(()); + } + } + // change permissions of file to be accessible by all let mut perms = fs::metadata(format!( - "{dir}{sep}{husk_name}", + "{dir}{sep}{husk_name}{dev_node}", dir = &DEV_DIR(), sep = &SEP, - husk_name = config.kernel_name.clone() + husk_name = config.kernel_name.clone(), + dev_node = dev_node )) .unwrap() .permissions(); @@ -102,8 +132,8 @@ fn thread_compile(config: Config) -> () { // compile logic -> bool success state let completed = match dev_compile(config) { - Ok(_) => true, - Err(_) => false, + Ok(()) => true, + Err(()) => false, }; // format ending time From 83dd99c759a54f0d98de59e958ac2c096d96251a Mon Sep 17 00:00:00 2001 From: matt Date: Sat, 5 Nov 2022 21:39:46 -0700 Subject: [PATCH 11/21] feat(cmd): add theatre/add/theater command --- Cargo.lock | 536 +++++++++++++++++++++++++++++++++++- Cargo.toml | 5 + src/commands/build/mod.rs | 106 +++++-- src/commands/install/mod.rs | 14 +- src/commands/mod.rs | 4 + src/commands/remove/mod.rs | 20 +- src/commands/theatre/mod.rs | 450 ++++++++++++++++++++++++++++++ src/util.rs | 75 ++++- 8 files changed, 1172 insertions(+), 38 deletions(-) create mode 100644 src/commands/theatre/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 8cd152d..4ee1edf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bitflags" version = "1.3.2" @@ -83,12 +89,39 @@ version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +[[package]] +name = "bzip2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cc" version = "1.0.73" @@ -201,6 +234,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -327,6 +370,24 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + [[package]] name = "filetime" version = "0.2.18" @@ -355,6 +416,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.1.0" @@ -399,11 +475,44 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] [[package]] name = "generator" @@ -429,6 +538,25 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -450,6 +578,77 @@ dependencies = [ "libc", ] +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -520,6 +719,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "iovec" version = "0.1.4" @@ -529,6 +737,12 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + [[package]] name = "itoa" version = "1.0.3" @@ -659,6 +873,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "miniz_oxide" version = "0.5.4" @@ -723,6 +943,24 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.7.0" @@ -875,6 +1113,51 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +[[package]] +name = "openssl" +version = "0.10.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_str_bytes" version = "6.3.0" @@ -916,6 +1199,18 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + [[package]] name = "popcorn" version = "2.0.0" @@ -926,15 +1221,20 @@ dependencies = [ "clap", "console", "dirs", + "flate2", "log", "notify", + "progress_bar", "regex", + "reqwest", "serde", "serde_derive", "serde_json", "state", + "tar", "tokio", "webbrowser", + "zip-extract", ] [[package]] @@ -981,6 +1281,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "progress_bar" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8e8843722ce55deb442e7d6476a97c081b8485ea2807b031d160f592f99037" +dependencies = [ + "lazy_static", +] + [[package]] name = "quote" version = "1.0.21" @@ -1045,6 +1354,52 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rustversion" version = "1.0.9" @@ -1066,6 +1421,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys 0.36.1", +] + [[package]] name = "scoped-tls" version = "1.0.0" @@ -1084,6 +1449,29 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +[[package]] +name = "security-framework" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.145" @@ -1115,6 +1503,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -1184,6 +1584,31 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -1296,6 +1721,30 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.9" @@ -1305,6 +1754,12 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.36" @@ -1367,6 +1822,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + [[package]] name = "unicode-bidi" version = "0.3.8" @@ -1411,6 +1872,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -1428,6 +1895,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -1465,6 +1942,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.83" @@ -1712,6 +2201,15 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -1721,3 +2219,37 @@ dependencies = [ "winapi 0.2.8", "winapi-build", ] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + +[[package]] +name = "zip" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +dependencies = [ + "byteorder", + "bzip2", + "crc32fast", + "flate2", + "thiserror", + "time", +] + +[[package]] +name = "zip-extract" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c5cc0309f6e81ab96c2b43d5e935025f8732c886690be8f78f68e06bad1d274" +dependencies = [ + "log", + "thiserror", + "zip", +] diff --git a/Cargo.toml b/Cargo.toml index 21fb3a2..bb70346 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ name = "popcorn" [dependencies] regex = "1" +flate2 = "1.0" +tar = "0.4.38" log = "0.4.17" chrono = "0.4" dirs = "4.0.0" @@ -31,8 +33,11 @@ serde = "1.0.117" anyhow = "1.0.59" console = "0.15.1" webbrowser = "0.8.0" +zip-extract = "0.1.1" serde_json = "1.0.59" +progress_bar = "1.0.3" serde_derive = "1.0.117" +reqwest = { version = "0.11", features = ["json"] } tokio = { version = "1.20.1", features = ["full"] } clap = { version = "3.2.16", features = ["derive"] } async-compression = { version = "0.3.14", features = ["tokio", "gzip"] } \ No newline at end of file diff --git a/src/commands/build/mod.rs b/src/commands/build/mod.rs index 8fd2264..0df604b 100644 --- a/src/commands/build/mod.rs +++ b/src/commands/build/mod.rs @@ -59,7 +59,7 @@ pub fn seed_cmd( if let Ok(mut child) = Command::new(shell.split("/").last().unwrap()) .arg("-c") - .arg(seed_cmd) + .arg(seed_cmd.clone()) .spawn() { let finished = child.wait().unwrap(); @@ -102,7 +102,7 @@ fn ensure_butter_in_path() -> Result<(), String> { .unwrap(); match apply_changes() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => { return Err(err); } @@ -127,7 +127,7 @@ fn ensure_butter_in_path() -> Result<(), String> { .unwrap(); match apply_changes() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => { return Err(err); } @@ -169,7 +169,7 @@ fn update_path(kernel_type: String, output: String) -> Result<(), String> { .unwrap(); match apply_changes() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => { return Err(err); } @@ -213,7 +213,7 @@ fn update_path(kernel_type: String, output: String) -> Result<(), String> { } match ensure_butter_in_path() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => return Err(err), } } @@ -222,7 +222,12 @@ fn update_path(kernel_type: String, output: String) -> Result<(), String> { Ok(()) } -pub fn build_thread(output: String, config: Config) -> Result<(), ()> { +pub fn build_thread( + output: String, + config: Config, + external: bool, + external_dir: String, +) -> Result<(), ()> { let output_path = Path::new(&PROD_DIR()).join(output.clone()); let output_dir = if config.kernel_type.to_lowercase() == "unpacked" { output_path.clone() @@ -234,24 +239,75 @@ pub fn build_thread(output: String, config: Config) -> Result<(), ()> { if config.kernel_type.to_lowercase() == "unpacked" && output_path.exists() { // prod dir already exists, only need to remove possible unpacked conflict. match remove_dir_all(output_path) { - Ok(_) => (), + Ok(()) => (), Err(_err) => { Print::error("Unable to remove existing kernel."); return Err(()); } } } - - match seed_cmd( - config.seed_cmd.clone(), - config.kernel_type.clone(), - output_dir, - false, - ) { - Ok(_) => (), - Err(err) => { - Print::error(err); - return Err(()); + if external { + // move files from /tmp/ to prod_dir + if config.kernel_type.to_lowercase() == "unpacked" { + let dir_contents = fs::read_dir(external_dir).unwrap(); + + for contents in dir_contents { + let file = contents.unwrap(); + + create_dir_all(output_dir.clone().as_os_str()); + + match fs::rename( + file.path(), + format!( + "{}{SEP}{}", + output_dir.clone().display(), + file.file_name().into_string().unwrap(), + SEP = SEP + ), + ) { + Ok(()) => (), + Err(_) => { + Print::error("Unable to move files."); + remove_dir_all(output_dir); + return Err(()); + } + } + } + } else { + match fs::rename( + format!( + "{}{SEP}{}", + external_dir, + config.kernel_name.clone(), + SEP = SEP + ), + format!( + "{}{SEP}{}", + output_dir.clone().display(), + config.kernel_name.clone(), + SEP = SEP + ), + ) { + Ok(()) => (), + Err(_) => { + Print::error("Unable to move entry file."); + remove_dir_all(output_dir); + return Err(()); + } + } + } + } else { + match seed_cmd( + config.seed_cmd.clone(), + config.kernel_type.clone(), + output_dir.clone(), + false, + ) { + Ok(()) => (), + Err(err) => { + Print::error(err); + return Err(()); + } } } @@ -287,7 +343,7 @@ pub fn build_thread(output: String, config: Config) -> Result<(), ()> { }; match husk.write_all(format!("#!/bin/bash\n{}", stem_cmd).as_bytes()) { - Ok(_) => (), + Ok(()) => (), Err(_err) => { Print::error("An error occured while writing to husk file."); return Err(()); @@ -296,11 +352,11 @@ pub fn build_thread(output: String, config: Config) -> Result<(), ()> { } // change permissions of file to be accessible by all + let mut perms = fs::metadata(format!( - "{dir}{sep}{output}{husk_name}", - dir = &PROD_DIR(), - sep = &SEP, - output = output, + "{output_dir}{SEP}{husk_name}", + output_dir = output_dir.display(), + SEP = SEP, husk_name = config.kernel_name.clone() )) .unwrap() @@ -315,7 +371,7 @@ pub fn build_thread(output: String, config: Config) -> Result<(), ()> { Print::info("Compiled successfully."); match update_path(config.kernel_type.clone(), output.clone()) { - Ok(_) => { + Ok(()) => { Print::success(format!("Successfully built {}", config.kernel_name).as_str()); return Ok(()); } @@ -365,7 +421,7 @@ pub async fn handle(options: Options) -> Result<()> { output = output + &SEP.to_string(); } - thread::spawn(|| build_thread(output, CONFIG)).join(); + thread::spawn(|| build_thread(output, CONFIG, false, String::new())).join(); Ok(()) } diff --git a/src/commands/install/mod.rs b/src/commands/install/mod.rs index 85a0ae1..561b9c5 100644 --- a/src/commands/install/mod.rs +++ b/src/commands/install/mod.rs @@ -95,7 +95,7 @@ fn install_dev(PATH: String) -> Result<(), String> { .unwrap(); match apply_changes() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => { Print::error(err.as_str()); return Ok(()); @@ -145,7 +145,7 @@ fn install_dev(PATH: String) -> Result<(), String> { } match apply_changes() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => { Print::error(err.as_str()); return Ok(()); @@ -186,7 +186,7 @@ fn install_prod(PATH: String, butter_file: PathBuf) -> Result<(), String> { .unwrap(); match apply_changes() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => { Print::error(err.as_str()); return Ok(()); @@ -226,7 +226,7 @@ fn install_prod(PATH: String, butter_file: PathBuf) -> Result<(), String> { .unwrap(); match apply_changes() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => { Print::error(err.as_str()); return Ok(()); @@ -254,7 +254,7 @@ fn install_prod(PATH: String, butter_file: PathBuf) -> Result<(), String> { .unwrap(); match apply_changes() { - Ok(_) => return Ok(()), + Ok(()) => return Ok(()), Err(err) => { Print::error(err.as_str()); return Ok(()); @@ -310,7 +310,7 @@ pub async fn handle(options: Options) -> Result<()> { if options.dev { match install_dev(PATH) { - Ok(_) => (), + Ok(()) => (), Err(err) => { Print::error(err.as_str()); return Ok(()); @@ -318,7 +318,7 @@ pub async fn handle(options: Options) -> Result<()> { } } else { match install_prod(PATH, butter_filepath) { - Ok(_) => (), + Ok(()) => (), Err(err) => { Print::error(err.as_str()); return Ok(()); diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 2851b62..5d75d4f 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -4,6 +4,7 @@ pub mod init; pub mod install; pub mod issue; pub mod remove; +pub mod theatre; use anyhow::Result; use clap::Subcommand; @@ -27,6 +28,8 @@ pub enum Commands { Remove(remove::Options), #[clap(help_template = SUBCOMMAND_HELP)] Dev(dev::Options), + #[clap(help_template = SUBCOMMAND_HELP, alias = "add", alias = "theater")] + Theatre(theatre::Options), } pub async fn handle_command(command: Commands) -> Result<()> { @@ -37,5 +40,6 @@ pub async fn handle_command(command: Commands) -> Result<()> { Commands::Build(options) => build::handle(options).await, Commands::Remove(options) => remove::handle(options).await, Commands::Dev(options) => dev::handle(options).await, + Commands::Theatre(options) => theatre::handle(options).await, } } diff --git a/src/commands/remove/mod.rs b/src/commands/remove/mod.rs index 7f33d22..3449ffb 100644 --- a/src/commands/remove/mod.rs +++ b/src/commands/remove/mod.rs @@ -2,7 +2,7 @@ use anyhow::Result; use clap::Parser; use std::fmt::format; -use std::fs::{create_dir_all, read_to_string, OpenOptions}; +use std::fs::{create_dir_all, read_to_string, remove_dir_all, remove_file, OpenOptions}; use std::io::{prelude::*, BufReader}; use std::path::PathBuf; use std::process::Command; @@ -147,7 +147,14 @@ fn remove_prod(kernel_name: String, PATH: String) -> Result { kernel_name, SEP = SEP, PROD = PROD_DIR() - )) { + )) || Path::new(&format!( + "{PROD}{SEP}{}", + kernel_name, + SEP = SEP, + PROD = PROD_DIR() + )) + .exists() + { if cfg!(target_os = "windows") { return match remove_windows( format!("{PROD}{SEP}{}", kernel_name, SEP = SEP, PROD = PROD_DIR()), @@ -192,6 +199,15 @@ fn remove_prod(kernel_name: String, PATH: String) -> Result { // write to file write!(butter, "{}", contents_str); + // remove it + match remove_file(removed_kernel.clone()) { + Ok(()) => (), + Err(_) => match remove_dir_all(removed_kernel) { + Ok(()) => (), + Err(_) => (), + }, + } + Ok(format!("Removed kernel {}!", kernel_name)) } } else { diff --git a/src/commands/theatre/mod.rs b/src/commands/theatre/mod.rs new file mode 100644 index 0000000..ee6ed12 --- /dev/null +++ b/src/commands/theatre/mod.rs @@ -0,0 +1,450 @@ +use flate2::read::GzDecoder; +use progress_bar::*; +use reqwest::header::USER_AGENT; +use reqwest::Url; +use std::fs::{self, create_dir_all, read_dir, remove_dir_all, DirEntry}; +use std::io::{self, Cursor, Read}; +use std::thread; +use std::{fs::File, path::Path, process::Command}; +use tar::Archive; + +use anyhow::Result; +use clap::Parser; + +use crate::util::blame::BLAME; +use crate::util::{calculate_hash, open_config, Config, GithubTags, Print, PROD_DIR, SEP, TMP}; + +use super::build::build_thread; + +#[derive(Debug, Parser)] +#[clap(about = "Used to install remote kernels.")] +pub struct Options { + #[clap(name = "link", help = "Name of kernel theatre.")] + kernel_link: Option, + + #[clap(long = "url", help = "Download directly from a url.")] + url: bool, +} + +fn download_kernel(url: String, file_name: String) -> Result { + create_dir_all(TMP()); + + init_progress_bar(2); + set_progress_bar_action("Downloading", Color::LightBlue, Style::Bold); + + let mut file_ext = ""; + + if cfg!(target_os = "windows") { + file_ext = ".zip"; + + if let Ok(mut child) = Command::new("Invoke-WebRequest") + .args([ + &url, + "-Out", + &format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + sep = SEP, + file_name = file_name, + TMP_DIR = TMP(), + file_ext = file_ext + ), + ]) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + return Err("An error occured while downloading the kernel".to_string()); + } + print_progress_bar_info( + "Success", + &format!("loading {}", url), + Color::Green, + Style::Bold, + ); + inc_progress_bar(); + } else { + return Err("Could not run command to download external kernel".to_string()); + } + } else { + file_ext = ".tar.gz"; + + if let Ok(mut child) = Command::new("curl") + .args([ + "--silent", + &url, + "-L", + "--output", + &format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + sep = SEP, + file_name = file_name, + TMP_DIR = TMP(), + file_ext = file_ext + ), + ]) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + print_progress_bar_info("Failed", "to download", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("An error occured while downloading the kernel".to_string()); + } + print_progress_bar_info( + "Success", + &format!("downloaded {}", url), + Color::Green, + Style::Bold, + ); + inc_progress_bar(); + } else { + print_progress_bar_info("Failed", "to run command", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err( + "Could not run command to download external kernel (is curl installed?)" + .to_string(), + ); + } + } + + let source = File::open(format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + file_name = file_name, + sep = SEP, + TMP_DIR = TMP(), + file_ext = file_ext + )); + + if cfg!(target_os = "windows") { + let mut deflate = source.unwrap(); + let mut data = Vec::new(); + deflate.read_to_end(&mut data); + + match zip_extract::extract( + Cursor::new(data), + Path::new(&format!("{}{}url-{}", TMP(), SEP, file_name)), + true, + ) { + Ok(()) => inc_progress_bar(), + _ => { + print_progress_bar_info("Failed", "to uncompress", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("Failed to uncompress deflate zip".to_string()); + } + } + } else { + let tar_gz = source.unwrap(); + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + + // have to put in url-download- so can later rename top-level dir to url- + match archive.unpack(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)) { + Ok(_) => print_progress_bar_info( + "Success", + "uncompressed tarball", + Color::Green, + Style::Bold, + ), + Err(_) => { + print_progress_bar_info("Failed", "to uncompress", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("Unable to uncompress tarball".to_string()); + } + }; + + // untaring will save it to incorrect dir, need to move contents of dir outside of it. + let mut files = + read_dir(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)).unwrap(); + + // ensure the final dir is empty/non-existant so no errors are thrown during rename + remove_dir_all(format!("{}{sep}{}", TMP(), file_name, sep = SEP)); + + let file_vec = files.collect::>>(); + + // downloaded with only 1 file in it (most likely dir) + if file_vec.len() == 1 { + let dir = file_vec[0].as_ref().expect("unable to get entry"); + + match fs::rename( + dir.path(), + format!("{}{sep}{}", TMP(), file_name, sep = SEP), + ) { + Ok(_) => { + remove_dir_all(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)); + inc_progress_bar() + } + Err(_) => { + print_progress_bar_info("Failed", "to format dir", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("Unable to strip top-level dir".to_string()); + } + }; + } else { + match fs::rename( + format!("{}{sep}{}-download", TMP(), file_name, sep = SEP), + format!("{}{sep}{}", TMP(), file_name, sep = SEP), + ) { + Ok(_) => inc_progress_bar(), + Err(_err) => { + print_progress_bar_info("Failed", "to format dir", Color::Red, Style::Normal); + finalize_progress_bar(); + println!("{}", _err); + return Err("Unable to rename dir".to_string()); + } + } + } + } + + finalize_progress_bar(); + + return Ok(format!("{}{sep}{}", TMP(), file_name, sep = SEP)); +} + +fn ensure_os_compat(path: String) -> Result { + let config = match open_config(&format!("{}{SEP}.kernelrc", path, SEP = SEP)) { + Ok(config) => config, + Err(_) => { + remove_dir_all(path); + return Err("Unable to read config".to_string()); + } + }; + + let compat_os = if config.advanced.is_some() && config.clone().advanced.unwrap().os.is_some() { + config.clone().advanced.unwrap().os.unwrap() + } else { + ["".to_string(), "".to_string(), "".to_string()] + }; + + let os = if cfg!(target_os = "windows") { + "windows".to_string() + } else if cfg!(target_os = "macos") { + "mac".to_string() + } else { + "linux".to_string() + }; + + if !compat_os.contains(&os) { + remove_dir_all(path); + return Err("Unsupported os type.".to_string()); + } + + Ok(config) +} + +pub async fn handle(options: Options) -> Result<()> { + // get kernel link from passed in args + let kernel_link = match options.kernel_link { + Some(link) => link, + None => { + Print::error("Please specify a theatre."); + return Ok(()); + } + }; + + // if kernel_link does not contain "/" exit. + if !options.url && !kernel_link.contains("/") { + Print::error("Please follow the scheme of user/repo."); + return Ok(()); + }; + + // declare version for downloading specific version. + let mut ver: Option = None; + + // kernel_link for non-url + let mut extern_kernel = kernel_link.clone(); + + // test if kernel_link contains a specific version. + if !options.url && kernel_link.contains("@") { + let kernel_ver_vec = kernel_link.split("@").collect::>(); + + ver = Some(kernel_ver_vec[1].to_string()); + extern_kernel = (kernel_ver_vec[0]).to_string() + } + + // if is --url flag is present + if options.url { + let url = kernel_link.clone(); + + // calculate hash for url as name + let hash_name = calculate_hash(&url).to_string(); + + // download files and move to /tmp/<...> + let download_dir = match download_kernel(url, format!("url-{}", hash_name)) { + Ok(path) => path, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + let config = match ensure_os_compat(download_dir.clone()) { + Ok(config) => config, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + if Path::new(&PROD_DIR()) + .join(config.kernel_name.clone()) + .exists() + { + Print::warn("A kernel with that name already exists."); + remove_dir_all(download_dir.clone()); + return Ok(()); + } + + // build here + let kernel_name = config.kernel_name.clone(); + let kernel_type = config.kernel_type.clone(); + + if &kernel_name == "" { + Print::error("No kernel_name in the config file."); + return Ok(()); + } else if &kernel_type == "" { + Print::error("No kernel_type in the config file."); + return Ok(()); + } else if &config.seed_cmd == "" { + Print::error("No seed_cmd in the config file."); + return Ok(()); + } + + let dir = &PROD_DIR(); + let prod_path = Path::new(dir); + + if !prod_path.exists() { + create_dir_all(prod_path); + } + + let mut output = kernel_name.clone(); + + if kernel_type.to_lowercase() == "unpacked" { + output += &SEP.to_string(); + }; + + thread::spawn(|| build_thread(output, config, true, download_dir)).join(); + } else { + // not --url, it is a github repo or external pkg manager pkg + + // filename for storage in /tmp + let file_name = extern_kernel.replace("/", "@"); + + // type of os (used for downloading files) + let os = if cfg!(target_os = "windows") { + "windows.zip".to_string() + } else if cfg!(target_os = "macos") { + "mac.tar.gz".to_string() + } else { + "linux.tar.gz".to_string() + }; + + // get ver number + if ver.is_none() { + let github_tag_link = format!("https://api.github.com/repos/{}/tags", extern_kernel); + + let url = match Url::parse(&*github_tag_link) { + Ok(url) => url, + Err(_) => { + Print::error("Could not parse URL"); + return Ok(()); + } + }; + + let req = match reqwest::Client::new() + .get(url) + .header(USER_AGENT, format!("Popcorn {}", BLAME.version)) + .send() + .await + { + Ok(data) => data, + Err(_) => { + Print::error("Could not fetch data (URL not valid)"); + return Ok(()); + } + }; + + if req.status() != 200 { + Print::error("Could not fetch data (does the repo exist?)"); + return Ok(()); + } + + let release = match req.json::().await { + Ok(json) => json, + Err(err) => { + Print::error(&format!("Unable to jsonify response; {}", err)); + return Ok(()); + } + }; + + let tag = &release[0].name; + + ver = Some(tag.to_string()); + } + + // download files and move to /tmp/<...> + let download_dir = match download_kernel( + format!( + "https://github.com/{}/releases/download/{}/kernel-{}", + extern_kernel, + ver.unwrap(), + os + ), + file_name, + ) { + Ok(path) => path, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + let config = match ensure_os_compat(download_dir.clone()) { + Ok(config) => config, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + if Path::new(&PROD_DIR()) + .join(config.kernel_name.clone()) + .exists() + { + Print::warn("A kernel with that name already exists."); + remove_dir_all(download_dir.clone()); + return Ok(()); + } + + // build here + let kernel_name = config.kernel_name.clone(); + let kernel_type = config.kernel_type.clone(); + + if &kernel_name == "" { + Print::error("No kernel_name in the config file."); + return Ok(()); + } else if &kernel_type == "" { + Print::error("No kernel_type in the config file."); + return Ok(()); + } else if &config.seed_cmd == "" { + Print::error("No seed_cmd in the config file."); + return Ok(()); + } + + let dir = &PROD_DIR(); + let prod_path = Path::new(dir); + + if !prod_path.exists() { + create_dir_all(prod_path); + } + + let mut output = kernel_name.clone(); + + if kernel_type.to_lowercase() == "unpacked" { + output += &SEP.to_string(); + }; + + thread::spawn(|| build_thread(output, config, true, download_dir)).join(); + } + + Ok(()) +} diff --git a/src/util.rs b/src/util.rs index 1d936a1..0f32702 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,15 +2,39 @@ use anyhow::Result; use chrono::Local; use console::style; use dirs; +use std::collections::hash_map::DefaultHasher; use std::env; +use std::hash::{Hash, Hasher}; -use serde_derive::Deserialize; +use serde_derive::{Deserialize, Serialize}; use std::any::{type_name, Any}; -use std::path::MAIN_SEPARATOR; +use std::path::{Path, MAIN_SEPARATOR}; pub mod blame; +pub type GithubTags = Vec; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Root { + pub name: String, + #[serde(rename = "zipball_url")] + pub zipball_url: String, + #[serde(rename = "tarball_url")] + pub tarball_url: String, + pub commit: Commit, + #[serde(rename = "node_id")] + pub node_id: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Commit { + pub sha: String, + pub url: String, +} + pub const SEP: char = MAIN_SEPARATOR; pub const PATHSEP: char = if cfg!(target_os = "windows") { ';' @@ -60,6 +84,47 @@ pub fn get_config() -> Result { Ok(config) } +pub fn open_config(path: &str) -> Result { + let file: String; + + match read_file(path) { + Ok(contents) => file = contents, + Err(error) => { + return Err(error); + } + } + + let config: Config = match serde_json::from_str(&file) { + Ok(json) => json, + Err(_) => Config { + kernel_name: "".to_string(), + kernel_type: "".to_string(), + unpacked_husk: Some("".to_string()), + dev_cmd: "".to_string(), + seed_cmd: "".to_string(), + advanced: None, + }, + }; + + Ok(config) +} + +pub fn TMP() -> String { + if cfg!(target_os = "windows") { + return env::temp_dir() + .join("popcorn-kernel") + .into_os_string() + .into_string() + .unwrap(); + } else { + return Path::new("/tmp") + .join("popcorn-kernel") + .into_os_string() + .into_string() + .unwrap(); + }; +} + pub fn DEV_DIR() -> String { match env::current_dir() { Ok(path) => { @@ -102,6 +167,12 @@ pub fn read_file(file_name: &str) -> Result { } } +pub fn calculate_hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + pub struct Print; impl Print { From 5880d33cd2342889ca8792bf54785573d9af94de Mon Sep 17 00:00:00 2001 From: matt Date: Sat, 5 Nov 2022 21:42:33 -0700 Subject: [PATCH 12/21] chore: remove old python commands --- README.md | 60 +++++++--- blame.json | 6 - cmds/build.py | 205 ---------------------------------- cmds/dev.py | 244 ---------------------------------------- cmds/help.py | 73 ------------ cmds/init.py | 38 ------- cmds/install.py | 109 ------------------ cmds/issue.py | 11 -- cmds/remove.py | 100 ----------------- cmds/theatre.py | 289 ------------------------------------------------ 10 files changed, 47 insertions(+), 1088 deletions(-) delete mode 100644 blame.json delete mode 100644 cmds/build.py delete mode 100644 cmds/dev.py delete mode 100644 cmds/help.py delete mode 100644 cmds/init.py delete mode 100644 cmds/install.py delete mode 100644 cmds/issue.py delete mode 100644 cmds/remove.py delete mode 100644 cmds/theatre.py diff --git a/README.md b/README.md index e63ab7e..e4852b6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ # 🍿 Popcorn + ### Say goodbye to complicated installation instructions and build steps. + #### So easy you can sit back and enjoy some popcorn. + ![](demo.gif) ## Contents + - [Requirements](#requirements) - [Terminology](#terminology) - [Installation](#install) @@ -14,6 +18,7 @@ - [Config](#config) ### Requirements + - python 3.7+ - pip - bash @@ -35,20 +40,26 @@ ### Install **Linux/Mac:** + ```bash curl -sSL cmdf.at/popcorn | bash ``` + or with homebrew + ```bash brew tap punctuations/tap brew install punctuations/tap/popcorn ``` **Windows:** + ```powershell iwr -useb raw.githubusercontent.com/punctuations/popcorn/main/install.ps1 | iex ``` + or with Chocolately + ```powershell coming soon... ``` @@ -56,6 +67,7 @@ coming soon... #### [Back to contents](#contents) ### Updating + If installed with the installation script just re-run the script, and it will update the program. #### [Back to contents](#contents) @@ -69,9 +81,11 @@ popcorn aims to help with the development process of not only clis using framewo ### Commands ##### Build: + The build command will compile the code in the current directory to the production directory, making a cli tool compiled and stored as a kernel. Ex. + ``` > popcorn build ``` @@ -79,16 +93,19 @@ Ex. output flag: Using the output flag will fix any conflicts between kernel names by storing it a different parent directory. + ``` > popcorn build -o raspberry ``` ##### Dev: + The dev command will launch a development environment which will listen to the specified directory for any changes and hot reload the development version of the command, to test out the command up-to-date and without having to manually compile every time. The development command will be accessible through the `{kernel_name}{dev_node}`, for example: `popcorn-dev` Ex. + ``` > popcorn dev ``` @@ -96,14 +113,17 @@ Ex. listen flag: The listen flag specifies the directory that it is listening to for changes. + ``` > popcorn dev -l ./foo/bar ``` ##### Install: + The install command is to install your kernel to the path and initialize the environment for it, this command should be run if the kernel has not been installed yet. Ex. + ``` > popcorn install ``` @@ -111,14 +131,17 @@ Ex. dev flag: the dev flag will install the **development** version of the command, the path will be saved to either shellrc file or .profile (on windows it will go straight to path) + ``` > popcorn install -d ``` ##### Remove: + The remove command is to remove already installed kernels. Ex. + ``` > popcorn remove foo ``` @@ -126,14 +149,17 @@ Ex. dev flag: The dev flag is used to indicate the requested kernel is a development kernel, which will be removed from a separate place from the production kernels. + ``` > popcorn remove foo -d ``` ##### Init: + The init command initializes the config file for popcorn. Ex. + ``` > popcorn init ``` @@ -149,15 +175,17 @@ The force flag will replace any existing config file with the base/skeleton one. packed flag: The packed flag generates a different skeleton to that of the unpacked or default one and will only have necessary values. -```` -> popcorn init --packed -```` +``` +> popcorn init --packed +``` ##### Help: + The help command provides help for the commands, providing more detail about them. Ex. + ``` > popcorn help dev ``` @@ -165,30 +193,35 @@ Ex. #### [Back to contents](#contents) ### Kernel Examples + Some examples of how to construct a kernel can be seen at the following: + - **[Tablespoon](https://github.com/punctuations/tablespoon)** created by [Matt](https://dont-ping.me) #### [Back to contents](#contents) ### Config + The config (.kernelrc) is used for all kernels, and is required, you can use the init command which will generate a skeleton config to edit for the user. ##### Skeleton config file: + ```json { - "kernel_name": "popcorn", - "kernel_type": "unpacked", - "unpacked_husk": "python @local/popcorn.py @args", - "dev_cmd": "popcorn dev", - "seed_cmd": "cp -r * @dest", - "advanced": { - "os": ["mac", "windows", "linux"], - "dev_node": "-dev" - } + "kernel_name": "popcorn", + "kernel_type": "unpacked", + "unpacked_husk": "python @local/popcorn.py @args", + "dev_cmd": "popcorn dev", + "seed_cmd": "cp -r * @dest", + "advanced": { + "os": ["mac", "windows", "linux"], + "dev_node": "-dev" + } } ``` ##### Values: + - `kernel_name`: The name that will be used to access the program. - `kernel_type`: Two possible values 'packed' or 'unpacked' which determine the file structure of the project. - `unpacked`: Requires entrypoint that bounces to another file. (directory) @@ -199,8 +232,9 @@ The config (.kernelrc) is used for all kernels, and is required, you can use the - `advanced/dev_node`: The naming scheme applied for the development command. ##### Variables: + - `@local`: used for the local directory for the program. - `@dest`: used to specify the destination of the kernel (changes per command) - `@args`: used only in `unpacked_husk`, will be all arguments passed to command. -#### [Back to contents](#contents) \ No newline at end of file +#### [Back to contents](#contents) diff --git a/blame.json b/blame.json deleted file mode 100644 index 21ad6d0..0000000 --- a/blame.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "🍿 Popcorn", - "description": "Say good-bye to complicated installation instructions.", - "version": "1.0.8", - "source": "https://github.com/punctuations/popcorn" -} diff --git a/cmds/build.py b/cmds/build.py deleted file mode 100644 index 0a85c43..0000000 --- a/cmds/build.py +++ /dev/null @@ -1,205 +0,0 @@ -import json -import os -import re -import shutil -import stat -import sys -import threading - -from _utils import styled_print - -config = {} -kernel_name = "" -kernel_type = "" - - -def initialize_globals(): - global kernel_name - global kernel_type - global config - try: - f = open("./.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Please create a .kernelrc file.") - sys.exit(0) - - kernel_name = config["kernel_name"] - kernel_type = config["kernel_type"] - - -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" - -rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" -dotprofile = f"{os.environ['HOME']}{os.sep}.profile" - - -def build_thread(output): - path_init = False - - try: - shutil.rmtree(f"{PROD_DIR}{os.sep}{output}") - os.mkdir(PROD_DIR) - if kernel_type.lower() == "unpacked": - os.mkdir(f"{PROD_DIR}{os.sep}{output}") - except FileExistsError: - pass - except FileNotFoundError: - pass - except KeyError: - styled_print.error("Please include a kernel_name in the config.") - sys.exit(0) - - try: - # run seed_cmd - if kernel_type.lower() == "unpacked": - try: - os.mkdir(f"{PROD_DIR}{os.sep}{output}") - except FileExistsError: - pass - seed_cmd = re.compile(re.escape("@dest"), re.IGNORECASE) \ - .sub(f"{PROD_DIR}{os.sep}{output}", config["seed_cmd"]) - else: - seed_cmd = re.compile(re.escape("@dest"), re.IGNORECASE).sub(f"{PROD_DIR}", config["seed_cmd"]) - exit_status = os.WEXITSTATUS(os.system(seed_cmd)) - - # create unpacked kernel with unpacked stem. - if kernel_type.lower() == "unpacked": - try: - arg_stem = re.compile(re.escape("@args"), re.IGNORECASE).sub("\"$@\"", config["unpacked_husk"]) - unpacked_husk = re.compile(re.escape("@local"), re.IGNORECASE) \ - .sub(f"{PROD_DIR}{os.sep}{output[:-1] if output.endswith('/') else output}", arg_stem) - with open(f"{PROD_DIR}{os.sep}{output}{kernel_name}", "w") as kernel: - kernel.write(f"#!/bin/bash\n{unpacked_husk}") - kernel.close() - except KeyError: - styled_print.error("Please include the unpacked_husk in the config.") - kernel.close() - sys.exit(0) - - # change permissions - try: - os.chmod(os.path.join(PROD_DIR, output if kernel_type.lower() == "packed" else f"{output}{kernel_name}"), - stat.S_IRWXO | stat.S_IRWXU | stat.S_IRWXG) - except FileNotFoundError: - styled_print.error("Unable to amend permission of file; file not found.") - sys.exit(0) - - if exit_status == 0: - styled_print.success("Compiled successfully!") - - except KeyError: - styled_print.error("Please enter in a seed_cmd") - sys.exit(0) - - if os.name != "nt": - try: - buttered = open(f'{PROD_DIR}{os.sep}butter.sh', 'r') - lines = buttered.readlines() - buttered.close() - - if kernel_type == "packed": - if f"export PATH=$PATH{os.pathsep}{PROD_DIR}\n" not in lines: - with open(f'{PROD_DIR}{os.sep}butter.sh', "a") as builds: - builds.write(f"export PATH=$PATH{os.pathsep}{PROD_DIR}\n") - builds.close() - else: - if f"export PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n" not in lines: - with open(f'{PROD_DIR}{os.sep}butter.sh', "a") as builds: - builds.write(f"export PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n") - builds.close() - except FileNotFoundError: - with open(f'{PROD_DIR}{os.sep}butter.sh', "w") as butter: - if kernel_type == "packed": - butter.write(f"#!/bin/bash\nexport PATH=$PATH{os.pathsep}{PROD_DIR}\n") - else: - butter.write(f"#!/bin/bash\nexport PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n") - butter.close() - else: - if PROD_DIR not in os.environ["PATH"] and kernel_type.lower() == "packed": - path_init = True - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{PROD_DIR}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{PROD_DIR}'") - styled_print.success("Added to path") - - if f"{PROD_DIR}{os.sep}{output}" not in os.environ["PATH"] and kernel_type.lower() == "unpacked": - path_init = True - os.system( - f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{PROD_DIR}{output}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{PROD_DIR}{output}'") - styled_print.success("Added to path") - - if os.name != "nt": - if os.path.exists(rcfile): - shellrc = open(rcfile, "r") - rclines = shellrc.read() - shellrc.close() - if f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n" not in rclines: - with open(rcfile, "w") as init: - init.write(f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n") - init.write(rclines) - init.close() - else: - profile = open(dotprofile, "r") - proflines = profile.read() - profile.close() - if f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n" not in proflines: - with open(dotprofile, "w") as init: - init.write(f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n") - init.write(proflines) - init.close() - if path_init: - if os.name == 'nt': - os.system(". $profile") - else: - if os.path.exists(rcfile): - os.system(f"source {rcfile}") - else: - os.system(f"source {dotprofile}") - - styled_print.success(f"Successfully built {kernel_name}.") - - -# popcorn build -# flags: -o, --output: change the output directory of the kernel -def build(args): - """ - Used to create production-level kernels. - - :param args: arguments passed to command - """ - initialize_globals() - - output = ["-o", "--output"] - # when file is placed here make sure to set path variable to file containing accessible target - # ex. (unpacked) export PATH = $PATH:$HOME/.local/bin/popcorn - # ex. (packed) export PATH = $PATH:$HOME/.local/bin - has_output_flag = [element for element in output if (element in args)] - output_index = args.index(has_output_flag[0]) if len(has_output_flag) >= 1 else 0 - - if not os.path.exists(PROD_DIR): - os.mkdir(PROD_DIR) - - if has_output_flag: - try: - output = args[output_index + 1] - except IndexError: - styled_print.error("Please specify an output name.") - sys.exit(0) - else: - try: - output = kernel_name - except KeyError: - styled_print.error("Please include a kernel_name in the config.") - sys.exit(0) - - try: - if kernel_type.lower() == "unpacked": - output = output + os.sep - except KeyError: - styled_print.error("Please include the kernel_type in the config.") - sys.exit(0) - - thread = threading.Thread(target=build_thread, args=(output,)) - thread.start() diff --git a/cmds/dev.py b/cmds/dev.py deleted file mode 100644 index 94fe0b4..0000000 --- a/cmds/dev.py +++ /dev/null @@ -1,244 +0,0 @@ -import json -import os -import re -import shutil -import stat -import subprocess -import sys -import threading -import time -from datetime import datetime, timedelta - -from watchdog.events import FileSystemEventHandler -from watchdog.observers import Observer - -from _utils import styled_print - -listen = ["-l", "--listen"] - -DEV_DIR = f"{os.path.realpath(os.getcwd())}{os.sep}.popcorn" -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" - -last_event = {"message": False} -event_delta = timedelta(seconds=int(2)) - -config = {} -kernel_name = "" -kernel_type = "" - - -def initialize_globals(): - global config - global kernel_name - global kernel_type - - try: - f = open("./.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Please create a .kernelrc file.") - sys.exit(0) - - kernel_name = config["kernel_name"] - kernel_type = config["kernel_type"] - - -rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" -profile = f"{os.environ['HOME']}{os.sep}.profile" - - -def thread_compile(): - time.sleep(2) - - event_time_remaining = last_event["time"] + event_delta - datetime.now() - if event_time_remaining.days <= -1: - if last_event["message"]: - styled_print.info("Updates to config detected, to see up-to-date changes re-run this command.") - styled_print.event("Received compile event.") - - try: - try: - shutil.rmtree(f"{DEV_DIR}{os.sep}") - os.mkdir(DEV_DIR) - except FileExistsError: - pass - except FileNotFoundError: - pass - - seed_cmd = re.compile(re.escape("@dest"), re.IGNORECASE).sub(f"{DEV_DIR}", config["seed_cmd"]) - exit_status = os.WEXITSTATUS(os.system(seed_cmd)) - - # create unpacked kernel. - try: - if kernel_type.lower() == "unpacked": - try: - arg_stem = re.compile(re.escape("@args"), re.IGNORECASE).sub("\"$@\"", config["unpacked_husk"]) - unpacked_husk = re.compile(re.escape("@local"), re.IGNORECASE).sub(DEV_DIR, arg_stem) - with open(f"{DEV_DIR}{os.sep}{kernel_name}", "w") as kernel: - kernel.write(f"#!/bin/bash\n{unpacked_husk}") - kernel.close() - except KeyError: - styled_print.error("Please include the unpacked_husk in the config.") - kernel.close() - sys.exit(0) - - except KeyError: - styled_print.error("Please include the kernel_type in the config.") - sys.exit(0) - - # edit permissions to all - try: - os.chmod(os.path.join(DEV_DIR, kernel_name), stat.S_IRWXO | stat.S_IRWXU | stat.S_IRWXG) - except FileNotFoundError: - styled_print.error("Unable to amend permission of file; file not found.") - sys.exit(0) - - if exit_status == 0: - styled_print.success("Compiled successfully.") - else: - styled_print.error("Failed to compile, please check seed_cmd.") - except KeyError: - styled_print.error("Please enter in a seed_cmd") - sys.exit(0) - - # rename kernel to dev branch. - try: - dev_node = config['advanced']['dev_node'] - - os.rename(f"{DEV_DIR}{os.sep}{kernel_name}", - f"{DEV_DIR}{os.sep}{kernel_name}{config['advanced']['dev_node'] if dev_node else '-dev'}") - except FileNotFoundError: - styled_print.error(f"Please have a file named {kernel_name} as entry point.") - sys.exit(0) - - -class Handler(FileSystemEventHandler): - - @staticmethod - def on_any_event(event): - if event.is_directory: - return None - - try: - p = subprocess.check_output(["git", "check-ignore", event.src_path]) - - if type(p) is not bytes: - ignored = False - else: - ignored = True - except subprocess.CalledProcessError: - ignored = False - - if event.src_path.split("/")[-1] == ".kernelrc": - last_event["message"] = True - - if not ignored: - last_event["time"] = datetime.now() - thread = threading.Thread(target=thread_compile) - thread.start() - - -# popcorn dev -# flags: -l, --listen: set the dir/file that is being watched for changes -def dev(args): - """ - Used to create development kernels. - - :param args: arguments passed to command - """ - initialize_globals() - - if DEV_DIR not in os.environ["PATH"]: - styled_print.warning("Development kernel not installed.\n\t\tplease try: popcorn install --dev") - sys.exit(0) - - has_listen_flag = [element for element in listen if (element in args)] - listen_index = args.index(has_listen_flag[0]) if len(has_listen_flag) >= 1 else 0 - - if not kernel_name: - styled_print.error("Please enter a kernel_name in config.") - sys.exit(0) - - # initialize development env - shutil.rmtree(DEV_DIR, ignore_errors=True) - os.mkdir(DEV_DIR) - - try: - seed_cmd = re.compile(re.escape("@dest"), re.IGNORECASE).sub(DEV_DIR, config["seed_cmd"]) - exit_status = os.WEXITSTATUS(os.system(seed_cmd)) - - # create packed kernel. - try: - if kernel_type.lower() == "unpacked": - try: - arg_stem = re.compile(re.escape("@args"), re.IGNORECASE).sub("\"$@\"", config["unpacked_husk"]) - unpacked_husk = re.compile(re.escape("@local"), re.IGNORECASE).sub(DEV_DIR, arg_stem) - with open(f"{DEV_DIR}{os.sep}{kernel_name}", "w") as kernel: - kernel.write(f"#!/bin/bash\n{unpacked_husk}") - kernel.close() - except KeyError: - styled_print.error("Please include the unpacked_husk in the config.") - kernel.close() - sys.exit(0) - - except KeyError: - styled_print.error("Please include the kernel_type in the config.") - sys.exit(0) - - # edit permissions to all - try: - os.chmod(os.path.join(DEV_DIR, kernel_name), stat.S_IRWXO | stat.S_IRWXU | stat.S_IRWXG) - except FileNotFoundError: - styled_print.error("Unable to amend permission of file; file not found.") - sys.exit(0) - - if exit_status == 0: - styled_print.success("Compiled successfully.") - else: - styled_print.error("Failed to compile, please check seed_cmd.") - - except KeyError: - styled_print.error("Please enter in a seed_cmd") - sys.exit(0) - - # rename kernel to dev branch. - try: - dev_node = config['advanced']['dev_node'] - os.rename(f"{DEV_DIR}{os.sep}{kernel_name}", - f"{DEV_DIR}{os.sep}{kernel_name}{config['advanced']['dev_node'] if dev_node else '-dev'}") - except FileNotFoundError: - styled_print.error(f"Please have a file named {kernel_name} as entry point.") - sys.exit(0) - - if has_listen_flag: - try: - print(f"Listening to {args[listen_index + 1]}") - watch_file = args[listen_index + 1] - except IndexError: - styled_print.error("Please specify a file or directory.") - sys.exit(0) - else: - print("Listening to .") - watch_file = "." - - if not os.path.exists(watch_file): - styled_print.error("File or directory does not exist.") - sys.exit(0) - - # Initialize logging event handler - event_handler = Handler() - - # Initialize Observer - observer = Observer() - observer.schedule(event_handler, watch_file, recursive=True) - - # Start the observer - observer.start() - try: - while True: - # Set the thread sleep time - time.sleep(1) - except KeyboardInterrupt: - observer.stop() - observer.join() diff --git a/cmds/help.py b/cmds/help.py deleted file mode 100644 index 6b61885..0000000 --- a/cmds/help.py +++ /dev/null @@ -1,73 +0,0 @@ -import json -import os -import sys - -from _utils import styled_print - - -# popcorn help -def help(args): - """ - Used to get more information about commands. - - :param args: arguments passed to command - """ - - ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - - def resource_path(relative_path): - """ Get absolute path to resource, works for dev and for PyInstaller """ - try: - # PyInstaller creates a temp folder and stores path in _MEIPASS - base_path = sys._MEIPASS - except Exception: - base_path = ROOT_DIR - - return os.path.join(base_path, relative_path) - - f = open(resource_path("blame.json")) - blame = json.load(f) - f.close() - - if len(args) < 1: - command_list = os.listdir(path=os.path.dirname(os.path.realpath(__file__))) - - commands = [] - definitions = [] - - for file in command_list: - if not file.startswith("_"): - commands.append(file.split(".")[0]) - - for file in commands: - with open(f"{os.path.dirname(os.path.realpath(__file__))}{os.sep}{file}.py", "r") as f: - contents = f.read() - definitions.append(contents.split("\"\"\"")[1].split(":")[0]) - f.close() - - print(f"\n  {blame['name']}  v{blame['version']}") - print(f"\n 💻 {blame['source']}") - print("\n ### Description \n") - print(f" {blame['description']}") - print("\n ### Commands \n") - for i, command in enumerate(commands): - print(f" •  {command} > {' '.join(definitions[i].split())}") - else: - command_file = f"{os.path.dirname(os.path.realpath(__file__))}{os.sep}{args[0]}.py" - if os.path.exists(command_file): - command = open(command_file, "r") - contents = command.read() - command.close() - command_desc = contents.split('\"\"\"\n')[1].split("\n")[0] - - print(f"\n  {blame['name']}  v{blame['version']}") - print(f"\n 💻 {blame['source']}") - print("\n ### Usage \n") - print(f" {command_desc}") - print("\n ### Flags \n") - for flags in contents.split("# flags: ")[1].split("\n")[0].split(";"): - flag = flags.split(":")[0] - flag_desc = flags.split(":")[1].split("\n")[0] - print(f" •  {flag} > {flag_desc}") - else: - styled_print.error("Command not found") diff --git a/cmds/init.py b/cmds/init.py deleted file mode 100644 index bf9fa8f..0000000 --- a/cmds/init.py +++ /dev/null @@ -1,38 +0,0 @@ -import os - -from _utils import styled_print - - -def create_config(has_packed): - with open('.kernelrc', "w") as config: - if has_packed: - config.write(f'{{\n"kernel_name": "{os.getcwd().split(os.sep)[-1]}",\n"kernel_type": "packed",' - f'\n"dev_cmd": "popcorn dev",\n"seed_cmd": "go build -o @dest",\n"advanced": {{' - f'\n"dev_node": "-dev"\n}}\n}}') - else: - config.write(f'{{\n\t"kernel_name": "{os.getcwd().split(os.sep)[-1]}",\n\t"kernel_type": "unpacked",' - f'\n\t"unpacked_husk": "python @local/popcorn.py @args",\n\t"dev_cmd": "popcorn dev",' - f'\n\t"seed_cmd": "cp -r * @dest",\n\t"advanced": {{\n\t\t"dev_node": "-dev"\n\t}}\n}}') - config.close() - - -# popcorn init -# flags: --force: replaces any existing config with default; -p, --packed: generates the packed config -def init(args): - """ - Used to initialize a .kernelrc configuration file. - - :param args: arguments passed to command - """ - force = ['--force'] - packed = ['-p', '--packed'] - - has_force_flag = [element for element in force if (element in args)] - has_packed_flag = [element for element in packed if (element in args)] - - if not os.path.exists('.kernelrc'): - create_config(has_packed_flag) - elif has_force_flag: - create_config(has_packed_flag) - else: - styled_print.info(".kernelrc already exists -- No changes made.") diff --git a/cmds/install.py b/cmds/install.py deleted file mode 100644 index c6db5b7..0000000 --- a/cmds/install.py +++ /dev/null @@ -1,109 +0,0 @@ -import json -import os -import sys - -from _utils import styled_print - -DEV_DIR = f"{os.path.realpath(os.getcwd())}{os.sep}.popcorn" -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" - -config = {} - - -def initialize_globals(): - global config - - try: - f = open("./.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Please create a .kernelrc file.") - sys.exit(0) - - -# popcorn install -# flags: -d, --dev: install development kernels -def install(args): - """ - Used to initialize environment for kernels. - - :param args: arguments passed to command. - """ - initialize_globals() - - dev = ["-d", "--dev"] - has_dev_flag = [element for element in dev if (element in args)] - - kernel_name = config["kernel_name"] - - if not kernel_name: - styled_print.error("Please include a kernel_name in config.") - sys.exit(0) - - if not os.path.exists(f'{PROD_DIR}{os.sep}butter.sh'): - if not os.path.exists(PROD_DIR): - os.mkdir(PROD_DIR) - - with open(f'{PROD_DIR}{os.sep}butter.sh', "w") as butter: - butter.write("#!/bin/bash\n") - butter.close() - - if has_dev_flag: - if DEV_DIR not in os.environ["PATH"]: - if os.name == 'nt': - # change path - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{DEV_DIR}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{DEV_DIR}'") - styled_print.success("Please run popcorn source to apply changes.") - else: - # edit .shellrc or .profile file - rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" - - if os.path.exists(rcfile): - shellrc = open(rcfile, "a") - shellrc.write(f"\nexport PATH=$PATH{os.pathsep}{DEV_DIR}\n") - shellrc.close() - else: - profile = open(f"{os.environ['HOME']}{os.sep}.profile", "a") - profile.write(f"\nexport PATH=$PATH{os.pathsep}{DEV_DIR}\n") - profile.close() - - if os.name == 'nt': - os.system(". $profile") - else: - rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" - dotprofile = f"{os.environ['HOME']}{os.sep}.profile" - - if os.path.exists(rcfile): - os.system(f"source {rcfile}") - styled_print.success("Please restart the terminal to apply changes.") - else: - os.system(f"source {dotprofile}") - styled_print.success("Please restart the terminal to apply changes.") - else: - styled_print.info("kernel already installed.") - else: - rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" - dotprofile = f"{os.environ['HOME']}{os.sep}.profile" - - if os.path.exists(rcfile): - shellrc = open(rcfile, "r+") - if f". $HOME{os.sep}.kernels{os.sep}butter.sh\n" not in shellrc.readlines(): - rclines = "\n".join(shellrc.readlines()) - shellrc.write(f"\n. $HOME{os.sep}.kernels{os.sep}butter.sh\n{rclines}") - shellrc.close() - else: - styled_print.info("kernel already installed.") - shellrc.close() - else: - profile = open(dotprofile, "r+") - if f". $HOME{os.sep}.kernels{os.sep}butter.sh\n" not in profile.readlines(): - proflines = "\n".join(profile.readlines()) - profile.seek(0) - profile.write(f"\n. $HOME{os.sep}.kernels{os.sep}butter.sh\n{proflines}") - profile.truncate() - profile.close() - else: - styled_print.info("kernel already installed.") - profile.close() diff --git a/cmds/issue.py b/cmds/issue.py deleted file mode 100644 index 69ba665..0000000 --- a/cmds/issue.py +++ /dev/null @@ -1,11 +0,0 @@ -import webbrowser - - -# popcorn issue -def issue(args): - """ - Submit issues about popcorn. - - :param args: arguments passed to command - """ - webbrowser.open("https://github.com/punctuations/popcorn/issues/new") diff --git a/cmds/remove.py b/cmds/remove.py deleted file mode 100644 index 601504c..0000000 --- a/cmds/remove.py +++ /dev/null @@ -1,100 +0,0 @@ -import os -import shutil -import sys - -from _utils import styled_print - -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" - - -# popcorn remove -# flags: -d, --dev: remove development kernels -def remove(args): - """ - Used to remove both production kernels and development kernels. - - :param args: arguments passed to command - """ - dev = ["-d", "--dev"] - has_dev_flag = [element for element in dev if (element in args)] - - if len(args) >= 1: - path = os.environ["PATH"] - - styled_print.info(f"Removing {args[0]}...") - if has_dev_flag: - if f"{args[0]}{os.sep}.popcorn" in path: - if os.name == 'nt': - split_paths = path.split(os.pathsep) - removed_kernel = f'{args[0]}{os.sep}.popcorn' - fixed_path = f'{os.pathsep}'.join([i for i in split_paths if removed_kernel not in i]) - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '{fixed_path}', 'User')") - os.system(f"$env:PATH = '{fixed_path}'") - else: - rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" - profile = f"{os.environ['HOME']}{os.sep}.profile" - - if os.path.exists(rcfile): - # remove form .shellrc - with open(rcfile, 'r+') as f: - data = ''.join([i for i in f if f"{args[0]}{os.sep}.popcorn" not in i]) - f.seek(0) - f.write(data) - f.truncate() - f.close() - else: - # remove from .profile - with open(profile, 'r+') as f: - data = ''.join([i for i in f if f"{args[0]}{os.sep}.popcorn" not in i]) - f.seek(0) - f.write(data) - f.truncate() - f.close() - styled_print.success(f"Removed {args[0]}") - styled_print.info("Please restart the terminal to apply changes.") - else: - styled_print.error("kernel not installed.") - else: - if f"{PROD_DIR}{os.sep}{args[0]}" in path or os.path.exists(f"{PROD_DIR}{os.sep}{args[0]}"): - if os.name == 'nt': - split_paths = path.split(os.pathsep) - removed_kernel = f'{PROD_DIR}{os.sep}{args[0]}' - fixed_path = f'{os.pathsep}'.join([i for i in split_paths if removed_kernel not in i]) - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '{fixed_path}', 'User')") - os.system(f"$env:PATH = '{fixed_path}'") - - try: - shutil.rmtree(f"{PROD_DIR}{os.sep}{args[0]}") - except FileNotFoundError: - styled_print.error("kernel not found.") - sys.exit(0) - except NotADirectoryError: - os.remove(f"{PROD_DIR}{os.sep}{args[0]}") - - else: - if args[0] == "butter.sh": - styled_print.error("Not a kernel.") - sys.exit(0) - - buttered = f'{PROD_DIR}{os.sep}butter.sh' - with open(buttered, 'r+') as butter: - data = ''.join([i for i in butter if args[0] not in i]) - butter.seek(0) - butter.write(data) - butter.truncate() - butter.close() - - try: - shutil.rmtree(f"{PROD_DIR}{os.sep}{args[0]}") - except FileNotFoundError: - styled_print.error("kernel not found.") - sys.exit(0) - except NotADirectoryError: - os.remove(f"{PROD_DIR}{os.sep}{args[0]}") - - styled_print.success(f"Removed {args[0]}") - styled_print.info("Please restart the terminal to apply changes.") - else: - styled_print.error("kernel not installed.") - else: - styled_print.error("Please specify a kernel to remove.") diff --git a/cmds/theatre.py b/cmds/theatre.py deleted file mode 100644 index 48acffb..0000000 --- a/cmds/theatre.py +++ /dev/null @@ -1,289 +0,0 @@ -import json -import os -import shutil -import stat -import subprocess -import sys -import tarfile -import threading -from zipfile import ZipFile - -import requests - -from _utils import styled_print - -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" -TMP_DIR = f"{os.sep}tmp" if os.name != 'nt' else os.environ['TEMP'] -TMP_DIR += f"{os.sep}popcorn-kernel{os.sep}" - -rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" -dotprofile = f"{os.environ['HOME']}{os.sep}.profile" - - -def build_thread(output, location, config): - os.chdir(f'{location}') - kernel_type = config['kernel_type'] - kernel_name = config['kernel_name'] - path_init = False - - try: - shutil.rmtree(f"{PROD_DIR}{os.sep}{output}") - os.mkdir(PROD_DIR) - if kernel_type.lower() == "unpacked": - os.mkdir(f"{PROD_DIR}{os.sep}{output}") - except FileExistsError: - pass - except FileNotFoundError: - pass - - try: - if kernel_type.lower() == 'unpacked': - kernel_files = [f for f in os.listdir()] - - for file in kernel_files: - shutil.move(file, f"{PROD_DIR}{os.sep}{output}{file}") - else: - # kernel is packed, only transfer the entry point. - os.rename(kernel_name, f"{PROD_DIR}{os.sep}{output}") - - # change permissions - try: - os.chmod(os.path.join(PROD_DIR, output if kernel_type.lower() == "packed" else f"{output}{kernel_name}"), - stat.S_IRWXO | stat.S_IRWXU | stat.S_IRWXG) - except FileNotFoundError: - styled_print.error("Unable to amend permission of file; file not found.") - sys.exit(0) - - styled_print.success("Compiled successfully!") - - except KeyError: - styled_print.error("Theatre does not contain a seed_cmd") - sys.exit(0) - - # add to buttered - if os.name != "nt": - try: - buttered = open(f'{PROD_DIR}{os.sep}butter.sh', 'r') - lines = buttered.readlines() - buttered.close() - - if kernel_type == "packed": - if f"export PATH=$PATH{os.pathsep}{PROD_DIR}\n" not in lines: - with open(f'{PROD_DIR}{os.sep}butter.sh', "a") as builds: - builds.write(f"export PATH=$PATH{os.pathsep}{PROD_DIR}\n") - builds.close() - else: - if f"export PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n" not in lines: - with open(f'{PROD_DIR}{os.sep}butter.sh', "a") as builds: - builds.write(f"export PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n") - builds.close() - except FileNotFoundError: - with open(f'{PROD_DIR}{os.sep}butter.sh', "w") as butter: - if kernel_type == "packed": - butter.write(f"#!/bin/bash\nexport PATH=$PATH{os.pathsep}{PROD_DIR}\n") - else: - butter.write(f"#!/bin/bash\nexport PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n") - butter.close() - else: - # is windows, add to path - if PROD_DIR not in os.environ["PATH"] and kernel_type.lower() == "packed": - path_init = True - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{PROD_DIR}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{PROD_DIR}'") - styled_print.success("Added to path") - - if f"{PROD_DIR}{os.sep}{output}" not in os.environ["PATH"] and kernel_type.lower() == "unpacked": - path_init = True - os.system( - f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{PROD_DIR}{output}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{PROD_DIR}{output}'") - styled_print.success("Added to path") - - # add buttered execution to rcfile/dotprofile - if os.name != "nt": - if os.path.exists(rcfile): - shellrc = open(rcfile, "r") - rclines = shellrc.read() - shellrc.close() - if f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n" not in rclines: - with open(rcfile, "w") as init: - init.write(f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n") - init.write(rclines) - init.close() - else: - profile = open(dotprofile, "r") - proflines = profile.read() - profile.close() - if f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n" not in proflines: - with open(dotprofile, "w") as init: - init.write(f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n") - init.write(proflines) - init.close() - if path_init: - if os.name == 'nt': - os.system(". $profile") - else: - if os.path.exists(rcfile): - os.system(f"source {rcfile}") - else: - os.system(f"source {dotprofile}") - - -def build(config, is_unpacked, location): - if not os.path.exists(PROD_DIR): - os.mkdir(PROD_DIR) - - try: - output = config["kernel_name"] - except KeyError: - styled_print.error("Theatre has no kernel_name.") - sys.exit(0) - - if is_unpacked: - output = output + os.sep - thread = threading.Thread(target=build_thread, args=(output, location, config,)) - thread.start() - else: - thread = threading.Thread(target=build_thread, args=(output, location, config,)) - thread.start() - - -# popcorn theatre -# flags: -u, --unpacked: get a unpacked remote kernel; --url: specify the download URL -def theatre(args): - """ - Used to get remote kernels. - - :param args: arguments passed to command - """ - - unpacked = ["-u", "--unpacked"] - has_unpacked_flag = [element for element in unpacked if (element in args)] - url = ["--url"] - has_url_flag = [element for element in url if (element in args)] - url_index = args.index(has_url_flag[0]) if len(has_url_flag) >= 1 else 0 - - has_target_ver = False - ver = None - - if len(args) >= 1: - if not has_url_flag and len(args[0].split("/")) != 2: - styled_print.error("Please follow the scheme of user/repo") - sys.exit(0) - - if not has_url_flag and "@" in args[0]: - ver = args[0].split("@")[1] - args[0] = args[0].split("@")[0] - has_target_ver = True - - if has_url_flag: - hash_name = args[url_index + 1].encode('utf-8').hex() - if os.name == 'nt': - file_ext = '.zip' - os.system(f"Invoke-WebRequest {args[url_index + 1]} -Out {TMP_DIR}url-{hash_name}{file_ext}") - else: - file_ext = '.tar.gz' - os.system(f"curl --silent {args[url_index + 1]} -L --output {TMP_DIR}url-{hash_name}{file_ext} " - f"--create-dirs") - - if os.name == 'nt': - with ZipFile(f'{TMP_DIR}url-{hash_name}{file_ext}', "r") as zip_obj: - zip_obj.extractall(f'{TMP_DIR}url-{hash_name}') - else: - with tarfile.open(f'{TMP_DIR}url-{hash_name}{file_ext}', "r") as tar_obj: - tar_obj.extractall(f'{TMP_DIR}url-{hash_name}') - - # load .kernelrc - try: - f = open(f"{TMP_DIR}url-{hash_name}{os.sep}.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Theatre does not contain .kernelrc") - shutil.rmtree(f"{TMP_DIR}url-{hash_name}") - sys.exit(0) - - # check to see if it is compatible with current os - os_type = 'windows' if os.name == 'nt' else 'mac' if sys.platform == 'darwin' else 'linux' - if os_type not in config["os"]: - styled_print.warning("Unsupported os type.") - shutil.rmtree(f"{TMP_DIR}url-{hash_name}") - sys.exit(0) - - # find .kernelrc and make sure the kernel names aren't a conflict. - if os.path.exists(f"{PROD_DIR}{os.sep}{args.splt('/')[1]}"): - styled_print.warning("A kernel with that name already exists.") - # delete the conflicted kernel here. - shutil.rmtree(f"{TMP_DIR}url-{hash_name}") - sys.exit(0) - - build(config, has_unpacked_flag, location=f"url-{hash_name}") - styled_print.success(f"Successfully added {config['kernel_name']}") - - else: - # from github. - file_name = '@'.join(args[0].split('/')) - os_type = 'windows' if os.name == 'nt' else 'mac' if sys.platform == 'darwin' else 'linux' - if not has_target_ver: - release = requests.get(f'https://api.github.com/repos/{args[0]}/tags').json() - ver = release[0]['name'] - - if os.name == 'nt': - file_ext = '.zip' - try: - subprocess.run(f"Invoke-WebRequest https://github.com/{args[0]}/releases/download/{ver}/kernel" - f"-{os_type}{file_ext} -Out {TMP_DIR}{file_name}{file_ext}", shell=True, check=True) - except (OSError, subprocess.SubprocessError, subprocess.CalledProcessError) as e: - print(e) - styled_print.error("An error occurred while getting the theatre's kernel.") - sys.exit(0) - - else: - file_ext = '.tar.gz' - try: - subprocess.run(f"curl --silent https://github.com/{args[0]}/releases/download/{ver}/kernel" - f"-{os_type}{file_ext} -L --output {TMP_DIR}{file_name}{file_ext} --create-dirs", - shell=True, check=True) - except (OSError, subprocess.SubprocessError, subprocess.CalledProcessError) as e: - print(e) - styled_print.error("An error occurred while getting the theatre's kernel.") - sys.exit(0) - - if os.name == 'nt': - with ZipFile(f'{TMP_DIR}{file_name}{file_ext}', "r") as zip_obj: - zip_obj.extractall(f'{TMP_DIR}{file_name}') - else: - with tarfile.open(f'{TMP_DIR}{file_name}{file_ext}', "r") as tar_obj: - tar_obj.extractall(f'{TMP_DIR}{file_name}') - - # load .kernelrc - try: - f = open(f"{TMP_DIR}{file_name}{os.sep}.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Theatre does not contain .kernelrc") - shutil.rmtree(f"{TMP_DIR}{file_name}") - shutil.rmtree(f"{TMP_DIR}{file_name}{file_ext}") - sys.exit(0) - - # check to see if it is compatible with current os - if config["advanced"]["os"] and os_type not in config["advanced"]["os"]: - styled_print.warning("Unsupported os type.") - shutil.rmtree(f"{TMP_DIR}{file_name}") - shutil.rmtree(f"{TMP_DIR}{file_name}{file_ext}") - sys.exit(0) - - # find .kernelrc and make sure the kernel names aren't a conflict. - if os.path.exists(f"{PROD_DIR}{os.sep}{config['kernel_name']}"): - styled_print.warning("A kernel with that name already exists.") - # delete the conflicted kernel here. - shutil.rmtree(f"{TMP_DIR}{file_name}") - shutil.rmtree(f"{TMP_DIR}{file_name}{file_ext}") - sys.exit(0) - - build(config, has_unpacked_flag, location=f"{TMP_DIR}{file_name}") - styled_print.success(f"Successfully added {config['kernel_name']}") - - else: - styled_print.error("Please specify a Theatre.") From fa42e0181c925e7c057da0cd563c938e7a1b1db3 Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 6 Nov 2022 01:06:39 -0700 Subject: [PATCH 13/21] chore(theatre): formatting --- src/commands/theatre/mod.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/commands/theatre/mod.rs b/src/commands/theatre/mod.rs index ee6ed12..375713e 100644 --- a/src/commands/theatre/mod.rs +++ b/src/commands/theatre/mod.rs @@ -169,7 +169,7 @@ fn download_kernel(url: String, file_name: String) -> Result { dir.path(), format!("{}{sep}{}", TMP(), file_name, sep = SEP), ) { - Ok(_) => { + Ok(()) => { remove_dir_all(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)); inc_progress_bar() } @@ -184,11 +184,10 @@ fn download_kernel(url: String, file_name: String) -> Result { format!("{}{sep}{}-download", TMP(), file_name, sep = SEP), format!("{}{sep}{}", TMP(), file_name, sep = SEP), ) { - Ok(_) => inc_progress_bar(), - Err(_err) => { + Ok(()) => inc_progress_bar(), + Err(_) => { print_progress_bar_info("Failed", "to format dir", Color::Red, Style::Normal); finalize_progress_bar(); - println!("{}", _err); return Err("Unable to rename dir".to_string()); } } @@ -231,6 +230,18 @@ fn ensure_os_compat(path: String) -> Result { Ok(config) } +fn download_homebrew(pkg: &str) -> Result<(), String> { + println!("Homebrew download {}", pkg); + + Ok(()) +} + +fn download_yum(pkg: &str) -> Result<(), String> { + println!("yum download {}", pkg); + + Ok(()) +} + pub async fn handle(options: Options) -> Result<()> { // get kernel link from passed in args let kernel_link = match options.kernel_link { @@ -338,6 +349,20 @@ pub async fn handle(options: Options) -> Result<()> { "linux.tar.gz".to_string() }; + // used to detect if is other pkg manager + let kernel_name_split = kernel_link.split("/").collect::>(); + let repo = kernel_name_split[0]; + let pkg = kernel_name_split[1]; + + // allow compatibility! + if repo.to_lowercase() == "homebrew" || repo.to_lowercase() == "brew" { + download_homebrew(pkg); + return Ok(()); + } else if repo.to_lowercase() == "yum" { + download_yum(pkg); + return Ok(()); + } + // get ver number if ver.is_none() { let github_tag_link = format!("https://api.github.com/repos/{}/tags", extern_kernel); From f6ee8bf01bbedbc51525644b7a9dda4a86a696b8 Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 6 Nov 2022 01:09:12 -0700 Subject: [PATCH 14/21] chore(theatre): cleanup --- src/commands/theatre/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/theatre/mod.rs b/src/commands/theatre/mod.rs index 375713e..9002d15 100644 --- a/src/commands/theatre/mod.rs +++ b/src/commands/theatre/mod.rs @@ -153,7 +153,7 @@ fn download_kernel(url: String, file_name: String) -> Result { }; // untaring will save it to incorrect dir, need to move contents of dir outside of it. - let mut files = + let files = read_dir(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)).unwrap(); // ensure the final dir is empty/non-existant so no errors are thrown during rename From a197faa3632bc27ccf80257d5708e7824842cadf Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 6 Nov 2022 01:14:36 -0700 Subject: [PATCH 15/21] chore(build & remove): cleanup --- src/commands/build/mod.rs | 4 ++-- src/commands/remove/mod.rs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/commands/build/mod.rs b/src/commands/build/mod.rs index 0df604b..249a63a 100644 --- a/src/commands/build/mod.rs +++ b/src/commands/build/mod.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Parser; -use std::env::{self, current_dir}; +use std::env; use std::fs::{self, File, OpenOptions}; use std::io::{prelude::*, BufReader}; use std::os::unix::prelude::PermissionsExt; @@ -10,7 +10,7 @@ use std::process::Command; use std::thread; use std::{fs::create_dir_all, fs::remove_dir_all, path::Path}; -use crate::util::{get_config, AdvancedConfig, Config, Print, DEV_DIR, PATHSEP, PROD_DIR, SEP}; +use crate::util::{get_config, Config, Print, DEV_DIR, PATHSEP, PROD_DIR, SEP}; use super::install::apply_changes; diff --git a/src/commands/remove/mod.rs b/src/commands/remove/mod.rs index 3449ffb..4bfe424 100644 --- a/src/commands/remove/mod.rs +++ b/src/commands/remove/mod.rs @@ -1,8 +1,7 @@ use anyhow::Result; use clap::Parser; -use std::fmt::format; -use std::fs::{create_dir_all, read_to_string, remove_dir_all, remove_file, OpenOptions}; +use std::fs::{remove_dir_all, remove_file, OpenOptions}; use std::io::{prelude::*, BufReader}; use std::path::PathBuf; use std::process::Command; @@ -95,7 +94,7 @@ fn remove_dev(kernel_name: String, PATH: String) -> Result { Err(_) => return Err("Unable to open rc file".to_string()), }; - let mut reader = BufReader::new(file.try_clone().unwrap()); + let reader = BufReader::new(file.try_clone().unwrap()); // filter array let filter: Vec<_> = reader From b39f9d6f8824ee0611e83d0d76459df9dbfab8bb Mon Sep 17 00:00:00 2001 From: Gedeon Sainrival Date: Mon, 14 Nov 2022 11:18:04 -0500 Subject: [PATCH 16/21] feat: add Rust workflow to build.yml --- .github/workflows/build.yml | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd86f8a..a2269c7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ jobs: compile_exec: name: Compile to executables runs-on: ${{ matrix.config.os }} - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/rewrite' # just to see if it works strategy: matrix: config: @@ -13,14 +13,20 @@ jobs: - os: windows-latest steps: - uses: actions/checkout@v3 - - name: Setup python - uses: actions/setup-python@v3 + - name: Setup Rust + - uses: actions-rs/toolchain@v1 with: - python-version: '3.9' - - name: Install dependencies for ${{ matrix.config.os }} - run: | - python -m pip install --upgrade pip wheel setuptools - pip install -r requirements.txt - pip install pyinstaller - - name: Build with pyinstaller for ${{ matrix.config.os }} - run: pyinstaller popcorn.spec \ No newline at end of file + toolchain: stable + + - name: Install dependencies on Ubuntu + if: startsWith(matrix.config.os, 'ubuntu') + run: sudo apt-get update && sudo apt-get install cmake pkg-config libgtk-3-dev + + - name: Update toolchain + run: rustup update + + - name: Cache dependencies + uses: Swatinem/rust-cache@v2 + + - name: Build with Cargo + run: cargo build \ No newline at end of file From 553cc72df095809dc16e0113ddf24ca4303ba074 Mon Sep 17 00:00:00 2001 From: Gedeon Sainrival Date: Mon, 14 Nov 2022 11:38:05 -0500 Subject: [PATCH 17/21] feat: I think I did this workflow right --- .github/workflows/compile_and_release.yml | 40 +++++++++++++++++------ 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/.github/workflows/compile_and_release.yml b/.github/workflows/compile_and_release.yml index d745fc7..2be8bf0 100644 --- a/.github/workflows/compile_and_release.yml +++ b/.github/workflows/compile_and_release.yml @@ -54,17 +54,37 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # [!] we need to checkout with tags and commit history - - name: Setup python - uses: actions/setup-python@v3 + - uses: actions/checkout@v3 + - name: Setup Rust + - uses: actions-rs/toolchain@v1 with: - python-version: '3.9' - - name: Install dependencies for ${{ matrix.config.os }} - run: | - python -m pip install --upgrade pip wheel setuptools - pip install -r requirements.txt - pip install pyinstaller - - name: Build with pyinstaller for ${{ matrix.config.os }} - run: pyinstaller popcorn.spec + toolchain: stable + + - name: Install dependencies on Ubuntu + if: startsWith(matrix.config.os, 'ubuntu') + run: sudo apt-get update && sudo apt-get install cmake pkg-config libgtk-3-dev + + - name: Build with Cargo for ${{ matrix.config.os }} + if: startsWith(matrix.config.os, 'ubuntu') + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: x86_64-unknown-linux-gnu + run: cargo build --release --target $TARGET + + if: startsWith(matrix.config.os, 'macos') + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: x86_64-apple-darwin # will probably have to change this around to accomodate for M1 & M2 + run: cargo build --release --target $TARGET + + if: startsWith(matrix.config.os, 'windows') # not sure if a target is needed here + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + run: cargo build --release + - name: Copy files to be compressed run: | cp README.md ./dist From 952381a35168ef96d2b7fdbf2f231ff8f532a6e4 Mon Sep 17 00:00:00 2001 From: Gedeon Sainrival <75461444+gedeondoescode@users.noreply.github.com> Date: Mon, 14 Nov 2022 19:36:38 -0500 Subject: [PATCH 18/21] Fix workflow (#4) --- .github/workflows/build.yml | 19 +++++++++------- .github/workflows/compile_and_release.yml | 27 +++++++++++++++++------ .github/workflows/main.yml | 22 ++++++++++-------- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a2269c7..cb7dd17 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -on: ['push', 'pull_request'] +on: ["push", "pull_request"] jobs: compile_exec: @@ -14,19 +14,22 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup Rust - - uses: actions-rs/toolchain@v1 + uses: actions-rs/toolchain@v1 with: toolchain: stable - + - name: Install dependencies on Ubuntu if: startsWith(matrix.config.os, 'ubuntu') run: sudo apt-get update && sudo apt-get install cmake pkg-config libgtk-3-dev - name: Update toolchain - run: rustup update - + run: rustup update + - name: Cache dependencies - uses: Swatinem/rust-cache@v2 - + uses: Swatinem/rust-cache@v2 + - name: Build with Cargo - run: cargo build \ No newline at end of file + uses: actions-rs/cargo@v1 + with: + command: build + args: --release diff --git a/.github/workflows/compile_and_release.yml b/.github/workflows/compile_and_release.yml index 2be8bf0..1fcbf7c 100644 --- a/.github/workflows/compile_and_release.yml +++ b/.github/workflows/compile_and_release.yml @@ -54,9 +54,9 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # [!] we need to checkout with tags and commit history - - uses: actions/checkout@v3 + - name: Setup Rust - - uses: actions-rs/toolchain@v1 + uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -70,21 +70,34 @@ jobs: with: toolchain: stable target: x86_64-unknown-linux-gnu - run: cargo build --release --target $TARGET + + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{with.target}} if: startsWith(matrix.config.os, 'macos') uses: actions-rs/toolchain@v1 with: toolchain: stable - target: x86_64-apple-darwin # will probably have to change this around to accomodate for M1 & M2 - run: cargo build --release --target $TARGET + target: aarch64-apple-darwin # will probably have to change this around to accomodate for M1 & M2 + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{with.target}} + if: startsWith(matrix.config.os, 'windows') # not sure if a target is needed here uses: actions-rs/toolchain@v1 with: toolchain: stable - run: cargo build --release - + target: x86_64-pc-windows-msvc + + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{with.target}} + - name: Copy files to be compressed run: | cp README.md ./dist diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d7ed1c9..2eb96bd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,13 +5,17 @@ jobs: runs-on: ubuntu-latest name: CI workflow steps: - - name: checkout source repo - uses: actions/checkout@v2 + - name: checkout source repo + uses: actions/checkout@v2 - - name: linting - uses: programmingwithalex/pylinters@v1.4.2 - with: - python-root: '.' - flake8-flags: '--max-line-length 120' - mypy-flags: '--ignore-missing-imports --install-types --non-interactive' - fail-on-isort: true \ No newline at end of file + - name: setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + + - name: Run cargo clean + uses: actions-rs/cargo@v1 + with: + command: clean From a07c18088623a320fedb260d0b725d9f6dd3b83f Mon Sep 17 00:00:00 2001 From: Gedeon Sainrival <75461444+gedeondoescode@users.noreply.github.com> Date: Wed, 16 Nov 2022 20:18:05 -0500 Subject: [PATCH 19/21] fix: file permissions in windows (#6) --- src/commands/build/mod.rs | 10 ++-------- src/commands/dev/mod.rs | 10 ++-------- src/util.rs | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/commands/build/mod.rs b/src/commands/build/mod.rs index 249a63a..a6ec3f7 100644 --- a/src/commands/build/mod.rs +++ b/src/commands/build/mod.rs @@ -4,13 +4,12 @@ use clap::Parser; use std::env; use std::fs::{self, File, OpenOptions}; use std::io::{prelude::*, BufReader}; -use std::os::unix::prelude::PermissionsExt; use std::path::PathBuf; use std::process::Command; use std::thread; use std::{fs::create_dir_all, fs::remove_dir_all, path::Path}; -use crate::util::{get_config, Config, Print, DEV_DIR, PATHSEP, PROD_DIR, SEP}; +use crate::util::{get_config, set_permissions, Config, Print, DEV_DIR, PATHSEP, PROD_DIR, SEP}; use super::install::apply_changes; @@ -361,12 +360,7 @@ pub fn build_thread( )) .unwrap() .permissions(); - perms.set_mode(0o511); - - if perms.mode() != 0o511 { - Print::error("Unable to change file permissions."); - return Err(()); - } + set_permissions(&mut perms, 0o511); Print::info("Compiled successfully."); diff --git a/src/commands/dev/mod.rs b/src/commands/dev/mod.rs index b332eb3..47da3a4 100644 --- a/src/commands/dev/mod.rs +++ b/src/commands/dev/mod.rs @@ -5,13 +5,13 @@ use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use regex::Regex; use std::fs::{self, create_dir_all, remove_dir_all, File}; use std::io::Write; -use std::os::unix::prelude::PermissionsExt; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::mpsc::channel; use std::time::Instant; use std::{env, thread, time}; +use crate::util::set_permissions; use crate::util::{blame::BLAME, get_config, Config, Print, DEV_DIR, SEP}; use super::build::seed_cmd; @@ -117,13 +117,7 @@ fn dev_compile(config: Config) -> Result<(), ()> { )) .unwrap() .permissions(); - perms.set_mode(0o511); - - if perms.mode() != 0o511 { - Print::error("Unable to change file permissions."); - return Err(()); - } - + set_permissions(&mut perms, 0o511); Ok(()) } diff --git a/src/util.rs b/src/util.rs index 0f32702..05acedf 100644 --- a/src/util.rs +++ b/src/util.rs @@ -4,6 +4,7 @@ use console::style; use dirs; use std::collections::hash_map::DefaultHasher; use std::env; +use std::fs::Permissions; use std::hash::{Hash, Hasher}; use serde_derive::{Deserialize, Serialize}; @@ -208,3 +209,23 @@ impl Print { ) } } + +#[cfg(unix)] +pub fn set_permissions(metadata_permissions: &mut Permissions, mode: u32) -> Result<(), ()> { + use std::os::unix::prelude::PermissionsExt; + + metadata_permissions.set_mode(0o511); + + if metadata_permissions.mode() != 0o511 { + Print::error("Unable to change file permissions."); + return Err(()); + } + + Ok(()) +} + +#[cfg(not(unix))] +#[allow(unused)] +pub fn set_permissions(metadata_permissions: &mut Permissions, mode: u32) -> Result<(), ()> { + Ok(()) +} From e255bcb131f29cb7f2968dfde4c39e86c45e457f Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 24 Sep 2023 15:39:06 -0700 Subject: [PATCH 20/21] feat: add updating & versions --- Cargo.lock | 7 + Cargo.toml | 1 + src/commands/issue/mod.rs | 5 + src/commands/mod.rs | 4 + src/commands/remove/mod.rs | 18 ++- src/commands/theatre/mod.rs | 186 +++++++++++++++++++++++++-- src/commands/update/mod.rs | 246 ++++++++++++++++++++++++++++++++++++ src/util.rs | 122 +++++++++++++++++- 8 files changed, 575 insertions(+), 14 deletions(-) create mode 100644 src/commands/update/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 4ee1edf..0f4782f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -867,6 +867,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.5.0" @@ -1223,6 +1229,7 @@ dependencies = [ "dirs", "flate2", "log", + "md5", "notify", "progress_bar", "regex", diff --git a/Cargo.toml b/Cargo.toml index bb70346..5289298 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ name = "popcorn" [dependencies] regex = "1" +md5 = "0.7.0" flate2 = "1.0" tar = "0.4.38" log = "0.4.17" diff --git a/src/commands/issue/mod.rs b/src/commands/issue/mod.rs index c895c24..af44d5a 100644 --- a/src/commands/issue/mod.rs +++ b/src/commands/issue/mod.rs @@ -8,6 +8,11 @@ use webbrowser; pub struct Options {} pub async fn handle(_options: Options) -> Result<()> { +// return match webbrowser::open("https://github.com/punctuations/popcorn/issues/new") { +// Ok(_) => Ok(()), +// Err(_) => Err(2), +// }; + let _ = webbrowser::open("https://github.com/punctuations/popcorn/issues/new"); Ok(()) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 5d75d4f..4532bc9 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -5,6 +5,7 @@ pub mod install; pub mod issue; pub mod remove; pub mod theatre; +pub mod update; use anyhow::Result; use clap::Subcommand; @@ -30,6 +31,8 @@ pub enum Commands { Dev(dev::Options), #[clap(help_template = SUBCOMMAND_HELP, alias = "add", alias = "theater")] Theatre(theatre::Options), + #[clap(help_template = SUBCOMMAND_HELP)] + Update(update::Options), } pub async fn handle_command(command: Commands) -> Result<()> { @@ -41,5 +44,6 @@ pub async fn handle_command(command: Commands) -> Result<()> { Commands::Remove(options) => remove::handle(options).await, Commands::Dev(options) => dev::handle(options).await, Commands::Theatre(options) => theatre::handle(options).await, + Commands::Update(options) => update::handle(options).await, } } diff --git a/src/commands/remove/mod.rs b/src/commands/remove/mod.rs index 4bfe424..2ac66d0 100644 --- a/src/commands/remove/mod.rs +++ b/src/commands/remove/mod.rs @@ -140,7 +140,7 @@ fn remove_dev(kernel_name: String, PATH: String) -> Result { } } -fn remove_prod(kernel_name: String, PATH: String) -> Result { +pub fn remove_prod(kernel_name: String, PATH: String) -> Result { if PATH.contains(&format!( "{PROD}{SEP}{}", kernel_name, @@ -177,7 +177,7 @@ fn remove_prod(kernel_name: String, PATH: String) -> Result { let prod_directory = Path::new(dir); let butter_file = prod_directory.join("butter.sh").clone(); - let mut butter = match OpenOptions::new().read(true).write(true).open(butter_file) { + let butter = match OpenOptions::new().read(true).open(&butter_file) { Ok(file) => file, Err(_) => return Err("Unable to open butter file".to_string()), }; @@ -185,7 +185,7 @@ fn remove_prod(kernel_name: String, PATH: String) -> Result { let reader = BufReader::new(butter.try_clone().unwrap()); // filter array - let filter: Vec<_> = reader + let filter: Vec = reader .lines() .into_iter() .map(|x| x.unwrap()) @@ -195,8 +195,18 @@ fn remove_prod(kernel_name: String, PATH: String) -> Result { // join contents into string let contents_str = filter.join("\n"); + // open and truncate + let mut butter_write = match OpenOptions::new() + .write(true) + .truncate(true) + .open(butter_file) + { + Ok(file) => file, + Err(_) => return Err("Unable to open butter file".to_string()), + }; + // write to file - write!(butter, "{}", contents_str); + write!(butter_write, "{}\n", contents_str); // remove it match remove_file(removed_kernel.clone()) { diff --git a/src/commands/theatre/mod.rs b/src/commands/theatre/mod.rs index 9002d15..dc94729 100644 --- a/src/commands/theatre/mod.rs +++ b/src/commands/theatre/mod.rs @@ -2,8 +2,8 @@ use flate2::read::GzDecoder; use progress_bar::*; use reqwest::header::USER_AGENT; use reqwest::Url; -use std::fs::{self, create_dir_all, read_dir, remove_dir_all, DirEntry}; -use std::io::{self, Cursor, Read}; +use std::fs::{self, create_dir_all, read_dir, remove_dir_all, DirEntry, OpenOptions}; +use std::io::{self, BufReader, Cursor, Read}; use std::thread; use std::{fs::File, path::Path, process::Command}; use tar::Archive; @@ -12,7 +12,9 @@ use anyhow::Result; use clap::Parser; use crate::util::blame::BLAME; -use crate::util::{calculate_hash, open_config, Config, GithubTags, Print, PROD_DIR, SEP, TMP}; +use crate::util::{ + calculate_hash, open_config, update_ver_cache, Config, GithubTags, Print, PROD_DIR, SEP, TMP, +}; use super::build::build_thread; @@ -26,7 +28,7 @@ pub struct Options { url: bool, } -fn download_kernel(url: String, file_name: String) -> Result { +pub fn download_kernel(url: String, file_name: String) -> Result { create_dir_all(TMP()); init_progress_bar(2); @@ -115,6 +117,15 @@ fn download_kernel(url: String, file_name: String) -> Result { file_ext = file_ext )); + // used for version at end of download + let checksum = md5::compute(format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + file_name = file_name, + sep = SEP, + TMP_DIR = TMP(), + file_ext = file_ext + )); + if cfg!(target_os = "windows") { let mut deflate = source.unwrap(); let mut data = Vec::new(); @@ -153,8 +164,7 @@ fn download_kernel(url: String, file_name: String) -> Result { }; // untaring will save it to incorrect dir, need to move contents of dir outside of it. - let files = - read_dir(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)).unwrap(); + let files = read_dir(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)).unwrap(); // ensure the final dir is empty/non-existant so no errors are thrown during rename remove_dir_all(format!("{}{sep}{}", TMP(), file_name, sep = SEP)); @@ -194,12 +204,27 @@ fn download_kernel(url: String, file_name: String) -> Result { } } + let config = match open_config(&format!( + "{}{SEP}{}{SEP}.kernelrc", + TMP(), + file_name, + SEP = SEP + )) { + Ok(config) => config, + Err(_) => todo!(), + }; + + match update_ver_cache(config.kernel_name, checksum, &url) { + Ok(_) => (), + Err(err) => return Err(err), + } + finalize_progress_bar(); return Ok(format!("{}{sep}{}", TMP(), file_name, sep = SEP)); } -fn ensure_os_compat(path: String) -> Result { +pub fn ensure_os_compat(path: String) -> Result { let config = match open_config(&format!("{}{SEP}.kernelrc", path, SEP = SEP)) { Ok(config) => config, Err(_) => { @@ -230,6 +255,129 @@ fn ensure_os_compat(path: String) -> Result { Ok(config) } +// async fn download_homebrew(pkg: &str) -> Result<(), String> { +// // get the ruby homebrew file +// let req = match reqwest::Client::new() +// .get(format!( +// "https://raw.githubusercontent.com/Homebrew/homebrew-core/master/Formula/{}.rb", +// pkg.to_lowercase() +// )) +// .header(USER_AGENT, format!("Popcorn {}", BLAME.version)) +// .send() +// .await +// { +// Ok(data) => data, +// Err(_) => { +// Print::error("Could not fetch data (URL not valid)"); +// return Ok(()); +// } +// }; + +// if req.status() != 200 { +// Print::error("Could not fetch data (does the homebrew pkg exist?)"); +// return Ok(()); +// } + +// let ruby = match req.text().await { +// Ok(txt) => txt, +// Err(err) => { +// Print::error(&format!("Unable to get text response; {}", err)); +// return Ok(()); +// } +// }; + +// // parse the ruby from the homebrew pkg + +// let (kernel_name, url) = match parse_class(ruby) { +// Ok((kernel_name, url)) => (kernel_name, url), +// Err(()) => return Ok(()), +// }; + +// // download + +// let mut file_ext = ""; + +// if cfg!(target_os = "windows") { +// file_ext = ".zip"; + +// if let Ok(mut child) = Command::new("Invoke-WebRequest") +// .args([ +// &url, +// "-Out", +// &format!( +// "{TMP_DIR}{sep}{file_name}{file_ext}", +// sep = SEP, +// file_name = kernel_name, +// TMP_DIR = TMP(), +// file_ext = file_ext +// ), +// ]) +// .spawn() +// { +// let finished = child.wait().unwrap(); +// if !finished.success() { +// return Err("An error occured while downloading the kernel".to_string()); +// } +// } else { +// return Err("Could not run command to download external kernel".to_string()); +// } +// } else { +// file_ext = ".tar.gz"; + +// if let Ok(mut child) = Command::new("curl") +// .args([ +// "--silent", +// &url, +// "-L", +// "--output", +// &format!( +// "{TMP_DIR}{sep}{file_name}{file_ext}", +// sep = SEP, +// file_name = kernel_name, +// TMP_DIR = TMP(), +// file_ext = file_ext +// ), +// ]) +// .spawn() +// { +// let finished = child.wait().unwrap(); +// if !finished.success() { +// return Err("An error occured while downloading the kernel".to_string()); +// } +// } else { +// return Err( +// "Could not run command to download external kernel (is curl installed?)" +// .to_string(), +// ); +// } +// } + +// // open tar file (to get compressed data) +// let f = File::open(format!( +// "{TMP_DIR}{sep}{file_name}{file_ext}", +// sep = SEP, +// file_name = kernel_name, +// TMP_DIR = TMP(), +// file_ext = file_ext +// )) +// .unwrap(); +// let mut reader = BufReader::new(f); + +// let mut compressed_contents = Vec::new(); + +// reader.read_to_end(&mut compressed_contents); + +// // calculate md5 hash from compressed bytes +// let checksum = md5::compute(compressed_contents); + +// println!("{} @ {}", kernel_name, url); +// println!("MD5 Checksum: {:?}", checksum); + +// // final success -> add stuff to versions.txt + +// Ok(()) +// } + fn download_homebrew(pkg: &str) -> Result<(), String> { println!("Homebrew download {}", pkg); @@ -354,6 +502,28 @@ pub async fn handle(options: Options) -> Result<()> { let repo = kernel_name_split[0]; let pkg = kernel_name_split[1]; + // check if package with same name (could be same package) is already installed + let dir = &PROD_DIR(); + let prod_directory = Path::new(dir); + let butter_file = prod_directory.join("butter.sh").clone(); + + if butter_file.exists() { + let butter = OpenOptions::new() + .read(true) + .append(true) + .open(butter_file) + .unwrap(); + + let mut reader = BufReader::new(butter.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if contents.contains(pkg) { + Print::warn("A kernel with this name already exists!"); + return Ok(()); + } + } + // allow compatibility! if repo.to_lowercase() == "homebrew" || repo.to_lowercase() == "brew" { download_homebrew(pkg); @@ -414,7 +584,7 @@ pub async fn handle(options: Options) -> Result<()> { ver.unwrap(), os ), - file_name, + pkg.to_string(), ) { Ok(path) => path, Err(err) => { diff --git a/src/commands/update/mod.rs b/src/commands/update/mod.rs new file mode 100644 index 0000000..3d8fb07 --- /dev/null +++ b/src/commands/update/mod.rs @@ -0,0 +1,246 @@ +use anyhow::Result; +use clap::Parser; +use progress_bar::*; + +use std::fs::{create_dir_all, remove_dir_all}; +use std::io::{prelude::*, BufReader}; +use std::path::Path; +use std::process::Command; +use std::thread; +use std::{env, fs::OpenOptions}; + +use crate::util::{calculate_hash, Print, PROD_DIR, SEP, TMP}; + +use super::build::build_thread; +use super::remove::remove_prod; +use super::theatre::{download_kernel, ensure_os_compat}; + +#[derive(Debug, Parser)] +#[clap(about = "Update kernels.")] +pub struct Options { + #[clap(name = "kernel", help = "Name of kernel.")] + kernel: Option, +} + +fn download_and_checksum(url: String, file_name: String) -> Result { + create_dir_all(TMP()); + + init_progress_bar(2); + set_progress_bar_action("Downloading", Color::LightBlue, Style::Bold); + + let mut file_ext = ""; + + if cfg!(target_os = "windows") { + file_ext = ".zip"; + + if let Ok(mut child) = Command::new("Invoke-WebRequest") + .args([ + &url, + "-Out", + &format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + sep = SEP, + file_name = file_name, + TMP_DIR = TMP(), + file_ext = file_ext + ), + ]) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + return Err("An error occured while downloading the kernel".to_string()); + } + print_progress_bar_info( + "Success", + &format!("loading {}", url), + Color::Green, + Style::Bold, + ); + inc_progress_bar(); + } else { + return Err("Could not run command to download external kernel".to_string()); + } + } else { + file_ext = ".tar.gz"; + + if let Ok(mut child) = Command::new("curl") + .args([ + "--silent", + &url, + "-L", + "--output", + &format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + sep = SEP, + file_name = file_name, + TMP_DIR = TMP(), + file_ext = file_ext + ), + ]) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + print_progress_bar_info("Failed", "to download", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("An error occured while downloading the kernel".to_string()); + } + print_progress_bar_info( + "Success", + &format!("downloaded {}", url), + Color::Green, + Style::Bold, + ); + inc_progress_bar(); + } else { + print_progress_bar_info("Failed", "to run command", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err( + "Could not run command to download external kernel (is curl installed?)" + .to_string(), + ); + } + } + + let checksum = md5::compute(format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + file_name = file_name, + sep = SEP, + TMP_DIR = TMP(), + file_ext = file_ext + )); + + return Ok(format!("{:?}", checksum)); +} + +pub async fn handle(options: Options) -> Result<()> { + // redownload and calculate checksum, if eq then dont update. + // if not eq then remove kernel and reinstall. + + // get kernel from passed in args + let kernel_name = match options.kernel { + Some(kernel) => kernel, + None => { + Print::error("Please specify a theatre."); + return Ok(()); + } + }; + + // get link to redownload update *** note: updates only work for theatres *** + let prod_dir = &PROD_DIR(); + let ver = Path::new(prod_dir).join("versions.txt"); + + if !ver.exists() { + // version file does not exist + Print::error("No version file detected (no compat theatre kernels installed?)") + } + + let versions_file = OpenOptions::new() + .read(true) + .append(true) + .open(ver) + .unwrap(); + + let mut reader = BufReader::new(versions_file.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + let split_contents = contents.splitn(2, &kernel_name).collect::>(); // [/* pre-kernel versions */, " old_checksum url\n next_kernel etc.."] + let split_spaced_contents = split_contents[1] + .split(|c| c == ' ' || c == '\n') + .collect::>(); // [old_checksum, url] + + let old_checksum = split_spaced_contents[1]; + let url = split_spaced_contents[2].to_string(); + + let checksum = match download_and_checksum(url.clone(), kernel_name.clone()) { + Ok(checksum) => checksum, + Err(_) => { + Print::error("Unable to calculate new checksum."); + return Ok(()); + } + }; + + if old_checksum == checksum { + Print::info("No update required; no new version detected.") + } else { + Print::info("Update needed."); + + // remove old + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + match remove_prod(kernel_name.clone(), PATH) { + Ok(_) => { + Print::info("(1/2) Removed pre-existing kernel."); + } + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + }; + + // download/reinstall files + let download_dir = match download_kernel(url, kernel_name.clone()) { + Ok(path) => path, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + let config = match ensure_os_compat(download_dir.clone()) { + Ok(config) => config, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + if Path::new(&PROD_DIR()) + .join(config.kernel_name.clone()) + .exists() + { + Print::warn("A kernel with that name already exists."); + remove_dir_all(download_dir.clone()); + return Ok(()); + } + + // build here + let kernel_name = config.kernel_name.clone(); + let kernel_type = config.kernel_type.clone(); + + if &kernel_name == "" { + Print::error("No kernel_name in the config file."); + return Ok(()); + } else if &kernel_type == "" { + Print::error("No kernel_type in the config file."); + return Ok(()); + } else if &config.seed_cmd == "" { + Print::error("No seed_cmd in the config file."); + return Ok(()); + } + + let dir = &PROD_DIR(); + let prod_path = Path::new(dir); + + if !prod_path.exists() { + create_dir_all(prod_path); + } + + let mut output = kernel_name.clone(); + + if kernel_type.to_lowercase() == "unpacked" { + output += &SEP.to_string(); + }; + + thread::spawn(|| build_thread(output, config, true, download_dir)).join(); + + Print::success(&format!("(2/2) Successfully updated {}.", kernel_name)); + } + + Ok(()) +} diff --git a/src/util.rs b/src/util.rs index 05acedf..0eefc5e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,10 +2,12 @@ use anyhow::Result; use chrono::Local; use console::style; use dirs; +use md5::Digest; use std::collections::hash_map::DefaultHasher; use std::env; -use std::fs::Permissions; +use std::fs::{File, OpenOptions, Permissions}; use std::hash::{Hash, Hasher}; +use std::io::{BufReader, Read, Write}; use serde_derive::{Deserialize, Serialize}; @@ -174,6 +176,122 @@ pub fn calculate_hash(t: &T) -> u64 { s.finish() } +pub fn update_ver_cache(kernel_name: String, checksum: Digest, url: &str) -> Result<(), String> { + let prod_dir = &PROD_DIR(); + let ver = Path::new(prod_dir).join("versions.txt"); + + if !ver.exists() { + // version file does not exist + match File::create(&ver) { + Ok(_) => (), + Err(_) => return Err("Unable to create versions file".to_string()), + } + } + + // check if kernel already there and if checksum matches then replace, if its same checksum then dont add. + let versions_file = OpenOptions::new() + .read(true) + .append(true) + .open(ver) + .unwrap(); + + let mut reader = BufReader::new(versions_file.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if !contents.contains(&kernel_name) { + let mut file = match OpenOptions::new() + .write(true) + .append(true) + .open(Path::new(prod_dir).join("versions.txt")) + { + Ok(file) => file, + Err(_e) => return Err("version file not found (unable to create?)".to_string()), + }; + + file.write( + format!( + "{kernel} {:?} {url}\n", + checksum, + kernel = kernel_name, + url = url + ) + .as_bytes(), + ) + .unwrap(); + } else if !contents.contains(&format!("{:x}", checksum)) && contents.contains(&kernel_name) { + let split_contents = contents.splitn(2, &kernel_name).collect::>(); + let check_split = split_contents[1].split(" ").collect::>(); + let old_checksum = check_split[1]; + let remaining_split = split_contents[1].split(old_checksum).collect::>(); + let updated_version_checksum = format!( + "{}{} {:x}{}", + &split_contents[0], kernel_name, checksum, remaining_split[1] + ); + + let mut file = match OpenOptions::new() + .write(true) + .truncate(true) + .open(Path::new(prod_dir).join("versions.txt")) + { + Ok(file) => file, + Err(_e) => return Err("version file not found (unable to create?)".to_string()), + }; + + file.write(updated_version_checksum.as_bytes()).unwrap(); + } + + Ok(()) +} + +// pub fn parse_class(ruby: String) -> Result<(String, String), ()> { +// let runtime = minutus::Evaluator::build(); + +// let parser = ruby.split("\n").collect::>(); + +// // fix ruby class +// let fixed_ruby: String = parser +// .iter() +// .map(|x| { +// if x.contains("Formula") { +// x.split(" < ").collect::>()[0].to_owned() + "\n" +// } else { +// x.to_string() + "\n" +// } +// }) +// .collect(); + +// println!("{}", fixed_ruby); + +// let out = runtime.evaluate(&fixed_ruby).unwrap(); + +// println!("{:?}", out); + +// // if above works then dont need this vv + +// let url = parser +// .iter() +// .filter(|x| x.contains("url")) +// .cloned() +// .collect::>()[0] +// .split("\"") +// .collect::>()[1]; + +// // return depends_on(s) +// // ... + +// // return install func as seed_cmd +// // ... + +// let kernel_name = "cowsay".to_string(); + +// if kernel_name == "" || url == "" { +// return Err(()); +// } + +// Ok((kernel_name, url.to_string())) +// } + pub struct Print; impl Print { @@ -211,7 +329,7 @@ impl Print { } #[cfg(unix)] -pub fn set_permissions(metadata_permissions: &mut Permissions, mode: u32) -> Result<(), ()> { +pub fn set_permissions(metadata_permissions: &mut Permissions, _mode: u32) -> Result<(), ()> { use std::os::unix::prelude::PermissionsExt; metadata_permissions.set_mode(0o511); From f6875484988a4b94d8b13acee2d39708c2280794 Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 24 Sep 2023 15:59:00 -0700 Subject: [PATCH 21/21] fix(rm): add versions to blacklist --- src/commands/remove/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/remove/mod.rs b/src/commands/remove/mod.rs index 2ac66d0..ddc240b 100644 --- a/src/commands/remove/mod.rs +++ b/src/commands/remove/mod.rs @@ -165,7 +165,7 @@ pub fn remove_prod(kernel_name: String, PATH: String) -> Result Err(err) => Err(err), }; } else { - if kernel_name == "butter.sh" { + if kernel_name == "butter.sh" || kernel_name == "versions.txt" { return Err("Not a kernel".to_string()); }