diff --git a/.envrc b/.envrc deleted file mode 100644 index 3550a30..0000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake diff --git a/.gitignore b/.gitignore deleted file mode 100755 index 6d5293e..0000000 --- a/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/target -.idea -.direnv - -examples/workspace/* -examples/repository/* -!examples/workspace/mlc.toml -!examples/repository/mlc.toml \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 5d7fb48..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,17 +0,0 @@ -image: "rust:latest" - -cargo:version: - script: - - rustc --version && cargo --version - -cargo:clippy: - before_script: - - rustup component add clippy - script: - - cargo clippy --no-deps -- -D clippy::all - -cargo:fmt: - before_script: - - rustup component add rustfmt - script: - - cargo fmt --check diff --git a/Buildfile b/Buildfile new file mode 100644 index 0000000..25eb1bd --- /dev/null +++ b/Buildfile @@ -0,0 +1,24 @@ +# Key: +# {{ image }}: will be replaced with `base.image` from .mlc/config.toml +# {{ pkg }} : will be replaced with the name of the package being built +# Post-build, the contents of /out will be copied to the host at `repo.out` from .mlc/config.toml + +FROM {{ image }} + +RUN mkdir /out +COPY {{ pkg }} /tmp/{{ pkg }} + +RUN pacman -Syu --noconfirm +RUN pacman -S --noconfirm --needed base-devel + +RUN useradd -m -G wheel build-user +RUN echo '%wheel ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers +RUN chown -R build-user /tmp/{{ pkg }} + +USER build-user +WORKDIR /tmp/{{ pkg }} + +RUN makepkg -s {{ flags }} --noconfirm + +USER root +RUN cp *.pkg.tar.* /out diff --git a/Cargo.lock b/Cargo.lock index db7d31e..0bea699 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,40 +4,96 @@ version = 3 [[package]] name = "Malachite" -version = "2.1.1" +version = "3.0.0" dependencies = [ + "cargo_toml", "clap", - "colored", - "crossterm", + "color-eyre", + "compress-tools", + "fs_extra", + "futures-util", + "glob", + "gpgme", + "i18n-embed", + "i18n-embed-fl", + "lazy_static", "libc", - "mimalloc", - "regex", - "rm_rf", + "liquid", + "names", + "podman-api", + "rust-embed", "serde", - "serde_derive", - "spinoff", "tabled", - "toml", + "tar", + "thiserror", + "tokio", + "toml 0.7.2", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +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-str" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e50acdf02a3ac61856d5c8d576a8b5fb452a6549f667ca29fefaa18c2cd05135" +checksum = "84252a7e1a0df81706ce70bbad85ed1e4916448a4093ccd52dd98c6a44a477cd" dependencies = [ "ansitok", ] [[package]] name = "ansitok" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2c6eb31f539d8fc1df948eb26452d6c781be4c9883663e7acb258644b71d5b1" +checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83" dependencies = [ "nom", + "vte", ] +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "arrayvec" version = "0.5.2" @@ -45,21 +101,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] -name = "atty" -version = "0.2.14" +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ - "hermit-abi", + "addr2line", + "cc", + "cfg-if", "libc", - "winapi", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "base64" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "bitflags" @@ -67,17 +133,81 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "build-rs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00b8763668c99f8d9101b8a0dd82106f58265464531a79b2cef0d9a30c17dd2" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + [[package]] name = "bytecount" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cargo_toml" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f83bc2e401ed041b7057345ebc488c005efa0341d5541ce7004d30458d0090b" +dependencies = [ + "serde", + "toml 0.7.2", +] + [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-expr" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +dependencies = [ + "smallvec", +] [[package]] name = "cfg-if" @@ -85,28 +215,43 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + [[package]] name = "clap" -version = "3.2.23" +version = "4.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex", - "indexmap", + "is-terminal", "once_cell", "strsim", "termcolor", - "textwrap", + "terminal_size", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck", "proc-macro-error", @@ -117,513 +262,2070 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" dependencies = [ "os_str_bytes", ] [[package]] -name = "colored" -version = "2.0.0" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ - "atty", - "lazy_static", - "winapi", + "termcolor", + "unicode-width", ] [[package]] -name = "crossterm" -version = "0.25.0" +name = "color-eyre" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" dependencies = [ - "bitflags", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", + "url", ] [[package]] -name = "crossterm_winapi" -version = "0.9.0" +name = "color-spantrace" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" dependencies = [ - "winapi", + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", ] [[package]] -name = "fnv" -version = "1.0.7" +name = "compress-tools" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "3a3b1511783270e75d95749c851008a01fba662a8136e488d29d2b3a51a56e08" +dependencies = [ + "derive_more", + "libc", + "pkg-config", + "vcpkg", +] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "containers-api" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "56082e32f18a6d60f06c736b49e234deffb93b13cb87091a39e0dec053d03819" +dependencies = [ + "chrono", + "flate2", + "futures-util", + "http", + "hyper", + "hyperlocal", + "log", + "mime", + "paste", + "pin-project 1.0.12", + "serde", + "serde_json", + "tar", + "thiserror", + "tokio", + "url", +] [[package]] -name = "heck" -version = "0.4.0" +name = "conv" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" +dependencies = [ + "custom_derive", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "cpufeatures" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] -name = "indexmap" -version = "1.9.2" +name = "crc32fast" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "autocfg", - "hashbrown", + "cfg-if", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] [[package]] -name = "libc" -version = "0.2.139" +name = "cstr-argument" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "b6bd9c8e659a473bce955ae5c35b116af38af11a7acb0b480e01f3ed348aeb40" +dependencies = [ + "cfg-if", + "memchr", +] + +[[package]] +name = "custom_derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" [[package]] -name = "libmimalloc-sys" -version = "0.1.28" +name = "cxx" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d1c67deb83e6b75fa4fe3309e09cfeade12e7721d95322af500d3814ea60c9" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" dependencies = [ "cc", - "libc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", ] [[package]] -name = "lock_api" -version = "0.4.9" +name = "cxx-build" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" dependencies = [ - "autocfg", - "scopeguard", + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", ] [[package]] -name = "log" -version = "0.4.17" +name = "cxxbridge-flags" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] -name = "maplit" -version = "1.0.2" +name = "derive_more" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "memchr" -version = "2.5.0" +name = "digest" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] [[package]] -name = "mimalloc" -version = "0.1.32" +name = "displaydoc" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2374e2999959a7b583e1811a1ddbf1d3a4b9496eceb9746f1192a59d871eca" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "libmimalloc-sys", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "doc-comment" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] -name = "mio" -version = "0.8.5" +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" dependencies = [ + "errno-dragonfly", "libc", - "log", - "wasi", - "windows-sys", + "winapi", ] [[package]] -name = "nom" -version = "7.1.1" +name = "errno-dragonfly" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ - "memchr", - "minimal-lexical", + "cc", + "libc", ] [[package]] -name = "once_cell" -version = "1.16.0" +name = "eyre" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] [[package]] -name = "os_str_bytes" -version = "6.4.1" +name = "filetime" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.45.0", +] [[package]] -name = "papergrid" -version = "0.5.1" +name = "find-crate" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453cf71f2a37af495a1a124bf30d4d7469cfbea58e9f2479be9d222396a518a2" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" dependencies = [ - "ansi-str", - "bytecount", - "fnv", - "strip-ansi-escapes", - "unicode-width", + "toml 0.5.11", ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "flate2" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ - "lock_api", - "parking_lot_core", + "crc32fast", + "miniz_oxide", ] [[package]] -name = "parking_lot_core" -version = "0.9.5" +name = "fluent" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell", "smallvec", - "windows-sys", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +dependencies = [ + "thiserror", +] + +[[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 = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-executor" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "futures_codec" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce54d63f8b0c75023ed920d46fd71d0cbbb830b0ee012726b5b4f506fb6dea5b" +dependencies = [ + "bytes 0.5.6", + "futures", + "memchr", + "pin-project 0.4.30", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gpg-error" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89aaeddbfb92313378c58e98abadaaa34082b3855f1d455576eeeda08bd592c" +dependencies = [ + "libgpg-error-sys", +] + +[[package]] +name = "gpgme" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57539732fbe58eacdb984734b72b470ed0bca3ab7a49813271878567025ac44f" +dependencies = [ + "bitflags", + "cfg-if", + "conv", + "cstr-argument", + "gpg-error", + "gpgme-sys", + "libc", + "memoffset", + "once_cell", + "smallvec", + "static_assertions", +] + +[[package]] +name = "gpgme-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "509223d659c06e4a26229437d6ac917723f02d31917c86c6ecd50e8369741cf7" +dependencies = [ + "build-rs", + "libc", + "libgpg-error-sys", + "system-deps", + "winreg", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes 1.4.0", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes 1.4.0", + "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.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes 1.4.0", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper", + "pin-project 1.0.12", + "tokio", +] + +[[package]] +name = "i18n-config" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d9f93ceee6543011739bc81699b5e0cf1f23f3a80364649b6d80de8636bc8df" +dependencies = [ + "log", + "serde", + "serde_derive", + "thiserror", + "toml 0.5.11", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2653dd1a8be0726315603f1c180b29f90e5b2a58f8b943d949d5170d9ad81101" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "lazy_static", + "locale_config", + "log", + "parking_lot", + "rust-embed", + "thiserror", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a425b9bbdc2e4cd797a2a79528662cb61894bd36db582e48da2c56c28eb727cd" +dependencies = [ + "dashmap", + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "strsim", + "syn", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db2330e035808eb064afb67e6743ddce353763af3e0f2bdfc2476e00ce76136" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn", +] + +[[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", +] + +[[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 = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "serde", + "static_assertions", +] + +[[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.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libgpg-error-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c97079310f39c835d3bd73578379d040f779614bb331c7ffbb6630fee6420290" +dependencies = [ + "build-rs", + "system-deps", + "winreg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "liquid" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f55b9db2305857de3b3ceaa0e75cb51a76aaec793875fe152e139cb8fed05c" +dependencies = [ + "doc-comment", + "liquid-core", + "liquid-derive", + "liquid-lib", + "serde", +] + +[[package]] +name = "liquid-core" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93764837aeac37f14b74708cd88a44d82edfa9ad2b1bcd9a3b4d8802fdd9f98" +dependencies = [ + "anymap2", + "itertools", + "kstring", + "liquid-derive", + "num-traits", + "pest", + "pest_derive", + "regex", + "serde", + "time 0.3.19", +] + +[[package]] +name = "liquid-derive" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926454345f103e8433833077acdbfaa7c3e4b90788d585a8358f02f0b8f5a469" +dependencies = [ + "proc-macro2", + "proc-quote", + "syn", +] + +[[package]] +name = "liquid-lib" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd06ca30ae026d26ee7fa8596f9590959e2d3726bc5a0f16a21ac4f050ec83c0" +dependencies = [ + "itertools", + "liquid-core", + "once_cell", + "percent-encoding", + "regex", + "time 0.3.19", + "unicode-segmentation", +] + +[[package]] +name = "locale_config" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" +dependencies = [ + "lazy_static", + "objc", + "objc-foundation", + "regex", + "winapi", +] + +[[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 = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "names" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" +dependencies = [ + "rand", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + +[[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.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "papergrid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1526bb6aa9f10ec339fb10360f22c57edf81d5678d0278e93bc12a47ffbe4b01" +dependencies = [ + "ansi-str", + "ansitok", + "bytecount", + "fnv", + "unicode-width", +] + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pest" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal 1.0.12", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +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 = "podman-api" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b8a428a3381f5e1fcfa1fb94092017f193c75f2d7959bc799ee82dadf0f72d" +dependencies = [ + "base64", + "byteorder", + "bytes 1.4.0", + "chrono", + "containers-api", + "flate2", + "futures-util", + "futures_codec", + "log", + "paste", + "podman-api-stubs", + "serde", + "serde_json", + "tar", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "podman-api-stubs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9a63943c0c6e69efcd252ad61f4914270b7e3fec170e6a04ffdb2fc8dcd6a9" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[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-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-quote" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e84ab161de78c915302ca325a19bee6df272800e2ae1a43fe3ef430bab2a100" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "proc-quote-impl", + "quote", + "syn", +] + +[[package]] +name = "proc-quote-impl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb3ec628b063cdbcf316e06a8b8c1a541d28fa6c0a8eacd2bfb2b7f49e88aa0" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rust-embed" +version = "6.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[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 = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + +[[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[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.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml 0.5.11", + "version-compare", +] + +[[package]] +name = "tabled" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c3ee73732ffceaea7b8f6b719ce3bb17f253fa27461ffeaf568ebd0cdb4b85" +dependencies = [ + "ansi-str", + "papergrid", + "tabled_derive", + "unicode-width", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "tabled_derive" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "beca1b4eaceb4f2755df858b88d9b9315b7ccfd1ffd0d7a48a52602301f01a57" dependencies = [ - "proc-macro-error-attr", + "heck", + "proc-macro-error", "proc-macro2", "quote", "syn", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "tar" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "filetime", + "libc", + "xattr", ] [[package]] -name = "proc-macro2" -version = "1.0.49" +name = "termcolor" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ - "unicode-ident", + "winapi-util", ] [[package]] -name = "psm" -version = "0.1.21" +name = "terminal_size" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "4c9afddd2cec1c0909f06b00ef33f94ab2cc0578c4a610aa208ddfec8aa2b43a" dependencies = [ - "cc", + "rustix", + "windows-sys 0.45.0", ] [[package]] -name = "quote" -version = "1.0.23" +name = "thiserror" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ - "proc-macro2", + "thiserror-impl", ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "thiserror-impl" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "bitflags", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "regex" -version = "1.7.0" +name = "thread_local" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "regex-syntax", + "cfg-if", + "once_cell", ] [[package]] -name = "regex-syntax" -version = "0.6.28" +name = "time" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] [[package]] -name = "rm_rf" -version = "0.6.2" +name = "time" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3443b7a35aa12ed2e99edfc0ecbefe6a53b4848305cc83e29981dfa1aea1f71e" +checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" dependencies = [ - "stacker", + "itoa", + "serde", + "time-core", + "time-macros", ] [[package]] -name = "rustversion" -version = "1.0.11" +name = "time-core" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] -name = "scopeguard" -version = "1.1.0" +name = "time-macros" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" +dependencies = [ + "time-core", +] [[package]] -name = "serde" -version = "1.0.151" +name = "tinystr" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +checksum = "7ac3f5b6856e931e15e07b478e98c8045239829a65f9156d4fa7e7788197a5ef" +dependencies = [ + "displaydoc", +] [[package]] -name = "serde_derive" -version = "1.0.151" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "proc-macro2", - "quote", - "syn", + "tinyvec_macros", ] [[package]] -name = "signal-hook" -version = "0.3.14" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" -dependencies = [ - "libc", - "signal-hook-registry", -] +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "signal-hook-mio" -version = "0.2.3" +name = "tokio" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ + "autocfg", + "bytes 1.4.0", "libc", + "memchr", "mio", - "signal-hook", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.42.0", ] [[package]] -name = "signal-hook-registry" -version = "1.4.0" +name = "tokio-macros" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "libc", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "smallvec" -version = "1.10.0" +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] [[package]] -name = "spinoff" -version = "0.5.4" +name = "toml" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812db6f40551bdcdb10e1d2070ec33f69805d2bfb7e59426c7d14e7e1b4194dd" +checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" dependencies = [ - "colored", - "maplit", - "once_cell", - "strum", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "stacker" -version = "0.1.15" +name = "toml_datetime" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "winapi", + "serde", ] [[package]] -name = "strip-ansi-escapes" -version = "0.1.1" +name = "toml_edit" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5" dependencies = [ - "vte", + "indexmap", + "nom8", + "serde", + "serde_spanned", + "toml_datetime", ] [[package]] -name = "strsim" -version = "0.10.0" +name = "tower-service" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] -name = "strum" -version = "0.24.1" +name = "tracing" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "strum_macros", + "cfg-if", + "pin-project-lite", + "tracing-core", ] [[package]] -name = "strum_macros" -version = "0.24.3" +name = "tracing-core" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", + "once_cell", + "valuable", ] [[package]] -name = "syn" -version = "1.0.107" +name = "tracing-error" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "tracing", + "tracing-subscriber", ] [[package]] -name = "tabled" -version = "0.8.0" +name = "tracing-subscriber" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b2f8c37d26d87d2252187b0a45ea3cbf42baca10377c7e7eaaa2800fa9bf97" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "ansi-str", - "papergrid", - "tabled_derive", - "unicode-width", + "sharded-slab", + "thread_local", + "tracing-core", ] [[package]] -name = "tabled_derive" +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "type-map" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9ee618502f497abf593e1c5c9577f34775b111480009ffccd7ad70d23fcaba8" +checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "rustc-hash", ] [[package]] -name = "termcolor" -version = "1.1.3" +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] -name = "textwrap" -version = "0.16.0" +name = "ucd-trie" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] -name = "toml" -version = "0.5.10" +name = "unic-langid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" dependencies = [ "serde", + "tinystr", ] +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[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-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[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 = "utf8parse" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +[[package]] +name = "valuable" +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-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + [[package]] name = "version_check" version = "0.9.4" @@ -651,12 +2353,93 @@ dependencies = [ "quote", ] +[[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 = "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[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.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + [[package]] name = "winapi" version = "0.3.9" @@ -703,44 +2486,86 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "xattr" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] diff --git a/Cargo.toml b/Cargo.toml old mode 100755 new mode 100644 index 5b72686..d1e3bdd --- a/Cargo.toml +++ b/Cargo.toml @@ -1,40 +1,46 @@ [package] name = "Malachite" -version = "2.1.1" -authors = ["michal "] +version = "3.0.0" edition = "2021" -description = "Packaging tool for pacman repositories" -repository = "https://github.com/crystal-linux/malachite" -license-file = "LICENSE" -keywords = ["pacman", "repository", "packaging"] -categories = ["filesystem", "development-tools"] + +[package.metadata] +codename = "Mi Goreng" [[bin]] name = "mlc" path = "src/main.rs" -[profile.release] -incremental = true -debug = false -lto = "fat" -codegen-units = 1 +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] - -clap = { version = "3.2.8", features = ["derive", "suggestions"] } -toml = { version = "0.5.9", default-features = false } -serde = { version = "1.0.139", default-features = false } -serde_derive = { version = "1.0.139", default-features = false } -libc = { version = "0.2.126", default-features = false } -colored = { version = "2.0.0", default-features = false } -tabled = { version = "0.8.0", default-features = false, features = [ +libc = "0.2.137" +clap = { version = "4.0.18", features = ["derive", "wrap_help"] } +rust-embed = "6.4.2" +thiserror = "1.0.37" +i18n-embed = { version = "0.13.4", features = [ + "fluent-system", + "desktop-requester", +] } +i18n-embed-fl = "0.6.4" +color-eyre = { version = "0.6.2", features = ["issue-url", "url"] } +lazy_static = "1.4.0" +toml = "0.7.2" +serde = { version = "1.0.147", default-features = false, features = [ "derive", - "color", + "serde_derive", ] } -crossterm = { version = "0.25.0", default-features = false } -regex = { version = "1.6.0", default-features = false, features = ["std"] } -spinoff = { version = "0.5.4", default-features = false } -rm_rf = { version = "0.6.2", default-features = false } +podman-api = "0.8.0" +names = { version = "0.14.0", default-features = false } +futures-util = "0.3.25" +tokio = { version = "1.21.2", features = ["full"] } +fs_extra = "1.2.0" +tar = "0.4.38" +glob = "0.3.0" +compress-tools = "0.14.0" +gpgme = "0.11.0" +tabled = { version = "0.10.0", features = ["color"] } +liquid = "0.26.0" -[target.'cfg(target_os = "linux")'.dependencies] -mimalloc = { version = "0.1.29" } +[build-dependencies] +cargo_toml = "0.15.2" +serde = { version = "1.0.147", default-features = false, features = ["derive"] } diff --git a/Generatefile b/Generatefile new file mode 100644 index 0000000..162917a --- /dev/null +++ b/Generatefile @@ -0,0 +1,25 @@ +# Key: +# {{ image }}: will be replaced with `base.image` from .mlc/config.toml +# {{ name }} : will be replaced with `repo.name` from .mlc/config.toml +# Post-build, the contents of /repo will be copied to the host at `repo.repo` from .mlc/config.toml +# If `repo.security` is set to true, all resultant *.pkg.tar.* files will be GPG signed by the host + +FROM {{ image }} + +RUN mkdir /{{ repo }} +COPY out /tmp/{{ name }} + +RUN pacman -Syu --noconfirm +RUN pacman -S --noconfirm --needed pacman-contrib + +RUN useradd -m -G wheel generate-user +RUN echo '%wheel ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers +RUN chown -R generate-user /tmp/{{ name }} + +USER generate-user +WORKDIR /tmp/{{ name }} + +RUN repo-add {{ name }}.db.tar.gz *.pkg.tar.* + +USER root +RUN cp /tmp/{{ name }}/* /{{ repo }} \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f288702..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README.md b/README.md deleted file mode 100644 index 5e7f134..0000000 --- a/README.md +++ /dev/null @@ -1,62 +0,0 @@ -

- - Logo - -

- -

Malachite

- -

- License - GitHub isses - GitHub pull requests
- Discord - The maintainer of this repository
- Mastodon Follow - Twitter Follow -

- - - -

-Malachite is a simple yet useful workspace and local repository management tool, made for packagers of Arch Linux based distributions. -

--> Detailed Usage Guide <--

-

- - - -### Basic Usage Guide - -| Action | Command | -|--------------------------------------------------------|-------------------------------------------| -| Build a package | mlc build \ [all if left empty] | -| Generate local repository | mlc repo-gen | -| Update local repos/PKGBUILDs | mlc pull/update [all if left empty] | -| Create and/or open config file | mlc conf | -| Initialises repo/workspace based on config in mlc.toml | mlc clone/init | -| Displays information about a Malachite repository | mlc info/status | - -### Pacman Repository Creation - -- `mlc config` to create the config (and also populate it) -- `mlc init` to build repository base from config file -- `mlc build ` to either build individual packages, or don't specify package names to build all packages in mlc.toml - - `build` typically automatically updates the repository unless `--no-regen` is passed, if so: -- `mlc repo-gen` to generate functional pacman repository at \/\.db from built packages - - -## How to build: - -Tested on latest Cargo (1.60.0-nightly) - -### Debug/development builds - -- `cargo build` - -### Optimised/release builds - -- `cargo build --release` - -### AUR - -- https://aur.archlinux.org/packages/malachite diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..f22924f --- /dev/null +++ b/build.rs @@ -0,0 +1,20 @@ +use serde::Deserialize; +use std::path::PathBuf; + +use cargo_toml::Manifest; + +#[derive(Clone, Debug, Deserialize)] +struct Metadata { + codename: String, +} + +fn main() { + let manifest = Manifest::::from_path_with_metadata(PathBuf::from("Cargo.toml")) + .expect("Failed to read manifest (Cargo.toml)"); + + if let Some(package) = manifest.package { + if let Some(metadata) = package.metadata { + println!("cargo:rustc-env=MALACHITE_CODENAME={}", metadata.codename); + } + } +} diff --git a/docs/COMMON_FEATURES.md b/docs/COMMON_FEATURES.md deleted file mode 100644 index 2bf3d53..0000000 --- a/docs/COMMON_FEATURES.md +++ /dev/null @@ -1,128 +0,0 @@ -# Common Features Between Modes -As [mode]us, shared of between uh… repositories… or something. - -### What you need to know -Malachite is fairly fleshed out in Repository mode, and not so much in Workspace mode. - -This isn't of course because I'm lazy and hate Workspace mode or anything, there's just not -a lot *to add*. - -Without further ado, let's take a look at this example config file. - -```toml -# mlc.toml - -[base] -mode = "workspace" -smart_pull = true - -[mode.workspace] -git_info = true -colorblind = true - -[repositories] -repos = [ - "foo:repo1:2", - "foo:repo2/testing", - "bar:baz!", - "bar:qux/testing!:1", -] - -[repositories.urls] -foo = "https://example.org/{}.git" -bar = "https://example.org/other/{}.git" -``` - -Now, this is going to look really confusing at first, but bear with me. - -In this document, we'll cover only what is required to know for **both** modes. -More specialized tutorials will be linked for each mode at the bottom of this page. - -Let's start with the base(ics). - - -### Base Config -The base config defines a few common parameters between all the Malachite modes. - -```toml -[base] -mode = "workspace" -smart_pull = true -``` - -In this snippet, we define `mode` to be `"workspace"`. - -`base.mode` in Malachite can only ever be one of `"workspace"` or `"repository"`, and defines, drumroll… -The mode in which it operates. If it is set to anything but those 2 modes, it crashes. - -Also defined in this snippet is `smart_pull`, which controls whether to pull… smartly. - -What that actually means is that instead of just performing a simple `git pull` in each repository, Malachite -will: - -- First run `git remote update` to fetch new remotes from origin -- Then run `git status` and parse the output to see if the current branch is behind -- If the current branch is behind, it runs a regular `git pull`, which will take advantage of the remotes - already being updated. - -Theoretically, this only actually speeds things up by a minute amount (think milliseconds, really). Where this feature shines however is in repository mode, -where it enables helpful automation features such as `build_on_update`. - -Regardless, it's recommended to keep this enabled for the slight speed-up, and only disable it if it causes issues. -I've never personally had issues with it in the past, but who knows what could happen. This is Git we're talking about. - - -### Repositories Config - -The repositories config is realistically what makes Malachite churn repo butter internally. It's the whole -purpose of what it does, and because of that we've tried to come up with a neat little system to help -facilitate many packages without having to type each URL out a million times. - -```toml -[repositories] -repos = [ - "foo:repo1:2", - "foo:repo2/testing", - "bar:baz!", - "bar:qux/testing!:1", -] - -[repositories.urls] -foo = "https://example.org/{}.git" -bar = "https://example.org/other/{}.git" -``` - -The way this works is simple: -- We have 2 URLs in the `repositories.urls` key. -- Each `repo` in the `repositories.repos` key is prefixed with an identifier. -- If the number is `foo`, it'll insert the URL with the id `foo`. - - Specifically, in the URL, it'll insert the defined `repo`'s name in place of the `%repo%` substring. - -#### Hang on, what are the special symbols???? - -I'm glad you asked! -- If you want to clone a specific branch, simply use the `/` delimiter. To clone repository `foo` on branch `bar`, use `id:foo/bar`. -- If you want a specific package to build first, use instances of `!` to set priority. This is explained later in the [Repository Mode](REPOSITORY_MODE.md) page - -The last `:` delimiter is entirely optional, and behaves differently depending on the mode: -- In Repository mode, it defines the desired commit hash/rev/tag to checkout on repository clone -- In Workspace mode, it defines the desired depth to clone the repository, useful with large git repositories, such as `nixpkgs`. - -That's literally it! - - -### Mode-Specific Config - -For mode-specific config, avert your eyes to the following links! - -- [Workspace Mode](WORKSPACE_MODE.md) -- [Repository Mode](REPOSITORY_MODE.md) - -### Examples - -Functioning config examples for both modes are available in the [examples](../examples) directory! - -### Usage - -Alternatively, you can look at the [Usage](USAGE.md) guide! - diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md deleted file mode 100644 index 6bd3462..0000000 --- a/docs/GETTING_STARTED.md +++ /dev/null @@ -1,47 +0,0 @@ -# Getting Started With Malachite -Baby's first Malachite repository! - -### What you need to know - -Malachite is: -- A pacman repository manager -- A workspace manager -- ~~Awesome~~ - -Malachite isn't: -- The end-all solution for all pacman repositories -- Perfect - - -### With that out of the way - -Hi! My name is Michal, and I wrote this tool pretty much on my own for [Crystal Linux](https://getcryst.al); -but it is not at all exclusive to Crystal. This tool should and will work on and for any pacman-based -distribution (so long as it packages all of Malachite's dependencies, of course). - -Throughout this tutorial, I'll explain each little feature of Malachite in what I hope to be bite-sized and -programmatic chunks. - -Without further ado, let's begin with the first, most important question: - - -### Modes - -What mode are you using malachite in? - -Currently, malachite supports 2 modes: - -#### Repository Mode -- Allows the user to configure and manage a remote (or local) pacman-based package repository -- Allows for customisability in repository name, signing preferences, signing key etc. -- Allows for basic levels of automation, by using features such as build_on_update - -#### Workspace Mode -- The most basic functionality of Malachite -- Just clones git directories into a "Workspace" directory for easier management -- Allows for basic pulling operations to keep your repositories up-to-date - -These modes essentially dictate everything about how Malachite functions, so much so that I now need to -split this page off before it gets too long! - -For more info, get started with the [Common Features](COMMON_FEATURES.md) page! diff --git a/docs/REPOSITORY_MODE.md b/docs/REPOSITORY_MODE.md deleted file mode 100644 index 60a580c..0000000 --- a/docs/REPOSITORY_MODE.md +++ /dev/null @@ -1,41 +0,0 @@ -# Repository Mode -PacManage your repositories in style! - -### Repository Config - -As opposed to the rather barren Workspace mode, the Repository mode config is rather fleshed out; -and we have a few options to choose from. - -Let's take an example section from a Repository mode config, - -```toml -[mode.repository] -name = "example" -build_on_update = true - -[mode.repository.signing] -enabled = true -key = "you@example.org" -on_gen = true -``` - -### Basic Repository Config - -To start with, there are 2 main config keys to Repository mode: -- `name`: Defines what pacman calls your repository. -- `build_on_update`: In conjunction with `smart_pull`, defines whether to rebuild packages automatically when an update is detected. - -### Signing - -Malachite also supports, and encourages, the signing of packages. -GPG Signing packages ensures that the user receives exactly what you packaged, without any chance of tampering. - -Calling back to the example above, we can see 3 config keys: - -- `enabled`: Defines whether to sign packages (heavily encouraged). -- `key`: Defines the GPG key ID to use for signing. -- `on_gen`: Defines whether to sign packages when they are built, or all at once on repository generation (this is also recommended). - ---- - -You can return to [Getting Started](GETTING_STARTED.md) page here! diff --git a/docs/USAGE.md b/docs/USAGE.md deleted file mode 100644 index 549e6a5..0000000 --- a/docs/USAGE.md +++ /dev/null @@ -1,32 +0,0 @@ -# Detailed Usage -Work it harder, make it better! - -### Global Flags - -| Flag | Description | -|-------------------|----------------------------------------------------------------------------------------------------------------------------------------| -| `--verbose`, `-v` | Prints lots of debug information to `stderr`. If something doesn't go right, sending us the output with this enabled will help greatly | -| `--exclude`, `-x` | Excludes the supplied package from the current operation. Can be used multiple times. | - -### Basic Commands - -| Action | Command | Extra Flags | -|-----------------------------------------------------------------------------------------|-------------------------------------------|------------------------------------------------------------------------------------------------------------------| -| Build a package/packages. | `mlc build ` [all if left empty] | `--no-regen`: Doesn't regenerate repository after build | -| Generate pacman repository | `mlc repo-gen` | | -| Update local repos/PKGBUILDs | `mlc pull/update` [all if left empty] | `--no-regen`: If `mode.repository.build_on_update` is `true`, Do not regenerate repository after package rebuild | -| Create and/or open config file | `mlc conf` | | -| Initialises repo/workspace based on config in mlc.toml | `mlc clone/init` | | -| Displays an info panel/overview of the current repo | `mlc info/status` | | -| Resets Malachite repository by deleting all directories, omitting `mlc.toml` and `.git` | `mlc clean/reset` | `--force`: Remove dirty directories (unstaged, untracked, etc) | - -### Exit Codes - -| AppExitCode (named Enum) | Exit code (i32) | Error Description | -|--------------------------|-----------------|--------------------------------------------------------------------------------------------------------| -| `RunAsRoot` | `1` | Malachite was run as root. This is highly discouraged. So highly, in fact, that it will refuse to run. | -| `PkgsNotFound` | `2` | No packages were specified/found for the desired operation | -| `DirNotEmpty` | `3` | The creation of a Malachite repository was attempted in a non-empty directory | -| `ConfigParseError` | `4` | The config file could not be parsed | -| `RepoParseError` | `5` | The repository info could not be parsed | -| `RepoNotClean` | `6` | The git repository is not clean and cannot be removed without `--force` | diff --git a/docs/WORKSPACE_MODE.md b/docs/WORKSPACE_MODE.md deleted file mode 100644 index 14efb54..0000000 --- a/docs/WORKSPACE_MODE.md +++ /dev/null @@ -1,31 +0,0 @@ -# Workspace Mode -You'll never have to work(space) another day in your life! - -### Workspace Config - -Taking an example section from the Workspace mode config, - -```toml -[mode.workspace] -git_info = true -colorblind = true -``` - -Currently, Workspace mode only has 2 options, both pertaining to the display of information. (`mlc info`) - -The first key is `git_info`, which is a boolean value. If it is true, the git information will be displayed alongside repository information. - -This information will be formatted as so: `D Pl Ps ` - -The key for the values is as follows: -- D: Whether the repository is dirty or not (unstaged changes) -- Pl: Whether there are unpulled changes at the remote -- Ps: Whether there are unpushed changes in your local repository - -These will be typically displayed in either Green (Clean) or Red (Dirty) - -However, if `colorblind` is set to true, the colors will instead be set to Blue (Clean) or Dark Red (Dirty), to be more discernible to colorblind users. - ---- - -You can return to [Getting Started](GETTING_STARTED.md) page here! diff --git a/examples/repository/mlc.toml b/examples/repository/mlc.toml deleted file mode 100644 index a8aa808..0000000 --- a/examples/repository/mlc.toml +++ /dev/null @@ -1,26 +0,0 @@ -[base] -mode = "repository" -smart_pull = true - -[mode.repository] -name = "repository-test" -build_on_update = true - -[mode.repository.signing] -enabled = true -key = "michal@tar.black" -on_gen = true - -[repositories] -repos = [ - "crs:malachite/development:0a5bdc9", # Note, in this example, these two - "mic:apod:v.1.1.2", # will fail to build. - "pkg:pfetch!", - "nms:rpass" # This too -] - -[repositories.urls] -crs = "https://github.com/crystal-linux/{}" -pkg = "https://github.com/crystal-linux/pkgbuild.{}" -mic = "https://git.tar.black/michal/{}" -nms = "https://github.com/not-my-segfault/{}" diff --git a/examples/workspace/mlc.toml b/examples/workspace/mlc.toml deleted file mode 100644 index 27d2bed..0000000 --- a/examples/workspace/mlc.toml +++ /dev/null @@ -1,20 +0,0 @@ -[base] -mode = "workspace" -smart_pull = true - -[mode.workspace] -git_info = true -colorblind = false - -[repositories] -repos = [ - "crs:amethyst", - "crs:malachite/development!", - "aur:notop-git", - "nix:nixpkgs/nixos-unstable:1", -] - -[repositories.urls] -crs = "https://github.com/crystal-linux/{}" -aur = "https://aur.archlinux.org/{}" -nix = "https://github.com/nixos/{}" diff --git a/flake.lock b/flake.lock index 36e9580..55c2fe4 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1655042882, - "narHash": "sha256-9BX8Fuez5YJlN7cdPO63InoyBy7dm3VlJkkmTt6fS1A=", + "lastModified": 1671096816, + "narHash": "sha256-ezQCsNgmpUHdZANDCILm3RvtO1xH8uujk/+EqNvzIOg=", "owner": "nix-community", "repo": "naersk", - "rev": "cddffb5aa211f50c4b8750adbec0bbbdfb26bb9f", + "rev": "d998160d6a076cfe8f9741e56aeec7e267e3e114", "type": "github" }, "original": { @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1664281702, - "narHash": "sha256-haixZ4TJLu1Dciow54wrHrHvlGDVr5sW6MTeAV/ZLuI=", + "lastModified": 1676912548, + "narHash": "sha256-5KH+YpMju3Zj6PU4wV3qaEznRvyt0y7Ei4hS7jsEh2c=", "owner": "nixos", "repo": "nixpkgs", - "rev": "7e52b35fe98481a279d89f9c145f8076d049d2b9", + "rev": "20c135b191fd84f556cc5eb37b8d9d683a580b1e", "type": "github" }, "original": { @@ -44,11 +44,11 @@ }, "utils": { "locked": { - "lastModified": 1656928814, - "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=", + "lastModified": 1676283394, + "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", "owner": "numtide", "repo": "flake-utils", - "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249", + "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 801478d..68e5b93 100644 --- a/flake.nix +++ b/flake.nix @@ -46,6 +46,9 @@ pkg-config pacman openssl + libarchive + libgpg-error + gpgme ]; # For rust-analyzer RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; @@ -53,4 +56,4 @@ formatter = pkgs.alejandra; }); -} \ No newline at end of file +} diff --git a/i18n.toml b/i18n.toml new file mode 100644 index 0000000..05c50ba --- /dev/null +++ b/i18n.toml @@ -0,0 +1,4 @@ +fallback_language = "en" + +[fluent] +assets_dir = "i18n" \ No newline at end of file diff --git a/i18n/en/Malachite.ftl b/i18n/en/Malachite.ftl new file mode 100644 index 0000000..b85a45a --- /dev/null +++ b/i18n/en/Malachite.ftl @@ -0,0 +1,23 @@ +# src/args.rs + +about = A scriptable and declarative Pacman repository management tool + +help-verbose = Set amount of logging output +help-exclude = Exclude given repositories from the operation + +help-init = Initializes empty Malachite repository in current directory + +help-pull = Clone and/or update Git repositories +help-pull-rebuild = Rebuild packages if new commits are found +help-pull-concurrent = Number of concurrent rebuilds + +help-build = Build given package(s) +help-build-packages = Package(s) to build +help-build-concurrent = Number of concurrent builds + +help-clean = Remove Git repositories no longer present in the configuration +help-clean-prune = Keep X latest versions of each package, removing any that are older + +help-info = Display information about the current Malachite repository + +help-generate = Generate Pacman repository from built packages \ No newline at end of file diff --git a/justfile b/justfile new file mode 100755 index 0000000..ba347ea --- /dev/null +++ b/justfile @@ -0,0 +1,14 @@ +#!/usr/bin/env just --justfile + +release: + cargo build --release + +lint: + cargo clippy --all-targets --all-features -- -D warnings + cargo fmt -- --check + +test: + if [ -d example ]; then rm -rf example; fi + mkdir example + + pushd example && cargo run -- init && cargo run -- pull && popd diff --git a/src/args.rs b/src/args.rs index 25e61c7..d0ff800 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,75 +1,75 @@ use clap::{ArgAction, Parser, Subcommand}; +use crate::fl; + +static VERSION: &str = concat!( + env!("CARGO_PKG_VERSION"), + " (", + env!("MALACHITE_CODENAME"), + ")" +); + #[derive(Debug, Clone, Parser)] -#[clap(name = "Malachite", version = env ! ("CARGO_PKG_VERSION"), about = env ! ("CARGO_PKG_DESCRIPTION"))] +#[command(bin_name = "mlc", name = "Malachite", version = VERSION, about = fl!("about"), infer_subcommands = true)] pub struct Args { - #[clap(subcommand)] - pub subcommand: Option, + #[command(subcommand)] + pub subcommand: Operation, - /// Sets the level of verbosity - #[clap(long, short, global(true), action = ArgAction::SetTrue)] - pub verbose: bool, + #[arg(long, short, action = ArgAction::Count, global = true, help = fl!("help-verbose"))] + pub verbose: u8, - /// Excludes packages from given operation, if applicable - #[clap(short = 'x', long = "exclude", action = ArgAction::Append, takes_value = true)] + #[arg(long, short = 'x', action = ArgAction::Append, global = true, help = fl!("help-exclude"))] pub exclude: Vec, } #[derive(Debug, Clone, Subcommand)] pub enum Operation { - /// Builds the given packages - #[clap(name = "build", aliases = & ["b"])] - Build { - /// The packages to operate on - #[clap(name = "package(s)", action = ArgAction::Append, index = 1)] - packages: Vec, + #[command(bin_name = "mlc", name = "init", short_flag = 'I', about = fl!("help-init"))] + Init, - /// Does not regenerate repository after building given package(s) - #[clap(short = 'n', long = "no-regen", action = ArgAction::SetTrue)] - no_regen: bool, + #[command(bin_name = "mlc", name = "pull", short_flag = 'P', about = fl!("help-pull"))] + Pull { + #[arg(long, short, action = ArgAction::SetTrue, help = fl!("help-pull-rebuild"))] + rebuild: bool, + + #[arg(long, short, help = fl!("help-pull-concurrent"))] + concurrent: Option, }, - /// Generates Pacman repository from built packages - #[clap(name = "repo-gen", aliases = & ["repo", "r"])] - RepoGen, + #[command(bin_name = "mlc", name = "build", short_flag = 'B', about = fl!("help-build"))] + Build { + #[arg(required = true, help = fl!("help-build-packages"))] + packages: Vec, - /// Clones all git repositories from mlc.toml branching from current directory - #[clap(name = "clone", aliases = & ["init", "i", "c"])] - Clone, + #[arg(long, short, help = fl!("help-build-concurrent"))] + concurrent: Option, + }, - /// Removes everything in directory except for mlc.toml - #[clap(name = "clean", aliases = & ["clean", "cl", "reset"])] + #[command(bin_name = "mlc", name = "clean", short_flag = 'C', about = fl!("help-clean"))] Clean { - /// Force removes everything, even if git directory is dirty or has unpushed changes or changes at remote - #[clap(short = 'f', long = "force", action = ArgAction::SetTrue)] - force: bool, + #[arg(long, short, help = fl!("help-clean-prune"))] + prune: Option, }, - /// Removes all but the latest 3 versions of each package in a repository - #[clap(name = "prune", aliases = & ["prune", "p"])] - Prune, - - /// Shows an info panel/overview about the current repository - #[clap(name = "info", aliases = & ["status", "s", "i"])] + #[command(bin_name = "mlc", name = "info", short_flag = 'i', about = fl!("help-info"))] Info, - /// Pulls in git repositories from mlc.toml branching from current directory - #[clap(name = "pull", aliases = & ["u"])] - Pull { - /// The packages to operate on - #[clap(name = "package(s)", help = "The packages to operate on", action = ArgAction::Append, index = 1)] - packages: Vec, - - /// Does not regenerate repository after pulling given package(s). This only applies if build_on_update is set to true in repository config - #[clap(short = 'n', long = "no-regen", action = ArgAction::SetTrue)] - no_regen: bool, + #[command(bin_name = "mlc", name = "generate", short_flag = 'G', about = fl!("help-generate"))] + Generate, +} - /// Will prompt for confirmation before rebuilding a package - #[clap(long, action = ArgAction::SetTrue)] - interactive: bool, - }, +#[derive(Default, Clone, Debug)] +pub struct GlobalArgs { + pub verbosity: u8, + pub exclude: Vec, +} - /// Create and/or open local config file - #[clap(name = "config", aliases = & ["conf"])] - Config, +impl GlobalArgs { + pub fn new() -> Self { + let args: Args = Args::parse(); + Self { + verbosity: args.verbose, + exclude: args.exclude, + } + } } diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..d118b3a --- /dev/null +++ b/src/build.rs @@ -0,0 +1,188 @@ +use std::fs; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +use fs_extra::dir::{self, CopyOptions}; +use futures_util::StreamExt; +use futures_util::TryStreamExt; +use glob::glob; +use names::{Generator, Name}; +use podman_api::opts::ContainerCreateOpts; +use podman_api::opts::ImageBuildOpts; +use podman_api::Podman; + +use crate::errors::AppResult; +use crate::utils::{uid, ShellCommand}; + +pub const BUILDFILE: &str = include_str!("../Buildfile"); + +#[derive(Clone, Debug)] +pub enum BuildKind { + Podman { image: String }, + Host, +} + +impl BuildKind { + pub fn image(&self) -> Option<&str> { + match self { + Self::Podman { image } => Some(image), + Self::Host => None, + } + } +} + +pub struct Build { + pub kind: BuildKind, + pub name: String, + pub pkg: String, + pub out: PathBuf, + pub flags: Vec, +} + +impl Build { + pub fn new>( + kind: BuildKind, + pkg: S, + out: P, + flags: Vec, + ) -> AppResult { + let mut generator = Generator::with_naming(Name::Numbered); + + let pod = Self { + kind, + name: generator.next().unwrap(), + pkg: pkg.to_string(), + out: out.into(), + flags, + }; + + Ok(pod) + } + + pub async fn build(&self) -> AppResult<()> { + fs::create_dir_all(&self.out)?; + + match &self.kind { + BuildKind::Podman { .. } => self.podman_build().await?, + BuildKind::Host => self.host_build().await?, + } + + Ok(()) + } + + async fn podman_build(&self) -> AppResult<()> { + let uid = uid(); + + let podman = Podman::new(format!("unix:///var/run/user/{}/podman/podman.sock", uid))?; + let image = self.kind.image().unwrap(); + + let buildfile = fs::read_to_string(".mlc/Buildfile")?; + let buildfile_template = liquid::ParserBuilder::with_stdlib() + .build()? + .parse(&buildfile)?; + + let buildfile_values = liquid::object!({ + "image": image, + "pkg": &self.pkg, + "flags": &self.flags.join(" "), + }); + + let buildfile = buildfile_template.render(&buildfile_values)?; + + let build_path = std::env::temp_dir().join(&self.name); + + fs::create_dir_all(&build_path)?; + fs::write(build_path.join("Buildfile"), buildfile)?; + + dir::copy(&self.pkg, &build_path, &CopyOptions::new())?; + dir::copy(".mlc/store", &build_path, &CopyOptions::new())?; + + let opts = &ImageBuildOpts::builder(build_path.to_string_lossy()) + .dockerfile("Buildfile") + .tag(&self.name) + .build(); + + let mut log_file = File::create(&format!(".mlc/logs/{}-{}.log", &self.pkg, &self.name))?; + + let images = podman.images(); + match images.build(opts) { + Ok(mut build_stream) => { + while let Some(chunk) = build_stream.next().await { + match chunk { + Ok(chunk) => { + println!("{}", chunk.stream); + let _ = log_file.write(chunk.stream.as_bytes())?; + } + Err(e) => eprintln!("{}", e), + } + } + } + Err(e) => eprintln!("{}", e), + }; + + podman + .containers() + .create( + &ContainerCreateOpts::builder() + .image(&self.name) + .name(&self.name) + .build(), + ) + .await?; + + let bytes = podman + .containers() + .get(&self.name) + .copy_from("/out") + .try_concat() + .await?; + + let mut archive = tar::Archive::new(&bytes[..]); + archive.unpack(self.out.parent().unwrap())?; + + podman.images().get(&self.name).remove().await?; + + fs::remove_dir_all(build_path)?; + + Ok(()) + } + + async fn host_build(&self) -> AppResult<()> { + let build_path = std::env::temp_dir().join(&self.name); + + fs::create_dir_all(&build_path)?; + dir::copy(&self.pkg, &build_path, &CopyOptions::new())?; + dir::copy(".mlc/store", &build_path, &CopyOptions::new())?; + + ShellCommand::makepkg() + .args(["-s", "--noconfirm"]) + .args(&self.flags) + .cwd(&build_path.join(&self.pkg)) + .spawn()? + .wait()?; + + let glob = glob( + &build_path + .join(&self.pkg) + .join("*.pkg.tar.*") + .to_string_lossy(), + )?; + for entry in glob { + match entry { + Ok(path) => { + fs_extra::copy_items( + &[path.to_string_lossy().to_string()], + &self.out, + &CopyOptions::new(), + )?; + } + Err(e) => eprintln!("{}", e), + } + } + + fs::remove_dir_all(build_path)?; + + Ok(()) + } +} diff --git a/src/builders.rs b/src/builders.rs new file mode 100644 index 0000000..f538691 --- /dev/null +++ b/src/builders.rs @@ -0,0 +1,182 @@ +use crate::errors::AppResult; +use crate::utils::ShellCommand; +use std::path::PathBuf; + +#[derive(Debug, Clone)] +pub struct GitCloneBuilder { + pub url: String, + pub branch: Option, + pub path: PathBuf, +} + +impl GitCloneBuilder { + pub fn new(url: S, path: P) -> Self + where + S: ToString, + P: Into, + { + Self { + url: url.to_string(), + branch: None, + path: path.into(), + } + } + + pub fn branch(mut self, branch: String) -> Self { + self.branch = Some(branch); + self + } + + pub fn clone(&self) -> AppResult { + let mut args = vec![ + "clone".to_string(), + self.url.clone(), + self.path.to_string_lossy().to_string(), + ]; + + if let Some(branch) = &self.branch { + args.push("--branch".to_string()); + args.push(branch.clone()); + } + + let command = ShellCommand::git().args(args); + + Ok(command) + } +} + +#[derive(Debug, Clone)] +pub struct GitPullBuilder { + pub path: PathBuf, +} + +impl GitPullBuilder { + pub fn new>(path: P) -> Self { + Self { path: path.into() } + } + + pub fn pull(&self) -> AppResult { + let command = ShellCommand::git().cwd(self.path.clone()).args(["pull"]); + + Ok(command) + } +} + +#[derive(Debug, Clone)] +pub struct GitCheckoutBuilder { + pub path: PathBuf, + pub branch: String, +} + +impl GitCheckoutBuilder { + pub fn new(path: P, branch: S) -> Self + where + S: ToString, + P: Into, + { + Self { + path: path.into(), + branch: branch.to_string(), + } + } + + pub fn checkout(&self) -> AppResult { + let command = ShellCommand::git() + .cwd(self.path.clone()) + .arg("checkout") + .arg(self.branch.clone()); + + Ok(command) + } +} + +#[derive(Debug, Clone)] +pub struct GitStatusBuilder { + pub path: PathBuf, +} + +impl GitStatusBuilder { + pub fn new>(path: P) -> Self { + Self { path: path.into() } + } + + pub fn status(&self) -> AppResult { + let command = ShellCommand::git().cwd(self.path.clone()).arg("status"); + + Ok(command) + } +} + +#[derive(Debug, Clone)] +pub struct GitInitBuilder { + pub path: PathBuf, +} + +impl GitInitBuilder { + pub fn new>(path: P) -> Self { + Self { path: path.into() } + } + + pub fn init(&self) -> AppResult { + let command = ShellCommand::git().cwd(self.path.clone()).arg("init"); + + Ok(command) + } +} + +#[derive(Debug, Clone)] +pub struct GitAddBuilder { + pub path: PathBuf, + pub refspecs: Vec, +} + +impl GitAddBuilder { + pub fn new(path: PathBuf) -> Self { + Self { + path, + refspecs: vec![], + } + } + + #[allow(dead_code)] + pub fn refspec(mut self, refspec: S) -> Self { + self.refspecs.push(refspec.to_string()); + self + } + + pub fn refspecs(mut self, refspecs: V) -> Self + where + S: ToString, + V: IntoIterator, + { + self.refspecs + .extend(refspecs.into_iter().map(|s| s.to_string())); + self + } + + pub fn add(&self) -> AppResult { + let command = ShellCommand::git() + .cwd(self.path.clone()) + .arg("add") + .args(self.refspecs.clone()); + + Ok(command) + } +} + +#[derive(Debug, Clone)] +pub struct GitFetchBuilder { + pub path: PathBuf, +} + +impl GitFetchBuilder { + pub fn new>(path: P) -> Self { + Self { path: path.into() } + } + + pub fn fetch(&self) -> AppResult { + let command = ShellCommand::git().cwd(self.path.clone()).arg("fetch"); + + Ok(command) + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..b03914f --- /dev/null +++ b/src/config.rs @@ -0,0 +1,192 @@ +use std::borrow::Cow; +use std::collections::HashMap; +use std::env; +use std::ops::Not; +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +use crate::build::BUILDFILE; +use crate::builders::{GitAddBuilder, GitInitBuilder}; +use crate::errors::ConfigError; +use crate::generate::GENERATEFILE; +use crate::lock::LOCKFILE_VERSION; +use crate::AppError; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Config { + pub base: BaseConfig, + pub repo: RepoConfig, + pub repositories: Repositories, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct BaseConfig { + pub src: PathBuf, + pub podman: bool, + pub image: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct RepoConfig { + pub name: String, + pub out: PathBuf, + pub repo: PathBuf, + pub security: SecurityConfig, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct SecurityConfig { + pub sign: bool, + pub sign_key: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Repositories { + pub names: Vec, + pub keys: HashMap, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(into = "String", try_from = "String")] +pub struct GitRepo { + pub key: String, + pub name: String, + pub rev: Option, +} + +impl From for GitRepo { + fn from(s: String) -> Self { + let (key, repo) = s.split_once(':').unwrap(); + let (name, rev) = repo.split_once('@').unwrap_or((repo, "")); + + Self { + key: key.to_string(), + name: name.to_string(), + rev: rev.is_empty().not().then_some(rev.to_string()), + } + } +} + +impl From for String { + fn from(repo: GitRepo) -> Self { + if let Some(rev) = repo.rev { + format!("{}:{}@{}", repo.key, repo.name, rev) + } else { + format!("{}:{}", repo.key, repo.name) + } + } +} + +impl GitRepo { + pub fn expand(&self, config: &Config) -> Result { + let url = config + .repositories + .keys + .get(&self.key) + .ok_or_else(|| ConfigError::Expand(format!("Unknown key: {}", self.key)))?; + + Ok(url.replace("{}", &self.name)) + } +} + +impl Default for Config { + fn default() -> Self { + let pwd = env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + let name = pwd + .file_name() + .map(|s| s.to_string_lossy()) + .unwrap_or_else(|| Cow::from("mlc".to_string())); + Self { + base: BaseConfig { + src: pwd.clone(), + podman: false, + image: "archlinux:latest".to_string(), + }, + repo: RepoConfig { + name: name.to_string(), + out: pwd.join("out"), + repo: pwd.join("repo"), + security: SecurityConfig { + sign: false, + sign_key: None, + }, + }, + repositories: Repositories { + names: vec![ + GitRepo { + key: "crs".to_string(), + name: "malachite".to_string(), + rev: Some("mlc3-rewrite".to_string()), + }, + GitRepo { + key: "aur".to_string(), + name: "ame".to_string(), + rev: None, + }, + ], + keys: HashMap::from([ + ( + "crs".to_string(), + "https://git.getcryst.al/crystal/software/{}.git".to_string(), + ), + ( + "aur".to_string(), + "https://aur.archlinux.org/{}.git".to_string(), + ), + ]), + }, + } + } +} + +impl Config { + pub fn init() -> Result<(), AppError> { + let pwd = env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + let config = Config::default(); + + if pwd.read_dir()?.count() > 0 { + return Err(AppError::Config(ConfigError::Init( + "Directory is not empty".to_string(), + ))); + } + + std::fs::create_dir(".mlc")?; + std::fs::write( + ".mlc/config.toml", + toml::to_string_pretty(&config) + .map_err(|e| AppError::Config(ConfigError::Serialize(e)))?, + )?; + std::fs::write(".mlc/Buildfile", BUILDFILE)?; + std::fs::write(".mlc/Generatefile", GENERATEFILE)?; + + std::fs::create_dir(".mlc/conf.d")?; + std::fs::create_dir(".mlc/store")?; + std::fs::create_dir(".mlc/logs")?; + + std::fs::write( + ".mlc/mlc.lock", + format!("[lockfile]\nversion = '{}'\n\n[remote]\n", LOCKFILE_VERSION), + )?; + std::fs::write(".mlc/conf.d/.gitkeep", "\n")?; + std::fs::write(".mlc/store/.gitkeep", "\n")?; + std::fs::write(".mlc/logs/.gitkeep", "\n")?; + std::fs::write(".gitignore", "/*/\n!/.mlc")?; + + GitInitBuilder::new(pwd.clone()).init()?.silent()?; + + GitAddBuilder::new(pwd) + .refspecs([".gitignore", ".mlc/**"]) + .add()? + .silent()?; + + Ok(()) + } + + pub fn load() -> Result { + let pwd = env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + let config = toml::from_str(&std::fs::read_to_string(pwd.join(".mlc/config.toml"))?)?; + + Ok(config) + } +} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..cd5feb8 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,93 @@ +use thiserror::Error; + +pub type AppResult = Result; + +#[derive(Error, Debug)] +pub enum AppError { + #[error("Malachite should not be run as root")] + RunAsRoot, + + #[error(transparent)] + Config(#[from] ConfigError), + + #[error(transparent)] + Glob(#[from] glob::PatternError), + + #[error(transparent)] + Git(#[from] GitError), + + #[error(transparent)] + LibArchive(#[from] compress_tools::Error), + + #[error(transparent)] + Gpg(#[from] gpgme::Error), + + #[error(transparent)] + Utf8(#[from] std::string::FromUtf8Error), + + #[error(transparent)] + Podman(#[from] podman_api::Error), + + #[error(transparent)] + Io(#[from] IoError), + + #[error(transparent)] + Liquid(#[from] liquid::Error), +} + +#[derive(Error, Debug)] +pub enum IoError { + #[error("IO Error: {0}")] + Io(#[from] std::io::Error), + + #[error(transparent)] + FsExtra(#[from] fs_extra::error::Error), +} + +#[derive(Error, Debug)] +pub enum GitError { + #[error("Invalid Git repository: {0}")] + InvalidRepository(String), + + #[error("Unable to merge: {0}")] + MergeError(String), +} + +#[derive(Error, Debug)] +pub enum ConfigError { + #[error("Failed to deserialize config: {0}")] + Deserialize(#[from] toml::de::Error), + + #[error("Failed to serialize config: {0}")] + Serialize(#[from] toml::ser::Error), + + #[error("Failed to expand repositories: {0}")] + Expand(String), + + #[error("Failed to initialize config: {0}")] + Init(String), +} + +impl From for AppError { + fn from(e: std::io::Error) -> Self { + AppError::Io(IoError::Io(e)) + } +} + +impl From for AppError { + fn from(e: fs_extra::error::Error) -> Self { + AppError::Io(IoError::FsExtra(e)) + } +} + +impl From for AppError { + fn from(e: toml::de::Error) -> Self { + AppError::Config(ConfigError::Deserialize(e)) + } +} + +impl From for AppError { + fn from(e: toml::ser::Error) -> Self { + AppError::Config(ConfigError::Serialize(e)) + } +} diff --git a/src/generate.rs b/src/generate.rs new file mode 100644 index 0000000..d41be01 --- /dev/null +++ b/src/generate.rs @@ -0,0 +1,211 @@ +use std::env::set_current_dir; +use std::fs; +use std::path::PathBuf; + +use fs_extra::dir; +use futures_util::StreamExt; +use futures_util::TryStreamExt; +use glob::glob; +use gpgme::{Context, Protocol, SignMode}; +use names::Generator; +use podman_api::opts::ContainerCreateOpts; +use podman_api::opts::ImageBuildOpts; +use podman_api::Podman; + +use crate::config::SecurityConfig; +use crate::errors::AppResult; +use crate::utils::{uid, ShellCommand}; + +pub const GENERATEFILE: &str = include_str!("../Generatefile"); + +#[derive(Clone, Debug)] +pub enum GenerateKind { + Podman { image: String }, + Host, +} + +impl GenerateKind { + pub fn image(&self) -> Option<&str> { + match self { + Self::Podman { image } => Some(image), + Self::Host => None, + } + } +} + +pub struct PacmanRepository { + pub kind: GenerateKind, + pub name: String, + pub tempdir: String, + pub out: PathBuf, + pub repo: PathBuf, + pub security: SecurityConfig, +} + +impl PacmanRepository { + pub fn new>( + kind: GenerateKind, + name: S, + out: P, + repo: P, + security: SecurityConfig, + ) -> Self { + let mut generator = Generator::with_naming(names::Name::Numbered); + + Self { + kind, + name: name.to_string(), + tempdir: generator.next().unwrap(), + out: out.into(), + repo: repo.into(), + security, + } + } + + pub async fn generate(&self) -> AppResult<()> { + fs::create_dir_all(&self.repo)?; + + match &self.kind { + GenerateKind::Podman { .. } => self.podman_generate().await?, + GenerateKind::Host => self.host_generate()?, + } + + if self.security.sign { + let packages: Vec = glob(&format!("{}/*.pkg.tar.*", self.repo.display()))? + .filter_map(Result::ok) + .collect(); + + let mut gpg_ctx = Context::from_protocol(Protocol::OpenPgp)?; + gpg_ctx.set_armor(true); + + for package in packages { + let contents = fs::read(&package)?; + let mut buffer = Vec::new(); + + gpg_ctx.sign(SignMode::Detached, &contents, &mut buffer)?; + + let sigfile = format!("{}.sig", package.display()); + fs::write(sigfile, buffer)?; + } + } + + Ok(()) + } + + pub fn host_generate(&self) -> AppResult<()> { + if self.repo.exists() { + fs::remove_dir_all(&self.repo)?; + } + + fs::create_dir_all(&self.repo)?; + + let generate_path = std::env::temp_dir().join(&self.tempdir); + fs::create_dir_all(&generate_path)?; + + let copy_opts = dir::CopyOptions { + content_only: true, + ..Default::default() + }; + + dir::copy(&self.out, &generate_path, ©_opts)?; + + let mlc_dir = std::env::current_dir()?; + set_current_dir(&generate_path)?; + + let files: Vec = glob("*.pkg.tar.*")? + .filter_map(Result::ok) + .map(|p| p.display().to_string()) + .collect(); + + ShellCommand::repo_add() + .arg(format!("{}.db.tar.gz", self.name)) + .args(files) + .spawn()? + .wait()?; + + set_current_dir(mlc_dir)?; + + let copy_opts = dir::CopyOptions { + content_only: true, + ..Default::default() + }; + + dir::copy(&generate_path, &self.repo, ©_opts)?; + + fs::remove_dir_all(&generate_path)?; + + Ok(()) + } + pub async fn podman_generate(&self) -> AppResult<()> { + let uid = uid(); + + let podman = Podman::new(format!("unix:///run/user/{}/podman/podman.sock", uid))?; + let image = self.kind.image().unwrap(); + + let generatefile = fs::read_to_string(".mlc/Generatefile")?; + let generatefile_template = liquid::ParserBuilder::with_stdlib() + .build()? + .parse(&generatefile)?; + + let generatefile_values = liquid::object!({ + "image": image, + "name": &self.name, + "repo": self.repo.file_name().unwrap().to_str().unwrap(), + }); + + let generatefile = generatefile_template.render(&generatefile_values)?; + + let generate_path = std::env::temp_dir().join(&self.tempdir); + fs::create_dir_all(&generate_path)?; + fs::write(generate_path.join("Generatefile"), generatefile)?; + + dir::copy(&self.out, &generate_path, &dir::CopyOptions::new())?; + + let opts = &ImageBuildOpts::builder(generate_path.to_string_lossy()) + .dockerfile("Generatefile") + .tag(&self.tempdir) + .build(); + + let images = podman.images(); + match images.build(opts) { + Ok(mut generate_stream) => { + while let Some(chunk) = generate_stream.next().await { + match chunk { + Ok(chunk) => println!("{:?}", chunk), + Err(e) => eprintln!("{}", e), + } + } + } + Err(e) => eprintln!("{}", e), + }; + + podman + .containers() + .create( + &ContainerCreateOpts::builder() + .image(&self.tempdir) + .name(&self.tempdir) + .build(), + ) + .await?; + + let bytes = podman + .containers() + .get(&self.tempdir) + .copy_from(format!( + "/{}", + self.repo.file_name().unwrap().to_str().unwrap() + )) + .try_concat() + .await?; + + let mut archive = tar::Archive::new(&bytes[..]); + archive.unpack(self.repo.parent().unwrap())?; + + podman.images().get(&self.tempdir).remove().await?; + + fs::remove_dir_all(&generate_path)?; + + Ok(()) + } +} diff --git a/src/git.rs b/src/git.rs new file mode 100644 index 0000000..b9546fc --- /dev/null +++ b/src/git.rs @@ -0,0 +1,93 @@ +use std::path::PathBuf; + +use crate::builders::{GitCheckoutBuilder, GitCloneBuilder, GitPullBuilder}; +use crate::errors::AppResult; +use crate::utils::ShellCommand; + +pub struct GitRepository { + pub path: PathBuf, +} + +impl GitRepository { + pub fn new(path: PathBuf) -> Self { + Self { path } + } + + pub fn clone_repo(path: PathBuf, url: &str, branch: Option) -> AppResult<()> { + let mut builder = GitCloneBuilder::new(url, path); + + if let Some(branch) = branch { + builder = builder.branch(branch); + } + + builder.clone()?.silent()?; + + Ok(()) + } + + pub fn pull(&self) -> AppResult<()> { + let builder = GitPullBuilder::new(self.path.clone()); + + builder.pull()?.silent()?; + + Ok(()) + } + + pub fn update_origin(&self, url: &str) -> AppResult<()> { + ShellCommand::git() + .cwd(self.path.clone()) + .args(["remote", "set-url", "origin"]) + .arg(url) + .silent()?; + + Ok(()) + } + + pub fn hash(&self) -> AppResult { + let output = ShellCommand::git() + .cwd(self.path.clone()) + .args(["rev-parse", "--short", "HEAD"]) + .output()?; + + Ok(String::from_utf8(output.stdout)?.trim().to_string()) + } + + pub fn origin(&self) -> AppResult { + let output = ShellCommand::git() + .cwd(self.path.clone()) + .args(["remote", "get-url", "origin"]) + .output()?; + + Ok(String::from_utf8(output.stdout)?.trim().to_string()) + } + + pub fn current_branch(&self) -> AppResult { + let output = ShellCommand::git() + .cwd(self.path.clone()) + .args(["rev-parse", "--abbrev-ref", "HEAD"]) + .output()?; + + Ok(String::from_utf8(output.stdout)?.trim().to_string()) + } + + pub fn default_branch(&self) -> AppResult { + let output = ShellCommand::git() + .cwd(self.path.clone()) + .args(["symbolic-ref", "refs/remotes/origin/HEAD", "--short"]) + .output()?; + + let stdout = String::from_utf8(output.stdout)?.trim().to_string(); + + let (_, branch) = stdout.split_once('/').unwrap(); + + Ok(branch.to_string()) + } + + pub fn checkout(&self, branch: &str) -> AppResult<()> { + GitCheckoutBuilder::new(self.path.clone(), branch) + .checkout()? + .silent()?; + + Ok(()) + } +} diff --git a/src/i18n.rs b/src/i18n.rs new file mode 100644 index 0000000..8f7a372 --- /dev/null +++ b/src/i18n.rs @@ -0,0 +1,32 @@ +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + DesktopLanguageRequester, +}; +use lazy_static::lazy_static; +use rust_embed::RustEmbed; + +#[derive(RustEmbed)] +#[folder = "i18n"] +struct Localizations; + +fn read() -> FluentLanguageLoader { + let loader: FluentLanguageLoader = fluent_language_loader!(); + let req_langs = DesktopLanguageRequester::requested_languages(); + i18n_embed::select(&loader, &Localizations, &req_langs).unwrap(); + loader +} + +lazy_static! { + pub static ref LANG_LOADER: FluentLanguageLoader = read(); +} + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANG_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),*) => {{ + i18n_embed_fl::fl!($crate::i18n::LANG_LOADER, $message_id, $($args), *) + }}; +} diff --git a/src/info.rs b/src/info.rs new file mode 100644 index 0000000..2d51c91 --- /dev/null +++ b/src/info.rs @@ -0,0 +1,137 @@ +use std::fmt::{Display, Formatter}; +use std::path::PathBuf; + +use crate::builders::{GitFetchBuilder, GitStatusBuilder}; +use tabled::Tabled; + +use crate::errors::AppResult; +use crate::git::GitRepository; + +pub const BOLD: &str = "\x1b[1m"; +pub const CLEAN: &str = "\x1b[34m"; +pub const DIRTY: &str = "\x1b[33m"; +pub const UNKNOWN: &str = "\x1b[37m"; +pub const RESET: &str = "\x1b[0m"; + +#[derive(Debug, Clone, Tabled)] +pub enum GitStatus { + Clean, + Dirty, + Unknown, +} + +impl GitStatus { + fn color(&self) -> String { + match self { + Self::Clean => format!("{}{}", BOLD, CLEAN), + Self::Dirty => format!("{}{}", BOLD, DIRTY), + Self::Unknown => format!("{}{}", BOLD, UNKNOWN), + } + } +} + +impl Display for GitStatus { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Clean => write!(f, "✔"), + Self::Dirty => write!(f, "✘"), + Self::Unknown => write!(f, "?"), + } + } +} + +#[derive(Debug, Clone)] +pub struct RichInfo { + output: String, + pub push: GitStatus, + pub pull: GitStatus, + pub dirty: GitStatus, +} + +impl Display for RichInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}↑ {}↓ {}*{}", + self.push.color(), + self.pull.color(), + self.dirty.color(), + RESET + ) + } +} + +#[derive(Debug, Clone, Tabled)] +pub struct GitInfo { + #[tabled(skip)] + pub path: PathBuf, + #[tabled(rename = "Repository")] + pub name: String, + #[tabled(rename = "Rich Info")] + pub rich: RichInfo, + #[tabled(rename = "Commit")] + pub commit: String, +} + +impl GitInfo { + pub fn new(path: PathBuf) -> AppResult { + let output = GitStatusBuilder::new(path.clone()).status()?.output()?; + let output = String::from_utf8(output.stdout)?; + + GitFetchBuilder::new(path.clone()) + .fetch()? + .arg("--all") + .silent()?; + + let mut repo = Self { + path: path.clone(), + name: path.file_name().unwrap().to_string_lossy().to_string(), + rich: RichInfo { + output, + push: GitStatus::Unknown, + pull: GitStatus::Unknown, + dirty: GitStatus::Unknown, + }, + commit: GitRepository::new(path).hash()?, + }; + + repo.push()?; + repo.pull()?; + repo.dirty()?; + + Ok(repo) + } + + fn push(&mut self) -> AppResult<()> { + self.rich.push = if self.rich.output.contains("ahead") { + GitStatus::Dirty + } else { + GitStatus::Clean + }; + + Ok(()) + } + + fn pull(&mut self) -> AppResult<()> { + self.rich.pull = if self.rich.output.contains("behind") { + GitStatus::Dirty + } else { + GitStatus::Clean + }; + + Ok(()) + } + + fn dirty(&mut self) -> AppResult<()> { + self.rich.dirty = if self.rich.output.contains("Untracked files") + || self.rich.output.contains("Changes not staged for commit") + || self.rich.output.contains("Changes to be committed") + { + GitStatus::Dirty + } else { + GitStatus::Clean + }; + + Ok(()) + } +} diff --git a/src/internal/exit_codes.rs b/src/internal/exit_codes.rs deleted file mode 100644 index b850e1d..0000000 --- a/src/internal/exit_codes.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub enum AppExitCode { - #[cfg(target_os = "linux")] - RunAsRoot = 1, - PkgsNotFound = 2, - DirNotEmpty = 3, - ConfigParseError = 4, - RepoParseError = 5, - RepoNotClean = 6, -} diff --git a/src/internal/mod.rs b/src/internal/mod.rs deleted file mode 100755 index 799267c..0000000 --- a/src/internal/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub use exit_codes::*; -pub use read::*; - -mod exit_codes; -mod read; -pub mod strings; -pub mod structs; diff --git a/src/internal/read.rs b/src/internal/read.rs deleted file mode 100644 index e960060..0000000 --- a/src/internal/read.rs +++ /dev/null @@ -1,119 +0,0 @@ -use std::fs; -use std::path::Path; - -use crate::internal::structs::{Config, Repo, SplitRepo, UnexpandedConfig}; -use crate::internal::AppExitCode; -use crate::{crash, log}; - -pub fn parse_cfg(verbose: bool) -> Config { - // Crash if mlc.toml doesn't exist - if !Path::exists("mlc.toml".as_ref()) { - crash!( - AppExitCode::ConfigParseError, - "Config file not found (mlc.toml)" - ); - } - - // Reading the config file to an UnexpandedConfig struct - let file = fs::read_to_string("mlc.toml").unwrap(); - let config: UnexpandedConfig = toml::from_str(&file).unwrap_or_else(|e| { - crash!( - AppExitCode::ConfigParseError, - "Error parsing config file: {}", - e - ); - // This is unreachable, but rustc complains about it otherwise - std::process::exit(1); - }); - - log!(verbose, "Config file read: {:?}", config); - - // Crash if incorrect mode is set - if config.base.mode != "workspace" && config.base.mode != "repository" { - crash!( - AppExitCode::ConfigParseError, - "Invalid mode in mlc.toml, must be either \"repository\" or \"workspace\"" - ); - } - - let mut expanded_repos: Vec = vec![]; - - // Parsing repos from the config file - for x in config.repositories.repos { - log!(verbose, "Parsing repo: {:?}", x); - // Splits the repo name and index into a SplitRepo struct - let split: Vec<&str> = x.split(':').collect(); - let split_struct = if split.len() > 2 { - SplitRepo { - id: split[0].parse().unwrap(), - name: split[1].parse().unwrap(), - extra: Some(split[2].parse().unwrap()), - } - } else { - SplitRepo { - id: split[0].parse().unwrap(), - name: split[1].parse().unwrap(), - extra: None, - } - }; - log!(verbose, "Split repo: {:?}", split_struct); - - // Parses all necessary values for expanding the repo to a Repo struct - let id = split_struct.id; - - // If a branch is defined, parse it - let branch = if split_struct.name.contains('/') { - log!(verbose, "Branch defined: {}", split_struct.name); - Some( - split_struct.name.split('/').collect::>()[1] - .to_string() - .replace('!', ""), - ) - } else { - log!(verbose, "No branch defined"); - None - }; - - // Strip branch and priority info from the name, if present - let name = if split_struct.name.contains('/') { - split_struct.name.split('/').collect::>()[0].to_string() - } else { - split_struct.name.to_string().replace('!', "") - }; - - // Substitutes the name into the url - let urls = &config.repositories.urls; - let mut urls_vec = vec![]; - for (i, url) in urls { - if i == &id { - log!(verbose, "Substituting url: {:?}", url); - urls_vec.push(url); - } - } - let url = urls_vec[0].replace("{}", &name); - - // Counts instances of ! in the name, and totals a priority accordingly - let priority = &split_struct.name.matches('!').count(); - - // Creates and pushes Repo struct to expanded_repos - let repo = Repo { - name, - url, - branch, - extra: split_struct.extra, - priority: *priority, - }; - log!(verbose, "Expanded repo: {:?}", repo); - expanded_repos.push(repo); - } - - // Returns parsed config file - let conf = Config { - base: config.base, - mode: config.mode, - repositories: expanded_repos, - }; - log!(verbose, "Config: {:?}", conf); - - conf -} diff --git a/src/internal/strings.rs b/src/internal/strings.rs deleted file mode 100755 index 390679f..0000000 --- a/src/internal/strings.rs +++ /dev/null @@ -1,75 +0,0 @@ -use colored::Colorize; -use std::process::exit; -use std::time::UNIX_EPOCH; - -use crate::internal::AppExitCode; - -const LOGO_SYMBOL: &str = "μ"; -const ERR_SYMBOL: &str = "❌"; - -#[macro_export] -macro_rules! info { - ($($arg:tt)+) => { - $crate::internal::strings::info_fn(&format!($($arg)+)); - } -} - -#[macro_export] -macro_rules! log { - ($verbose:expr, $($arg:tt)+) => { - $crate::internal::strings::log_fn(&format!("[{}:{}] {}", file!(), line!(), format!($($arg)+)), $verbose); - } -} - -#[macro_export] -macro_rules! crash { - ($exit_code:expr, $($arg:tt)+) => { - $crate::internal::strings::crash_fn(&format!("[{}:{}] {}", file!(), line!(), format!($($arg)+)), $exit_code) - } -} - -#[macro_export] -macro_rules! prompt { - (default $default:expr, $($arg:tt)+) => { - $crate::internal::strings::prompt_fn(&format!($($arg)+), $default) - }; -} - -pub fn info_fn(msg: &str) { - println!("{} {}", LOGO_SYMBOL.green(), msg.bold()); -} - -pub fn log_fn(msg: &str, verbose: bool) { - if verbose { - eprintln!( - "{} {}", - std::time::SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(), - msg - ); - } -} - -pub fn crash_fn(msg: &str, exit_code: AppExitCode) { - println!("{} {}", ERR_SYMBOL.red(), msg.bold()); - exit(exit_code as i32); -} - -pub fn prompt_fn(msg: &str, default: bool) -> bool { - let yn = if default { "[Y/n]" } else { "[y/N]" }; - print!("{} {} {}", "?".bold().green(), msg.bold(), yn); - let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - - let input = input.trim().to_lowercase(); - - if input == "y" || input == "yes" { - true - } else if input == "n" || input == "no" { - false - } else { - default - } -} diff --git a/src/internal/structs.rs b/src/internal/structs.rs deleted file mode 100755 index c023b12..0000000 --- a/src/internal/structs.rs +++ /dev/null @@ -1,87 +0,0 @@ -use serde_derive::Deserialize; -use std::collections::HashMap; - -//// Config structs -#[derive(Debug, Deserialize)] -pub struct Config { - pub base: ConfigBase, - pub mode: ConfigMode, - pub repositories: Vec, -} - -#[derive(Debug, Deserialize)] -pub struct UnexpandedConfig { - pub base: ConfigBase, - pub mode: ConfigMode, - pub repositories: ConfigRepositories, -} - -#[derive(Debug, Deserialize)] -pub struct ConfigBase { - pub mode: String, - pub smart_pull: bool, -} - -#[derive(Debug, Deserialize)] -pub struct ConfigMode { - pub repository: Option, - pub workspace: Option, -} - -#[derive(Debug, Deserialize)] -pub struct ConfigModeRepository { - pub name: String, - pub build_on_update: bool, - pub signing: ConfigModeRepositorySigning, -} - -#[derive(Debug, Deserialize)] -pub struct ConfigModeRepositorySigning { - pub enabled: bool, - pub key: Option, - pub on_gen: Option, -} - -#[derive(Debug, Deserialize)] -pub struct ConfigModeWorkspace { - pub git_info: bool, - pub colorblind: bool, - /* pub backup: bool, - pub backup_dir: Option, TODO: Implement backup - */ -} - -#[derive(Debug, Deserialize)] -pub struct ConfigRepositories { - pub repos: Vec, - pub urls: HashMap, -} - -#[derive(Debug, Deserialize)] -pub struct ConfigRepositoriesExpanded { - pub repos: Vec, -} - -//// Repository structs -#[derive(Debug, Deserialize)] -pub struct Repo { - pub name: String, - pub url: String, - pub branch: Option, - pub extra: Option, - pub priority: usize, -} - -#[derive(Debug)] -pub struct SplitRepo { - pub id: String, - pub name: String, - pub extra: Option, -} - -//// Build operation structs -#[derive(Debug)] -pub struct ErroredPackage { - pub name: String, - pub code: i32, -} diff --git a/src/lock.rs b/src/lock.rs new file mode 100644 index 0000000..398da7a --- /dev/null +++ b/src/lock.rs @@ -0,0 +1,86 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::config::Config; +use crate::errors::{AppError, ConfigError}; +use crate::git::GitRepository; + +pub const LOCKFILE_VERSION: &str = "1.0"; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Lockfile { + pub lockfile: Meta, + pub remote: HashMap, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Meta { + pub version: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Remote { + pub origin: String, + pub commit: String, + pub branch: String, +} + +impl Lockfile { + pub fn new(config: &Config) -> Result { + let path = config.base.src.join(".mlc").join("mlc.lock"); + + let lockfile = match std::fs::read_to_string(path) { + Ok(lockfile) => toml::from_str(&lockfile)?, + Err(_) => Self { + lockfile: Meta { + version: LOCKFILE_VERSION.to_string(), + }, + remote: HashMap::new(), + }, + }; + + Ok(lockfile) + } + + pub fn update(&mut self, config: &Config) -> Result<&mut Self, AppError> { + for repo in &config.repositories.names { + let git = GitRepository::new(config.base.src.join(&repo.name)); + let config_url = repo.expand(config)?; + let remote_url = git.origin()?; + + if config_url != remote_url { + git.update_origin(&config_url)?; + git.pull()?; + } + + if let Some(branch) = &repo.rev { + if branch != &git.current_branch()? { + git.checkout(branch)?; + } + } + + if repo.rev.is_none() && git.current_branch()? != git.default_branch()? { + git.checkout(git.default_branch()?.as_str())?; + } + + let remote = Remote { + origin: config_url, + commit: git.hash()?, + branch: git.current_branch()?, + }; + + self.remote.insert(repo.name.clone(), remote); + } + + Ok(self) + } + + pub fn save(&self) -> Result<(), AppError> { + let lockfile = toml::to_string_pretty(&self) + .map_err(|e| AppError::Config(ConfigError::Serialize(e)))?; + std::fs::write(".mlc/mlc.lock", lockfile)?; + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs old mode 100755 new mode 100644 index ea6879c..21c643b --- a/src/main.rs +++ b/src/main.rs @@ -1,88 +1,237 @@ -#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] -#![allow(clippy::too_many_lines)] - use clap::Parser; +use futures_util::future; +use lazy_static::lazy_static; +use std::sync::Arc; +use tokio::sync::Semaphore; -use crate::args::{Args, Operation}; -use crate::internal::parse_cfg; -use crate::internal::AppExitCode; +use args::Args; +use args::GlobalArgs; +use args::Operation; +use errors::AppError; +use errors::AppResult; +use git::GitRepository; +use utils::uid; -#[cfg(target_os = "linux")] -#[global_allocator] -static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; +use crate::build::{Build, BuildKind}; +use crate::config::Config; +use crate::generate::GenerateKind; +use crate::info::GitInfo; +use crate::info::{BOLD, CLEAN, DIRTY, RESET, UNKNOWN}; +use crate::lock::Lockfile; +use crate::prune::PackageFiles; mod args; -mod internal; -mod operations; -mod repository; +mod build; +mod builders; +mod config; +mod errors; +mod generate; +mod git; +mod i18n; +mod info; +mod lock; +mod prune; +mod utils; + +lazy_static!( + #[derive(Clone, Debug)] + pub static ref GLOBAL_ARGS: GlobalArgs = GlobalArgs::new(); +); -fn repository(verbose: bool) -> bool { - // Parse config - let config = parse_cfg(verbose); - log!(verbose, "Config: {:?}", config); +#[tokio::main] +pub async fn main() -> AppResult<()> { + color_eyre::install().unwrap(); + if uid() == 0 { + return Err(AppError::RunAsRoot); + } - // Get repository mode status - let repository = config.base.mode == "repository"; - log!(verbose, "Repository Mode: {:?}", repository); + let args: Args = Args::parse(); - // Return repository mode status - repository + match args.subcommand { + Operation::Build { + packages, + concurrent, + } => cmd_build(packages, concurrent).await?, + Operation::Generate => cmd_generate().await?, + Operation::Init => cmd_init()?, + Operation::Pull { + rebuild, + concurrent, + } => cmd_pull(rebuild, concurrent).await?, + Operation::Clean { prune } => cmd_clean(prune).await?, + Operation::Info => cmd_info()?, + } + + Ok(()) +} + +fn cmd_init() -> AppResult<()> { + Config::init()?; + Ok(()) } -fn main() { - #[cfg(target_os = "linux")] - if unsafe { libc::geteuid() } == 0 { - crash!(AppExitCode::RunAsRoot, "Running malachite as root is disallowed as it can lead to system breakage. Instead, malachite will prompt you when it needs superuser permissions"); +async fn cmd_pull(rebuild: bool, concurrent: Option) -> AppResult<()> { + let config = Config::load()?; + + for repository in &config.repositories.names { + let expanded = repository.expand(&config)?; + let path = config.base.src.join(&repository.name); + + if path.exists() { + GitRepository::new(path).pull()?; + } else { + GitRepository::clone_repo(path, &expanded, repository.rev.clone())?; + } } - // Get required variables - let args: Args = Args::parse(); - let exclude = &args.exclude; - let verbose = args.verbose; - log!(verbose, "Args: {:?}", args); - log!(verbose, "Exclude: {:?}", exclude); - log!(verbose, "Verbose: You guess. :)"); - - // Arg matching - match args.subcommand.unwrap_or(Operation::Clone) { - Operation::Clone => operations::clone(verbose), - Operation::Build { - packages, no_regen, .. - } => { - if !repository(verbose) { - crash!( - AppExitCode::ConfigParseError, - "Cannot build packages in workspace mode" - ); + let mut lock = Lockfile::new(&config)?; + let old_lock = lock.clone(); + lock.update(&config)?; + + if rebuild { + let mut packages_to_rebuild = vec![]; + + for (name, remote) in &old_lock.remote { + if let Some(new_remote) = lock.remote.get(name) { + if remote.commit != new_remote.commit { + packages_to_rebuild.push(name.clone()); + } } - operations::build(&packages, exclude.clone(), no_regen, verbose); } - Operation::Pull { - packages, - no_regen, - interactive, - .. - } => operations::pull(packages, exclude, verbose, no_regen, interactive), - Operation::RepoGen => { - if !repository(verbose) { - crash!( - AppExitCode::ConfigParseError, - "Cannot generate repository in workspace mode" - ); - } - repository::generate(verbose); + + if !packages_to_rebuild.is_empty() { + cmd_build(packages_to_rebuild, concurrent).await?; } - Operation::Config => operations::config(verbose), - Operation::Prune => { - if !repository(verbose) { - crash!( - AppExitCode::ConfigParseError, - "Cannot prune packages in workspace mode" - ); + } + + lock.save()?; + + Ok(()) +} + +async fn cmd_clean(prune: Option) -> AppResult<()> { + let config = Config::load()?; + + let excluded = [ + &config.repo.repo, + &config.repo.out, + &config.base.src.join(".mlc"), + &config.base.src.join(".git"), + ]; + + let mut to_delete = Vec::new(); + for entry in std::fs::read_dir(&config.base.src)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + let name = path.file_name().map(|s| s.to_string_lossy()).unwrap(); + if !config.repositories.names.iter().any(|x| x.name == name) + && !excluded.contains(&&path) + { + to_delete.push(path); } - operations::prune(verbose); } - Operation::Clean { force, .. } => operations::clean(verbose, force), - Operation::Info => operations::info(verbose), } + + for path in to_delete { + std::fs::remove_dir_all(path)?; + } + + if let Some(prune) = prune { + let mut packages = PackageFiles::new(); + packages.scan(&config.repo.out)?; + let pruned = packages.prune(prune)?; + if pruned > 0 { + cmd_generate().await?; + } + } + + Ok(()) +} + +async fn cmd_build(packages: Vec, concurrent: Option) -> AppResult<()> { + let config = Config::load()?; + + let kind = match config.base.podman { + true => BuildKind::Podman { + image: config.base.image, + }, + false => BuildKind::Host, + }; + + let concurrent = if let Some(concurrent) = concurrent { + concurrent + } else { + 1 + }; + + let out = config.repo.out; + + let sem = Arc::new(Semaphore::new(concurrent as usize)); + let results = future::join_all(packages.into_iter().map(|name| async { + let sem = sem.clone(); + let out = out.clone(); + let kind = kind.clone(); + + let perm = sem.acquire().await.unwrap(); + let build = Build::new(kind, name, out, vec![])?; + build.build().await?; + drop(perm); + + Ok::<_, AppError>(()) + })) + .await; + + for result in results { + result?; + } + + Ok(()) +} + +async fn cmd_generate() -> AppResult<()> { + let config = Config::load()?; + + let kind = match config.base.podman { + true => GenerateKind::Podman { + image: config.base.image, + }, + false => GenerateKind::Host, + }; + + generate::PacmanRepository::new( + kind, + config.repo.name, + config.repo.out, + config.repo.repo, + config.repo.security, + ) + .generate() + .await?; + + Ok(()) +} + +fn cmd_info() -> AppResult<()> { + let config = Config::load()?; + let repositories = config.repositories.names; + + let mut overview = Vec::new(); + + for repo in repositories { + let path = config.base.src.join(&repo.name); + + overview.push(GitInfo::new(path)?); + } + + let mut table = tabled::Table::new(overview); + table.with(tabled::Style::rounded()); + + println!("{}", table); + println!( + " {}Key:{} {}Clean {}Unknown {}Dirty{}", + BOLD, RESET, CLEAN, UNKNOWN, DIRTY, RESET + ); + + Ok(()) } diff --git a/src/operations/build.rs b/src/operations/build.rs deleted file mode 100644 index 8a26888..0000000 --- a/src/operations/build.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::internal::structs::{ErroredPackage, Repo}; -use crate::internal::AppExitCode; -use crate::{crash, info, log, repository}; - -pub fn build(packages: &[String], exclude: Vec, no_regen: bool, verbose: bool) { - // Read config struct from mlc.toml - let config = crate::internal::parse_cfg(verbose); - log!(verbose, "Config: {:?}", config); - // Check if any packages were passed, if not, imply all - let all = packages.is_empty(); - log!(verbose, "All: {:?}", all); - - // Read signing - let signing = config.mode.repository.as_ref().unwrap().signing.enabled; - - // Read on_gen - let on_gen = config.mode.repository.as_ref().unwrap().signing.on_gen; - - // Parse whether to sign on build or not - let sign = if signing && on_gen.is_some() && on_gen.unwrap() { - false - } else { - signing - }; - log!(verbose, "Signing: {:?}", sign); - - // Get list of repos and subtract exclude - let mut repos: Vec = config.repositories; - log!(verbose, "{} Repos: {:?}", repos.len(), repos); - if !exclude.is_empty() { - log!(verbose, "Exclude not empty: {:?}", exclude); - for ex in exclude { - repos.retain(|x| *x.name != ex); - } - } - - log!( - verbose, - "Exclusions parsed. Now {} Repos: {:?}", - repos.len(), - repos - ); - - // If packages is not empty and all isn't specified, build specified packages - let mut errored: Vec = vec![]; - if !packages.is_empty() && !all { - log!(verbose, "Packages not empty: {:?}", packages); - for pkg in packages.iter() { - // If repo is not in config, crash, otherwise, build - if repos.iter().map(|x| x.name.clone()).any(|x| x == *pkg) { - // Otherwise, build - log!(verbose, "Building {}", pkg); - - let code = repository::build(pkg, sign, verbose); - log!( - verbose, - "Package {} finished with exit code: {:?}", - pkg, - code - ); - - if code != 0 { - let error = ErroredPackage { - name: pkg.to_string(), - code, - }; - errored.push(error); - } - } else { - crash!( - AppExitCode::PkgsNotFound, - "Package repo {} not found in in mlc.toml", - pkg - ); - } - } - } - - // If all is specified, attempt to build a package from all repos - if all { - log!(verbose, "Proceeding to build all"); - - // Sort by package priority - log!(verbose, "Sorting by priority: {:?}", repos); - repos.sort_by(|a, b| b.priority.cmp(&a.priority)); - log!(verbose, "Sorted: {:?}", repos); - for pkg in repos { - log!(verbose, "Building {}", pkg.name); - - let code = repository::build(&pkg.name, sign, verbose); - log!( - verbose, - "Package {} finished with exit code: {:?}", - pkg.name, - code - ); - - if code != 0 { - let error = ErroredPackage { - name: pkg.name, - code, - }; - errored.push(error); - } - } - } - - // If all is not specified, but packages is empty, crash - if !all && packages.is_empty() { - log!(verbose, "Packages empty. Crashing"); - crash!(AppExitCode::PkgsNotFound, "No packages specified"); - } - - // If no_regen is passed, do not generate a repository - if !no_regen { - log!(verbose, "Generating repository"); - repository::generate(verbose); - } - - // Map errored packages to a string for display - let error_strings: Vec = errored - .iter() - .map(|x| format!("{}: Returned {}", x.name, x.code)) - .collect(); - - // If errored is not empty, let the user know which packages failed - if !errored.is_empty() { - log!(verbose, "Errored packages: \n{:?}", error_strings); - info!( - "The following packages build jobs returned a non-zero exit code: \n {}", - error_strings.join("\n ") - ); - info!("Please check `man 8 makepkg` for more information"); - // Check if code 63 appeared at all - if errored.iter().any(|x| x.code == 63) { - log!(verbose, "Code 63 found"); - info!("Note: Code 63 is an internal Malachite exit code, and specifies that no PKGBUILD was found."); - } - } -} diff --git a/src/operations/clean.rs b/src/operations/clean.rs deleted file mode 100644 index a6c6041..0000000 --- a/src/operations/clean.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::{crash, info, internal::AppExitCode, log}; - -pub fn clean(verbose: bool, force: bool) { - info!("Resetting mlc repo, deleting all directories"); - // Get a vec of all files/dirs in the current directory - let dir_paths = std::fs::read_dir(".").unwrap(); - log!(verbose, "Paths: {:?}", dir_paths); - let mut dirs = dir_paths - .map(|x| x.unwrap().path().display().to_string()) - .collect::>(); - - // Remove mlc.toml and .git from output - dirs.retain(|x| *x != "./mlc.toml" && *x != ".\\mlc.toml"); - dirs.retain(|x| *x != "./.git" && *x != ".\\.git"); - dirs.retain(|x| *x != "./.gitignore" && *x != ".\\.gitignore"); - dirs.retain(|x| *x != "./.gitmodules" && *x != ".\\.gitmodules"); - dirs.retain(|x| *x != "./README.md" && *x != ".\\README.md"); - - let mut unclean_dirs = vec![]; - - // Enter each directory and check git status - for dir in &dirs { - let root_dir = std::env::current_dir().unwrap(); - - log!(verbose, "Entering directory: {}", dir); - std::env::set_current_dir(dir).unwrap(); - - let status = std::process::Command::new("git") - .arg("status") - .output() - .unwrap(); - - let output = std::string::String::from_utf8(status.stdout).unwrap(); - log!(verbose, "Git status: {}", output); - - if output.contains("Your branch is up to date with") - && !output.contains("Untracked files") - && !output.contains("Changes not staged for commit") - { - log!(verbose, "Directory {} is clean", dir); - } else { - unclean_dirs.push(dir); - } - - std::env::set_current_dir(&root_dir).unwrap(); - log!(verbose, "Current directory: {}", root_dir.display()); - } - - if !unclean_dirs.is_empty() && !force && crate::parse_cfg(verbose).base.mode == "workspace" { - crash!( - AppExitCode::RepoNotClean, - "The following directories are not clean: \n {}\n\ - If you are sure no important changes are staged, run `mlc clean` with the `--force` flag to delete them.", - unclean_dirs.iter().map(|x| (*x).to_string().replace("./", "").replace(".\\", "")).collect::>().join(", ") - ); - } - - log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs); - for dir in &dirs { - log!(verbose, "Deleting directory: {}", dir); - rm_rf::remove(dir).unwrap(); - } - info!( - "Reset complete, dirs removed: \n \ - {}", - dirs.iter() - .map(|x| x.replace("./", "").replace(".\\", "")) - .collect::>() - .join("\n ") - ); -} diff --git a/src/operations/clone.rs b/src/operations/clone.rs deleted file mode 100644 index f7ab993..0000000 --- a/src/operations/clone.rs +++ /dev/null @@ -1,151 +0,0 @@ -use std::env; -use std::process::Command; - -use crate::{info, log}; - -pub fn clone(verbose: bool) { - // Read config struct from mlc.toml - let config = crate::internal::parse_cfg(verbose); - log!(verbose, "Config: {:?}", config); - // Parse repositories from config - let repos = &config.repositories; - log!(verbose, "Repos: {:?}", repos); - - // Get a vector of all files/dirs in the current directory, excluding config file - let dir_paths = std::fs::read_dir(".").unwrap(); - let mut dirs = dir_paths - .map(|x| x.unwrap().path().display().to_string()) - .collect::>(); - - // Remove mlc.toml and .git from output - dirs.retain(|x| *x != "./mlc.toml" && *x != ".\\mlc.toml"); - dirs.retain(|x| *x != "./.git" && *x != ".\\.git"); - - // If mode is repository, also exclude repository mode directories - if config.mode.repository.is_some() { - dirs.retain(|x| { - *x != format!("./{}", config.mode.repository.as_ref().unwrap().name) - && *x != format!(".\\{}", config.mode.repository.as_ref().unwrap().name) - }); - dirs.retain(|x| *x != "./out" && *x != ".\\out"); - } - log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs); - - // Creates a vector of the difference between cloned repos and repos defined in config - let mut repo_diff = vec![]; - for repo in repos { - let name = &repo.name; - - if !dirs.contains(&format!("./{}", name)) && !dirs.contains(&format!(".\\{}", name)) { - repo_diff.push(repo); - } - } - - // Diff logic - if repo_diff.is_empty() { - // No diff, do nothing - log!(verbose, "No diff"); - info!("All repos are already cloned"); - } else { - log!(verbose, "Diff: {:?}", repo_diff); - // This is just for pretty display purposes - let display = repo_diff - .iter() - .map(|x| x.name.to_string()) - .collect::>() - .join(" "); - info!("New/missing repos to clone: {}", display); - - // Clone all diff repos - for r in repo_diff { - log!(verbose, "Depth: {:?}", r.extra); - log!(verbose, "Cloning {}", r.name); - - if r.extra.is_some() && config.base.mode == "workspace" { - info!( - "Cloning ({} mode): {} with `--depth {}`", - config.base.mode, - r.name, - r.extra.as_ref().unwrap() - ); - } else if r.extra.is_some() && config.base.mode == "repository" { - info!( - "Cloning ({} mode): {} at {}", - config.base.mode, - r.name, - r.extra.as_ref().unwrap() - ); - } else { - info!("Cloning ({} mode): {}", config.base.mode, r.name); - } - - if r.extra.is_some() && config.base.mode == "workspace" { - // Clone with specified extra depth - Command::new("git") - .args(&["clone", &r.url, &r.name]) - // If a branch is specified, clone that specific branch - .args(if r.branch.is_some() { - vec!["-b", r.branch.as_ref().unwrap()] - } else { - vec![] - }) - .args(if r.extra.is_some() { - vec!["--depth", r.extra.as_ref().unwrap()] - } else { - vec![] - }) - .spawn() - .unwrap() - .wait() - .unwrap(); - } else if config.base.mode == "repository" { - // Clone and checkout specified hash - // Create an empty directory with repo.name and enter it - let root_dir = env::current_dir().unwrap(); - - // Git clone the repo - Command::new("git") - .args(&["clone", &r.url, &r.name]) - .args(if r.branch.is_some() { - vec!["-b", r.branch.as_ref().unwrap()] - } else { - vec![] - }) - .spawn() - .unwrap() - .wait() - .unwrap(); - - std::env::set_current_dir(&r.name).unwrap(); - log!(verbose, "Entered directory: {}", r.name); - - // Git checkout the PKGBUILD from the hash - if r.extra.is_some() { - Command::new("git") - .args(&["checkout", r.extra.as_ref().unwrap()]) - .spawn() - .unwrap() - .wait() - .unwrap(); - } - - // Return to the root directory - std::env::set_current_dir(root_dir).unwrap(); - log!(verbose, "Returned to root directory"); - } else { - // Clone normally - Command::new("git") - .args(&["clone", &r.url, &r.name]) - .args(if r.branch.is_some() { - vec!["-b", r.branch.as_ref().unwrap()] - } else { - vec![] - }) - .spawn() - .unwrap() - .wait() - .unwrap(); - } - } - } -} diff --git a/src/operations/config.rs b/src/operations/config.rs deleted file mode 100644 index 503fe25..0000000 --- a/src/operations/config.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::env; -use std::path::Path; -use std::process::Command; - -use crate::{log, repository::create}; - -pub fn config(verbose: bool) { - // Generate new config file if not already present - if !Path::exists("mlc.toml".as_ref()) { - log!(verbose, "Creating mlc.toml"); - create(verbose); - } - - // Open config file in user's editor of choice - let editor = env::var("EDITOR").unwrap_or_else(|_| "nano".to_string()); - log!(verbose, "Opening mlc.toml in {}", editor); - Command::new(editor) - .arg("mlc.toml") - .spawn() - .unwrap() - .wait() - .unwrap(); -} diff --git a/src/operations/info.rs b/src/operations/info.rs deleted file mode 100644 index c281df4..0000000 --- a/src/operations/info.rs +++ /dev/null @@ -1,286 +0,0 @@ -use colored::Colorize; -use spinoff::{Color, Spinner, Spinners}; -use std::env; -use std::fmt::Write; -use std::path::Path; -use std::process::Command; -use tabled::Tabled; - -use crate::{crash, info, internal::AppExitCode, log}; - -// For displaying the table of contents -#[derive(Clone, tabled::Tabled, Debug)] -struct RepoDisplayGit { - #[tabled(rename = "Name")] - name: String, - #[tabled(rename = "URL")] - url: String, - #[tabled(skip)] - priority: usize, - #[tabled(rename = "Git Info")] - git_info: String, -} - -#[derive(Clone, tabled::Tabled, Debug)] -struct RepoDisplay { - #[tabled(rename = "Name")] - name: String, - #[tabled(rename = "URL")] - url: String, - #[tabled(skip)] - priority: usize, -} - -pub fn git_status(verbose: bool, repo: &str, colorblind: bool) -> String { - let dir = env::current_dir().unwrap(); - log!( - verbose, - "Current directory: {}", - env::current_dir().unwrap().display() - ); - env::set_current_dir(&repo).unwrap_or_else(|e| { - crash!( - AppExitCode::RepoParseError, - "Failed to enter directory {} for Git info: {}, Have you initialized the repo?", - repo, - e.to_string() - ); - }); - log!(verbose, "Current directory: {}", repo); - - let output = Command::new("git").arg("status").output().unwrap(); - let output = String::from_utf8(output.stdout).unwrap(); - log!(verbose, "Git status: {}", output); - - let unstaged = output.contains("Changes not staged for commit") - || output.contains("Changes to be committed"); - let untracked = output.contains("Untracked files"); - let dirty = unstaged || untracked; - - let pull = output.contains("Your branch is behind"); - let push = output.contains("Your branch is ahead"); - - let latest_commit = Command::new("git") - .args(&["log", "--pretty=%h", "-1"]) - .output() - .unwrap(); - let mut latest_commit = String::from_utf8(latest_commit.stdout).unwrap(); - latest_commit.retain(|c| !c.is_whitespace()); - - let output = if colorblind { - format!( - "{} {} {} {}", - if dirty { "D".red() } else { "D".bright_blue() }, - if pull { "Pl".red() } else { "Pl".bright_blue() }, - if push { "Ps".red() } else { "Ps".bright_blue() }, - latest_commit - ) - } else { - format!( - "{} {} {} {}", - if dirty { "D".red() } else { "D".green() }, - if pull { "Pl".red() } else { "Pl".green() }, - if push { "Ps".red() } else { "Ps".green() }, - latest_commit - ) - }; - env::set_current_dir(&dir).unwrap(); - log!(verbose, "Current directory: {}", dir.display()); - output -} - -pub fn info(verbose: bool) { - log!(verbose, "Showing Info"); - // Parse config from mlc.toml - let config = crate::internal::parse_cfg(verbose); - log!(verbose, "Config: {:?}", config); - - // Check for git_info - let git_info = if config.mode.workspace.is_some() { - config.mode.workspace.as_ref().unwrap().git_info - } else { - false - }; - log!(verbose, "Git info: {}", git_info); - - // Check for colorblind mode - let colorblind = if config.mode.workspace.is_some() { - config.mode.workspace.as_ref().unwrap().colorblind - } else { - false - }; - log!(verbose, "Colorblind: {}", colorblind); - - // Add the branch to the name if it's not the default branch for said repository - let repos_unparsed = config.repositories; - let mut repos = vec![]; - let mut repos_git = vec![]; - - if git_info { - // Crash early if directories are not found for git_info - for repo in &repos_unparsed { - if !Path::new(&repo.name).exists() { - crash!( - AppExitCode::RepoParseError, - "Failed to check directory {} for Git info, have you initialized the repo?", - repo.name, - ); - }; - } - - // Start the spinner - let sp = Spinner::new( - Spinners::Line, - format!("{}", "Parsing Git Info...".bold()), - Color::Green, - ); - - // Construct bash script to run git remote upgrade on all repos asynchronously - // This helps speed up the operation when, for example, you have a lot of repositories and you store your SSH key as a subkey of your GPG key on a yubikey - // This took my `mlc info` time down from 17s to 8s (i have the above described setup) - let mut bash_script = String::new(); - bash_script.push_str( - "\n\ - #!/usr/bin/env bash\n\ - \n\ - # This script will run `git remote update` in all repositories\n\ - pull() { cd $1; git remote update; cd -; }\n\ - \n", - ); - for repo in &repos_unparsed { - writeln!(bash_script, "pull {} &", repo.name).unwrap(); - } - bash_script.push_str("wait\n"); - - log!(verbose, "Bash script: {}", bash_script); - - // Run the bash script - Command::new("bash") - .arg("-c") - .arg(bash_script) - .output() - .unwrap(); - - // Stop the spinner with a success message - let text = format!("{}", "Parsing Git Info... Done".bold()); - let symbol = format!("{}", "✔".bold().green()); - - sp.stop_and_persist(&symbol, &text); - log!(verbose, "Repos: {:?}", repos); - } - - // Iterate over all repositories - for repo in repos_unparsed { - // Get name with branch, '/' serving as the delimiter - let name = if repo.branch.is_some() { - format!("{}/{}", repo.name, repo.branch.unwrap()) - } else { - repo.name.clone() - }; - - // Get git info, if applicable - let git_info_string = if git_info { - let info = Some(git_status( - verbose, - &repo.name, - config.mode.workspace.as_ref().unwrap().colorblind, - )); - info - } else { - None - }; - - // Push to the correct vector, we're using a separate vector for git info because - // the struct we're displaying is different - if git_info { - repos_git.push(RepoDisplayGit { - name, - url: repo.url.clone(), - priority: repo.priority, - git_info: git_info_string.unwrap(), - }); - } else { - repos.push(RepoDisplay { - name, - url: repo.url.clone(), - priority: repo.priority, - }); - } - } - - // Sort by priority - repos.sort_by(|a, b| b.priority.cmp(&a.priority)); - repos_git.sort_by(|a, b| b.priority.cmp(&a.priority)); - if git_info { - log!(verbose, "Repos Sorted: {:?}", repos_git); - } else { - log!(verbose, "Repos Sorted: {:?}", repos); - } - - // Displaying basic info about the Malachite Repository - let internal_name = if config.mode.repository.is_none() - || config.mode.repository.as_ref().unwrap().name.is_empty() - { - env::current_dir() - .unwrap() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string() - } else { - config.mode.repository.unwrap().name - }; - let name = format!( - "{} \"{}\":", - // Sidenote: It should NOT be this convoluted to capitalise the first character of a string in rust. What the fuck. - String::from_utf8_lossy(&[config.base.mode.as_bytes()[0].to_ascii_uppercase()]) - + &config.base.mode[1..], - internal_name - ); - - // Get terminal width for table formatting - let width = match crossterm::terminal::size() { - Ok((w, _)) => w, - Err(_) => 80, - }; - - // Create table for displaying info - let table = if git_info { - tabled::Table::new(&repos_git) - .with(tabled::Style::modern()) - .with(tabled::Width::wrap(width as usize).keep_words()) - .to_string() - } else { - tabled::Table::new(&repos) - .with(tabled::Style::modern()) - .with(tabled::Width::wrap(width as usize).keep_words()) - .to_string() - }; - - // Get length of Vec for displaying in the table - let len = if git_info { - repos_git.len() - } else { - repos.len() - }; - - // Print all of the info - info!("{}", name); - info!("Total Repositories: {}", len.to_string().green()); - println!("{}", table); - if config.mode.workspace.is_some() && config.mode.workspace.as_ref().unwrap().git_info { - info!( - "D: Dirty - Unstaged Changes \n \ - Pl: Pull - Changes at Remote \n \ - Ps: Push - Unpushed Changes \n \ - {}: Dirty, {}: Clean", - " ".on_red(), - if config.mode.workspace.unwrap().colorblind { - " ".on_bright_blue() - } else { - " ".on_green() - } - ); - } -} diff --git a/src/operations/mod.rs b/src/operations/mod.rs deleted file mode 100644 index 82a8a46..0000000 --- a/src/operations/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub use build::*; -pub use clean::*; -pub use clone::*; -pub use config::*; -pub use info::*; -pub use prune::*; -pub use pull::*; - -mod build; -mod clean; -mod clone; -mod config; -mod info; -mod prune; -mod pull; diff --git a/src/operations/prune.rs b/src/operations/prune.rs deleted file mode 100644 index 7af2204..0000000 --- a/src/operations/prune.rs +++ /dev/null @@ -1,139 +0,0 @@ -use colored::Colorize; -use std::env; -use std::fs; -use std::path::PathBuf; - -use crate::info; -use crate::log; -use crate::parse_cfg; - -#[derive(Debug, Clone)] -struct PackageFile { - name: String, - ver: String, - ext: String, -} - -pub fn prune(verbose: bool) { - // Read config struct from mlc.toml - let config = parse_cfg(verbose); - log!(verbose, "Config: {:?}", config); - - // Read current directory - let current_dir = env::current_dir().unwrap(); - log!(verbose, "Current dir: {:?}", current_dir); - - // Enter out directory - env::set_current_dir("out").unwrap(); - log!(verbose, "Current dir: {:?}", env::current_dir().unwrap()); - - // Read all files from . into a Vec, except for .sig files - let mut files: Vec = vec![]; - for entry in fs::read_dir(".").unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - if path.extension().unwrap() != "sig" { - files.push(path); - } - } - log!(verbose, "Files: {:?}", files); - - // Split files into Vec, turning package-name-1.0.0-1-x86_64.tar.gz into PackageFile { name: "package-name", ver: "1.0.0-1", ext: "x86_64.tar.gz" } - let mut packages: Vec = vec![]; - for file in files { - // Regex, splits package-name-1.0.0-1-x86_64.tar.gz into 3 groups: package-name, -1.0.0-1, -x86_64.tar.gz - let re = regex::Regex::new(r"^(.+)(-.+-.+)(-.+\..+\..+\.+..+)$").unwrap(); - - // Get file name to string - let file = file.to_str().unwrap(); - - // Match file name against regex - for cap in re.captures_iter(file) { - // Collect regex captures - let name = cap[1].to_string(); - let mut ver = cap[2].to_string(); - let mut ext = cap[3].to_string(); - - // Strip leading - from ver and ext - ver.remove(0).to_string(); - ext.remove(0).to_string(); - - let package = PackageFile { name, ver, ext }; - log!(verbose, "Package: {:?}", package); - packages.push(package); - } - } - - // Split packages into a Vector of Vectors by unique name - let mut packages_by_name: Vec> = vec![]; - for package in &packages { - log!(verbose, "Sorting Package: {:?}", package); - let name = &package.name; - let mut found = false; - // Check if name is already present in packages_by_name - for p in &mut packages_by_name { - if &p[0].name == name { - log!(verbose, "Found {}", name); - found = true; - p.push(package); - } - } - // If not, create a new vector and push to it - if !found { - log!(verbose, "Creating {}", name); - packages_by_name.push(vec![package]); - } - } - - // Sort each Vector of Vectors by version - for p in &mut packages_by_name { - log!(verbose, "Sorting {:?}", p); - p.sort_by(|a, b| b.ver.cmp(&a.ver)); - } - - // Pushes all but the 3 most recent versions of each package into a new Vector of PackageFiles - let mut packages_to_delete: Vec = vec![]; - for p in &packages_by_name { - let mut to_delete = vec![]; - for (i, _) in p.iter().enumerate() { - if i >= 3 { - log!(verbose, "Deleting {:?}", p[i]); - to_delete.push(p[i].clone()); - } - } - packages_to_delete.extend(to_delete); - } - log!(verbose, "Packages to delete: {:?}", packages_to_delete); - - // Delete all packages in packages_to_delete - for p in &packages_to_delete { - let path = format!("{}-{}-{}", p.name, p.ver, p.ext); - log!(verbose, "Deleting {}", path); - std::process::Command::new("bash") - .args(&["-c", &format!("rm -rf ./{} ./{}.sig", path, path)]) - .output() - .unwrap(); - } - - // Return to current directory - env::set_current_dir(current_dir).unwrap(); - log!(verbose, "Current dir: {:?}", env::current_dir().unwrap()); - - // Print which packages were deleted - if packages_to_delete.is_empty() { - info!("No packages were deleted."); - } else { - info!("Deleted the following packages:"); - for p in &mut packages_to_delete { - println!( - "{}", - format!( - " {}-{}", - p.name.replace("./", "").replace(".\\", ""), - p.ver - ) - .bold() - ); - } - } -} diff --git a/src/operations/pull.rs b/src/operations/pull.rs deleted file mode 100644 index 3e79a2e..0000000 --- a/src/operations/pull.rs +++ /dev/null @@ -1,207 +0,0 @@ -use std::env; -use std::process::Command; - -use crate::info; -use crate::{crash, internal::AppExitCode, log, prompt}; - -struct PullParams { - smart_pull: bool, - build_on_update: bool, - no_regen: bool, -} - -fn do_the_pulling(repos: Vec, verbose: bool, params: &PullParams, interactive: bool) { - for repo in repos { - // Set root dir to return after each git pull - let root_dir = env::current_dir().unwrap(); - log!(verbose, "Root dir: {:?}", root_dir); - - // Enter repo dir - info!("Entering working directory: {}", &repo); - env::set_current_dir(&repo).unwrap(); - log!(verbose, "Current dir: {:?}", env::current_dir().unwrap()); - - let mut packages_to_rebuild: Vec = vec![]; - - // Pull logic - log!(verbose, "Pulling"); - if params.smart_pull { - // Update the remote - log!(verbose, "Smart pull"); - Command::new("git") - .args(&["remote", "update"]) - .spawn() - .unwrap() - .wait() - .unwrap(); - - // Check the repository status - let output = Command::new("git").arg("status").output().unwrap(); - - // If there are changes, pull normally - if String::from_utf8(output.stdout) - .unwrap() - .to_string() - .contains("Your branch is behind") - { - info!("Branch out of date, pulling changes"); - Command::new("git") - .arg("pull") - .spawn() - .unwrap() - .wait() - .unwrap(); - - // If build_on_update is set, rebuild package - if params.build_on_update { - if interactive { - let cont = prompt!(default true, "Rebuild package {}?", &repo); - if cont { - info!("Package {} updated, staging for rebuild", &repo); - log!(verbose, "Pushing package {} to be rebuilt", &repo); - packages_to_rebuild.push(repo); - } else { - info!("Not rebuilding package {}", &repo); - } - } else { - packages_to_rebuild.push(repo); - } - } - } else { - // If there are no changes, alert the user - info!("No changes to pull"); - } - } else { - // Pull normally - log!(verbose, "Normal pull"); - Command::new("git") - .arg("pull") - .spawn() - .unwrap() - .wait() - .unwrap(); - } - // Return to root dir - env::set_current_dir(&root_dir).unwrap(); - log!( - verbose, - "Returned to root dir: {:?}", - env::current_dir().unwrap() - ); - - // Rebuild packages if necessary - if !packages_to_rebuild.is_empty() && params.build_on_update { - info!("Rebuilding packages: {}", &packages_to_rebuild.join(", ")); - log!(verbose, "Rebuilding packages: {:?}", &packages_to_rebuild); - - // Push to build - crate::operations::build(&packages_to_rebuild, vec![], params.no_regen, verbose); - - // Ensure you are in root dir - env::set_current_dir(root_dir).unwrap(); - log!( - verbose, - "Returned to root dir: {:?}", - env::current_dir().unwrap() - ); - } - } -} - -pub fn pull( - packages: Vec, - exclude: &[String], - verbose: bool, - no_regen: bool, - interactive: bool, -) { - // Read config file - let config = crate::parse_cfg(verbose); - log!(verbose, "Config: {:?}", config); - - // If no packages are specified, imply all - let all = packages.is_empty(); - log!(verbose, "All: {}", all); - - // Read smart_pull from config - let smart_pull = config.base.smart_pull; - log!(verbose, "Smart pull: {}", smart_pull); - - // Read build_on_update from config - let build_on_update = if config.mode.repository.is_some() { - config.mode.repository.unwrap().build_on_update - } else { - false - }; - log!(verbose, "Build on update: {}", build_on_update); - - // Read repos from config - let repos = config - .repositories - .iter() - .map(|x| x.name.clone()) - .collect::>(); - log!(verbose, "Repos: {:?}", repos); - - // Set repos_applicable for next function - let mut repos_applicable = if all { repos } else { packages }; - log!(verbose, "Repos applicable: {:?}", repos_applicable); - - // Subtract exclude from repos_applicable - if !exclude.is_empty() { - for ex in exclude.iter() { - repos_applicable.retain(|x| *x != *ex); - } - } - log!(verbose, "Exclude: {:?}", exclude); - log!(verbose, "Repos applicable excluded: {:?}", repos_applicable); - - // If all is not specified and packages is empty, crash - if repos_applicable.is_empty() { - crash!(AppExitCode::PkgsNotFound, "No packages specified"); - } - - // Sort repos_applicable by priority - repos_applicable.sort_by(|a, b| { - config - .repositories - .iter() - .find(|x| x.name == *a) - .unwrap() - .priority - .cmp( - &config - .repositories - .iter() - .find(|x| x.name == *b) - .unwrap() - .priority, - ) - }); - - log!(verbose, "Pulling {:?}", repos_applicable); - - // If any repos are not in the config, run a clone - for repo in &repos_applicable { - if !std::path::Path::new(repo).exists() { - info!( - "Repo {} does not exist, ensuring all repos are cloned", - repo - ); - crate::operations::clone(verbose); - break; - } - } - - // Pull! - do_the_pulling( - repos_applicable, - verbose, - &PullParams { - smart_pull, - build_on_update, - no_regen, - }, - interactive, - ); -} diff --git a/src/prune.rs b/src/prune.rs new file mode 100644 index 0000000..bd14b53 --- /dev/null +++ b/src/prune.rs @@ -0,0 +1,99 @@ +use std::collections::HashMap; +use std::fs; +use std::fs::File; +use std::path::Path; + +use crate::errors::AppResult; + +#[derive(Debug, Clone)] +pub struct PackageFile { + pub path: String, + pub package: Package, +} + +#[derive(Debug, Clone)] +pub struct PackageFiles { + files: HashMap>, +} + +#[derive(Debug, Clone)] +pub struct Package { + pub name: String, + pub version: String, +} + +impl Package { + pub fn new(name: String, version: String) -> Self { + Self { name, version } + } + + pub fn read(path: &Path) -> AppResult { + let mut file = File::open(path)?; + let mut buffer = Vec::new(); + + compress_tools::uncompress_archive_file(&mut file, &mut buffer, ".PKGINFO")?; + let pkginfo = String::from_utf8(buffer)?; + + let mut pkgname = String::new(); + let mut pkgver = String::new(); + + for line in pkginfo.lines() { + let line = line.trim(); + if line.starts_with("pkgname") { + pkgname = line.split('=').nth(1).unwrap().trim().to_string(); + } else if line.starts_with("pkgver") { + pkgver = line.split('=').nth(1).unwrap().trim().to_string(); + } + } + + Ok(PackageFile { + path: path.display().to_string(), + package: Package::new(pkgname, pkgver), + }) + } +} + +impl PackageFiles { + pub fn new() -> Self { + Self { + files: HashMap::new(), + } + } + + pub fn add(&mut self, file: PackageFile) { + self.files + .entry(file.clone().package.name) + .or_default() + .push(file); + } + + pub fn scan(&mut self, path: &Path) -> AppResult<()> { + for entry in fs::read_dir(path)? { + let entry = entry?; + let path = entry.path(); + let path_display = path.display().to_string(); + if path.is_file() + && path_display.contains(".pkg.tar.") + && !path_display.ends_with(".sig") + { + let file = Package::read(&path)?; + self.add(file); + } + } + Ok(()) + } + + pub fn prune(&mut self, keep: u8) -> AppResult { + let mut to_delete = Vec::new(); + for (_, files) in self.files.iter_mut() { + files.sort_by(|a, b| b.package.version.cmp(&a.package.version)); + to_delete.extend(files.iter().skip(keep as usize)); + } + + for file in &to_delete { + fs::remove_file(&file.path)?; + } + + Ok(to_delete.len()) + } +} diff --git a/src/repository/config.rs b/src/repository/config.rs deleted file mode 100644 index 2e182c1..0000000 --- a/src/repository/config.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::Path; - -use crate::internal::AppExitCode; -use crate::{crash, log}; - -const DEFAULT_CONFIG: &str = r#" -[base] -# Either "repository" or "workspace" -mode = "" -# Better left as true, but can be set to false if it causes issues with branches -smart_pull = true - -[mode.repository] -# Decides what to call the repository and relevant files -name = "" -# Decides whether to build packages if package repo is updated on pull -build_on_update = false - -[mode.repository.signing] -# Decides whether or not to sign packages -enabled = true - -[mode.workspace] -# Whether to show rich git info for repositories -git_info = true -# Colorblind mode toggle -colorblind = false - -[repositories] -# List of repositories formatted as id:name (priority is decided by the ! suffix, and decides package build order) -repos = [ - "aur:hello!", - "crs:malachite" -] - -[repositories.urls] -# URL keys for repositories, with {} where the repository name would go -crs = "https://github.com/crystal-linux/{}" -aur = "https://aur.archlinux.org/{}" -"#; - -pub fn create(verbose: bool) { - // Ensure current directory is empty - if env::current_dir() - .unwrap() - .read_dir() - .unwrap() - .next() - .is_some() - { - crash!( - AppExitCode::DirNotEmpty, - "Directory is not empty, please only create a repository in an empty directory" - ); - } - log!(verbose, "Creating config file"); - - // If config file exists, create it - if !Path::exists("mlc.toml".as_ref()) { - let mut file = File::create("mlc.toml").unwrap(); - file.write_all(DEFAULT_CONFIG.as_ref()).unwrap(); - } - log!(verbose, "Config file created"); -} diff --git a/src/repository/mod.rs b/src/repository/mod.rs deleted file mode 100755 index 59534d6..0000000 --- a/src/repository/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub use config::*; -pub use package::*; -pub use repo::*; - -mod config; -mod package; -mod repo; diff --git a/src/repository/package.rs b/src/repository/package.rs deleted file mode 100755 index 04a26fa..0000000 --- a/src/repository/package.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::path::Path; -use std::process::Command; -use std::{env, fs}; - -use crate::internal::AppExitCode; -use crate::{crash, log}; - -pub fn build(pkg: &str, sign: bool, verbose: bool) -> i32 { - log!(verbose, "Building {}", pkg); - log!(verbose, "Signing: {}", sign); - - // Set root dir to return after build - let dir = env::current_dir().unwrap(); - log!(verbose, "Root dir: {:?}", dir); - - // Create out dir if not already present - if !Path::exists("out".as_ref()) { - log!(verbose, "Creating out dir"); - fs::create_dir_all("out").unwrap(); - } - - // If package directory is not found, crash - if !Path::exists(pkg.as_ref()) { - crash!( - AppExitCode::PkgsNotFound, - "Repo for package {} not found, aborting", - pkg - ); - } - - // Enter build directory - env::set_current_dir(pkg).unwrap(); - log!(verbose, "Current dir: {:?}", env::current_dir().unwrap()); - - // If PKGBUILD is not found, return 63 and break - if !Path::exists("PKGBUILD".as_ref()) { - env::set_current_dir(&dir).unwrap(); - log!(verbose, "Current dir: {:?}", env::current_dir().unwrap()); - return 63; - } - - // Parse extra flags from envvar - let extra_flags = env::var("MAKEPKG_FLAGS").unwrap_or_else(|_| "".to_string()); - let extra_flags = extra_flags.split(' ').collect::>(); - - // Default set of flags - let default_args = vec![ - "-sf", - "--skippgpcheck", - if sign { "--sign" } else { "--nosign" }, - "--noconfirm", - ]; - - // Build each package - let a = Command::new("makepkg") - .args( - default_args - .iter() - .chain(extra_flags.iter()) - .map(std::string::ToString::to_string), - ) - .spawn() - .unwrap() - .wait() - .unwrap(); - log!(verbose, "{} Build job returned: {:?}", pkg, a); - - // Copy built package to out dir - Command::new("bash") - .args(&["-c", "cp *.pkg.tar* ../out/"]) - .spawn() - .unwrap() - .wait() - .unwrap(); - log!(verbose, "Copied built package to out dir"); - - // Return to root dir - env::set_current_dir(dir).unwrap(); - log!( - verbose, - "Returned to root dir: {:?}", - env::current_dir().unwrap() - ); - - // Return exit code - a.code().unwrap() -} diff --git a/src/repository/repo.rs b/src/repository/repo.rs deleted file mode 100644 index 3d53a4e..0000000 --- a/src/repository/repo.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::path::Path; -use std::process::Command; -use std::{env, fs}; - -use crate::{crash, info, internal::parse_cfg, internal::AppExitCode, log}; - -pub fn generate(verbose: bool) { - // Read config struct from mlc.toml - let config = parse_cfg(verbose); - log!(verbose, "Config: {:?}", config); - - // Get signing from config - let signing = &config.mode.repository.as_ref().unwrap().signing.enabled; - log!(verbose, "Signing: {:?}", signing); - - // Get repository name from config - let name = &config.mode.repository.as_ref().unwrap().name; - log!(verbose, "Name: {}", name); - - // Read on_gen from config - let on_gen = &config.mode.repository.as_ref().unwrap().signing.on_gen; - log!(verbose, "On gen: {:?}", on_gen); - - // Read key from config - let key = &config.mode.repository.as_ref().unwrap().signing.key; - log!(verbose, "Key: {:?}", key); - - info!("Generating repository: {}", name); - - // If repository exists, empty it - if Path::exists(name.as_ref()) { - log!(verbose, "Deleting contents of {}", name); - Command::new("bash") - .args(&["-c", &format!("rm -rf {}/*", &name)]) - .spawn() - .unwrap() - .wait() - .unwrap(); // Dirty temp hack but oh well - } else { - // Create dir if it doesn't exist - fs::create_dir_all(&name).unwrap(); - log!(verbose, "Created {}", name); - } - - // Copy out packages to repository directory - Command::new("bash") - .args(&["-c", &format!("cp -v out/* {}/", &name)]) - .spawn() - .unwrap() - .wait() - .unwrap(); - log!(verbose, "Copied out packages to {}", name); - - // Enter repository directory - env::set_current_dir(&name).unwrap(); - log!(verbose, "Current dir: {:?}", env::current_dir().unwrap()); - - // Sign all package files in repository if signing and on_gen are true - if *signing && on_gen.is_some() && on_gen.unwrap() { - // Get a list of all .tar.* files in repository - let files = fs::read_dir(".").unwrap(); - - for file in files { - // Get file name - let file = file.unwrap(); - let path = file.path(); - - let sign_command = if key.is_some() && !key.as_ref().unwrap().is_empty() { - format!( - "gpg --default-key {} --detach-sign {}", - key.as_ref().unwrap(), - path.to_str().unwrap() - ) - } else { - format!("gpg --detach-sign {}", path.to_str().unwrap()) - }; - - // If extension is either .zst or .xz, sign it - if path.extension().unwrap() == "zst" || path.extension().unwrap() == "xz" { - log!(verbose, "Signing {}", path.display()); - Command::new("bash") - .arg("-c") - .args(&[&sign_command]) - .spawn() - .unwrap() - .wait() - .unwrap(); - } - } - log!(verbose, "Signed repository"); - } - - let db = format!("{}.db", &name); - let files = format!("{}.files", &name); - - // Check if package files end with .tar.zst or .tar.xz - let zst = Command::new("bash") - .args(&["-c", "ls *.tar.zst"]) - .spawn() - .unwrap() - .wait() - .unwrap(); - let xz = Command::new("bash") - .args(&["-c", "ls *.tar.xz"]) - .spawn() - .unwrap() - .wait() - .unwrap(); - - // This should never happen, crash and burn if it does - if zst.success() && xz.success() { - crash!( - AppExitCode::RepoParseError, - "Both .tar.zst and .tar.xz files found in repository. You've done something wrong. Aborting" - ); - } - - // Ensuring aarch64/ALARM support for the future - let aarch64_mode = if zst.success() { - false - } else if xz.success() { - true - } else { - crash!( - AppExitCode::PkgsNotFound, - "No .zst or .xz packages found in repository" - ); - // This should theoretically never be reached, but let's just give the compiler what it wants - false - }; - let suffix = if aarch64_mode { "xz" } else { "zst" }; - - // Create repo.db and repo.files using repo-add - Command::new("bash") - .args(&[ - "-c", - &format!( - "GLOBIGNORE=\"*.sig\" repo-add {}.tar.gz *.pkg.tar.{}", - db, suffix - ), - ]) - .spawn() - .unwrap() - .wait() - .unwrap(); - log!(verbose, "Created {} and {}", db, files); - - // Replace repo.{db,files}.tar.gz with just repo.{db,files} - Command::new("bash") - .args(&[ - "-c", - &format!("mv {}.tar.gz {}; mv {}.tar.gz {}", db, db, files, files), - ]) - .spawn() - .unwrap() - .wait() - .unwrap(); - log!( - verbose, - "Renamed {}.tar.gz to {} and {}.tar.gz to {}", - db, - db, - files, - files - ); -} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..4511588 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,102 @@ +use std::path::PathBuf; +use std::process::{Child, Command, ExitStatus}; + +use crate::errors::{AppError, AppResult}; + +pub fn uid() -> i32 { + (unsafe { libc::geteuid() } as i32) +} + +#[derive(Debug, Clone)] +pub struct ShellCommand { + pub command: String, + pub args: Vec, + pub cwd: Option, +} + +impl ShellCommand { + pub fn repo_add() -> Self { + Self::new("repo-add") + } + + pub fn git() -> Self { + Self::new("git") + } + + pub fn makepkg() -> Self { + Self::new("makepkg") + } + + pub fn new(command: S) -> Self { + Self { + command: command.to_string(), + args: vec![], + cwd: None, + } + } + + pub fn arg(mut self, arg: S) -> Self { + self.args.push(arg.to_string()); + self + } + + pub fn args(mut self, args: V) -> Self + where + S: ToString, + V: IntoIterator, + { + self.args.extend(args.into_iter().map(|s| s.to_string())); + self + } + + pub fn cwd>(mut self, cwd: P) -> Self { + self.cwd = Some(cwd.into()); + self + } + + #[allow(dead_code)] + pub fn wait(&self) -> AppResult { + let mut child = self.spawn()?; + + child.wait().map_err(AppError::from) + } + + #[allow(dead_code)] + pub fn wait_with_output(&self) -> AppResult { + let child = self.spawn()?; + + child.wait_with_output().map_err(AppError::from) + } + + pub fn silent(&self) -> AppResult<()> { + let _ = self.output()?; + + Ok(()) + } + + pub fn output(&self) -> AppResult { + let mut command = Command::new(&self.command); + command.args(&self.args); + + if let Some(cwd) = &self.cwd { + command.current_dir(cwd); + } + + let output = command.output()?; + + Ok(output) + } + + pub fn spawn(&self) -> AppResult { + let mut command = Command::new(&self.command); + command.args(&self.args); + + if let Some(cwd) = &self.cwd { + command.current_dir(cwd); + } + + let child = command.spawn()?; + + Ok(child) + } +}