diff --git a/.gitignore b/.gitignore index 8ab9a21145b7911a0b893fac2ed3121ceba4ebcb..f4da9214936f9d77e301115459500208f070daa7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,10 @@ src/src # Параметры VSCodium .vscode + +# Cargo files +/target + +# Отключение системы сборки +/src/bs.rs +/src/ui/bs.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df688df0c1b42354be4df1144d7903a71f5c918..6f220a903cc108a33df6e9f878ab3385dccd9c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Change Log +## cport v2.0.0-a1 + +- 25.06.2023 cov_id111 + - Изменение структуры проекта. + - Переход на использование нового стандарта системы портов. + +- 28.03.2023 cov_id111 + - Добавление в документацию сведений об использовании `cport`. + +- 27.03.2023 cov_id111 + - Добавление тестов; очистка кода. + +- 26.03.2023 cov_id111 + - Добавление в документацию сведений о новом стандарте системы портов. + +- 24.03.2023 cov_id111 + - Начало работы над `cport-rs`. + ## cport v1.0current - 02.10.2022 cov_id111 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..5b80574b7399301c0d38bc8a87bd75d111a47a8a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1961 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "allocator-api2" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9" + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.45.0", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cport_cli" +version = "2.0.0" +dependencies = [ + "anyhow", + "clap", + "colored", + "cursive", + "libcport", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cursive" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5438eb16bdd8af51b31e74764fef5d0a9260227a5ec82ba75c9d11ce46595839" +dependencies = [ + "ahash", + "cfg-if", + "crossbeam-channel", + "cursive_core", + "lazy_static", + "libc", + "log", + "maplit", + "ncurses", + "signal-hook", + "term_size", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "cursive_core" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4db3b58161228d0dcb45c7968c5e74c3f03ad39e8983e58ad7d57061aa2cd94d" +dependencies = [ + "ahash", + "crossbeam-channel", + "enum-map", + "enumset", + "lazy_static", + "log", + "num", + "owning_ref", + "time", + "unicode-segmentation", + "unicode-width", + "xi-unicode", +] + +[[package]] +name = "darling" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "darling_macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-map" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "988f0d17a0fa38291e5f41f71ea8d46a5d5497b9054d5a759fae2cbb819f2356" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enumset" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" +dependencies = [ + "hashbrown 0.14.0", +] + +[[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.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[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 = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indicatif" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "libcport" +version = "2.0.0" +dependencies = [ + "anyhow", + "colored", + "futures", + "futures-util", + "indicatif", + "nix", + "reqwest", + "rusqlite", + "serde", + "tokio", + "toml", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "mkport" +version = "0.1.0" +dependencies = [ + "anyhow", + "cursive", + "libcport", + "serde", + "toml", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ncurses" +version = "5.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5d34d72657dc4b638a1c25d40aae81e4f1c699062f72f467237920752032" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", + "pin-utils", + "static_assertions", +] + +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", +] + +[[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-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "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 = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl" +version = "0.10.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b3f656a17a6cbc115b5c7a40c616947d213ba182135b014d6051b73ab6f019" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ce0f250f34a308dcfdbb351f511359857d4ed2134ba715a4eadd46e1ffd617" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "owning_ref" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +dependencies = [ + "stable_deref_trait", +] + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.0", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[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.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "portable-atomic" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794" + +[[package]] +name = "proc-macro2" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rusqlite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[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.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[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.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-streams" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "xi-unicode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..6f0f1e72f471cbc5bed4c632532c4ca1dc6016da --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,40 @@ +# [package] +# name = "cport" +# version = "2.0.0-a1" +# edition = "2021" +# description = "Software management utility for Calmira GNU/Linux-libre system" +# authors = ["Michail Krasnov "] +# license = "GPL-3.0" +# repository = "https://gitlab.com/calmiralinux/CABS/cport" +# keywords = ["cport", "calmira_linux", "software", "source_code"] +# categories = ["command-line-utilities", "os"] + +[workspace] +members = [ + "libcport", + "cport_cli", + "mkport" +] + +[profile.release] +lto = true +strip = true +opt-level = "s" +panic = "abort" +debug = false +codegen-units = 1 + +# [dependencies] +# anyhow = "1.0.69" +# clap = { version = "4.1.7", features = ["derive"] } +# colored = "2.0.0" +# cursive = "0.20.0" +# futures = "0.3.26" +# futures-util = "0.3.28" +# indicatif = "0.17.3" +# nix = "0.26.2" +# reqwest = { version = "0.11.18", features = ["stream"] } +# rusqlite = "0.28.0" +# serde = { version = "1.0.152", features = ["derive"] } +# tokio = { version = "1.25.0", features = ["full"] } +# toml = "0.7.2" diff --git a/Makefile b/Makefile deleted file mode 100644 index 8b34d9252fdf9f2bac40a948351c48cb7a97ed49..0000000000000000000000000000000000000000 --- a/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -DESTDIR=/ -PYVERSION="3.10" - -binary: - python3 -m nuitka --low-memory --onefile src/cport.py - -install-binary: - mkdir -p ${DESTDIR}usr/bin - mkdir -p ${DESTDIR}usr/share/cport - mkdir -p ${DESTDIR}var/lib/Calmira - mkdir -p ${DESTDIR}var/cache/cport/downloads - cp -v cport.bin ${DESTDIR}usr/bin/cport - cp -v src/cport_cli/shared.sh ${DESTDIR}usr/share/cport - -remove-binary: - rm -v ${DESTDIR}usr/bin/cport - -clean: - rm -rf cport.{build,dist,onefile-build} cport.bin - -install: - mkdir -pv /usr/lib/python${PYVERSION}/site-packages - mkdir -p ${DESTDIR}usr/lib/python${PYVERSION}/site-packages - mkdir -p ${DESTDIR}usr/bin - mkdir -p ${DESTDIR}var/lib/Calmira - mkdir -p ${DESTDIR}usr/share/cport - mkdir -p ${DESTDIR}var/cache/cport/downloads - cp -rv src/{cport_cli,libcport} ${DESTDIR}usr/lib/python${PYVERSION}/site-packages/ - cp -rv src/cport_cli/shared.sh ${DESTDIR}usr/share/cport - cp -v src/cport.py ${DESTDIR}usr/bin/cport - -remove: - rm -rvf ${DESTDIR}/usr/{bin/cport,lib/python${PYVERSION}/site-packages/{cport_cli,libcport}} - rm -rvf ${DESTDIR}/var/{cache/cport,lib/Calmira} diff --git a/README.md b/README.md index 165da5356dc9462298c8c37d5b5965ee6fdf0b99..fb86b6e8d6458123da7873fde1c77badde900bef 100644 --- a/README.md +++ b/README.md @@ -1,244 +1,70 @@ -# cport +# cport-rs - ПРОТОТИП -![](pic/cport_check.gif) +> Данная версия является *прототипом* набора утилит `cport`. Этот прототип служит для определения всех слабых мест в системе портов и методов их избежать, а так же для видения пути развития как `cport`, так и системы портов в целом. На данный момент в разработке находится утилита `mkport`. После того, как она будет доведена до рабочего состояния, весь `cport` будет либо переписан, либо отрефакторен. -cport - менеджер системы портов дистрибутива Calmira GNU/Linux-libre, ставящий -перед собой задачу упрощения работы с программным обеспечением в этом -дистрибутиве. +`cport-rs` (также известен как `cport-v2.0`) - вторая версия менеджера системы портов `cport`, полностью переписанная на языке программирования Rust. Эта реализация МСП отличается от предыдущей переработкой архитектуры программы - теперь, используя крейт `cport`, можно создавать свои надстройки и утилиты, взаимодействующие с API системы портов и/или МСП `cport`. -## Содержание +## RoadMap -[TOC] - -## Предназначение - -У cport несколько целей: - -1. **Демонстрационная** - программа предназначена для демонстрации моделей - управления программным обеспечением в операционных системах семейства - GNU/Linux. В данном случае она демонстрирует использование системы портов как - метода для автоматизации сборки ПО из исходного кода. -2. **Практическая** - помимо автоматизации процессов, связанных с управлением - ПО, собранным из исходного кода, cport является утилитой для упращения УПО в - дистрибутивах GNU/Linux, собранных по LFS. cport ведёт базу данных ПО, а - также умеет выполнять базовые задачи по обслуживанию ОС. - -## Сборка - -### Зависимости - -| Тип | Порт/пакет | -| ------------ | ------------------------------------------------------------------------------------------------------------------ | -| Необходимые |
  1. base/python
  2. base/pymo/wget
  3. base/pymo/toml
| -| Опциональные |
  1. base/gcc
  2. general/pymo/nuitka
| - -### Использование Python-скриптов - -```bash -make install -``` - -### Трансляция Python-кода в C-код и компиляция посредством GCC - -```bash -make binary -make install-binary -``` - -## Синтаксис - -```bash -cport [-h] [--install NAME_I [NAME_I ...]] [--remove NAME_R [NAME_R ...]] - [--info NAME_I [NAME_I ...]] [--update] [--upgrade NAME_U [NAME_U ...]] - [--is-exist NAME_E] [--is-installed NAME_INS] [--check-ports] - [--yes] [--ignore-db IGNORE_DB] [--skip-check] [--rebuild-db] [-v] - {list} ... - -cport list [-h] [-a] [-i] [-I] [-b] -``` +- [X] Install ports +- [X] View port metadata +- [ ] Remove ports [частично] +- [ ] Upgrade ports +- [ ] Update port system +- [ ] Upgrade Calmira GNU/Linux-libre +- [ ] Calmira GNU/Linux-libre BS +- [X] Use Calmira GNU/Linux-libre software database +- [ ] Ports check: + - [ ] Installed + - [ ] Not-installed + - [ ] Broken +- [X] Ports metadata ## Использование -- `-i`, `--install` - установить порт(ы); -- `-r`, `--remove` - удалить порт(ы); -- `-u`, `--update` - обновить систему портов; -- `-I`, `--info` - просмотреть информацию о порте(ах); -- `list` - просмотреть список портов: - - `-a`, `--all` - всех портов (с отображением, установлены они или нет); - - `-i`, `--installed` - только установленных; - - `-I`, `--not-installed` - только НЕ установленных; - - `-b`, `--broken` - только битых; -- `--is-installed` - проверить, установлен ли порт или нет; -- `--is-exist` - проверить, существует ли порт; -- `-y`, `--yes` - отвечать утвердительно на все операции, запрашивающие у - пользователя подтверждения (при установке и удалении, например); -- `--rebuild-db` - восстановить/обновить/пересобрать базу данных cport; -- `--check-ports` - проверить систему портов на наличие или отсутствие портов, - указанных в качестве зависимостей других портов; -- `--ignore-db` - пропускать обращения к базе данных в операциях для установки и - удаления ПО. - -Например: - -```bash -# Установка портов 'editors/gvim', 'editors/nano' и 'base/acl' -# Соотв., будут установлены GVim, Nano и ACL. -cport -i editors/gvim editors/nano base/acl - -# Удаление 'editors/gvim' -cport -r editors/gvim - -# Просмотр информации об 'editors/gvim' -cport -I editors/gvim -``` +### bin files -Для просмотра доп. сведений введите: - -```bash -cport -h -``` +- `cport` - менеджер системы портов. +- `mkport` - утилита для создания новых портов. ### Установка порта -Для того, чтобы установить порт в систему, используется ключ `--install` или его -сокращённая версия `-i`: - ```bash -cport --install [все порты, разделённые между собой пробелом] +cport install PORT<1> PORT<2> ... PORT ``` -cport проверяет наличие порта в базе данных. В том случае, если порта в БД нет, -то выполняет сборочные инструкции из файла `install` порта. В случае, если ПО в -БД присутствует, то завершает работу с ошибкой. +**Дополнительные ключи:** -В том случае, если сборочные инструкции в файле `install` отработали без ошибок, -cport вычисляет статус установки порта. Статус - это число от 0 до 3, означающее -количество установленных в систему файлов (список файлов берётся из `files.list` -порта): - -- **0** - 100% файлов установлено в систему; -- **1** - 75%-99% файлов установлено в систему; -- **2** - 50-74% файлов; -- **3** - 5-49% файлов. - -Вычисленная информация вместе с остальной информацией о порте, взятой из файла -`port.toml`, добавляется в базу данных. В том случае, если статус равен 4 (не -считается статусом, но всё равно получается при вычислении), т.е. в системе -присутствует менее 4% или 5%, то считается, что установка порта произведена -неудачно. Порт в базу данных не добавляется и не считается установленным в -систему. +- `--jobs`, `-j` - указание числа потоков сборки ПО (по умолчанию - `1`). +- `--destination`, `-d` - указание альтернативного префикса установки пакета (по умолчанию - `/`). +- `--yes`, `-y` - утвердительно отвечать на все запросы продолжения. ### Удаление порта -Для удаления порта из файловой системы используется ключ `--remove` или его -сокрущённая версия `-r`. +``` +cport remove PORT<1> PORT<2> PORT +``` -cport проверяет наличие порта в базе данных. В том случае, если там он -отсутствует, то работа завершается с ошибкой. В противном случае cport читает -содержимое `files.list`, где записан список файлов порта, которые должны -присутствовать в системе и последовательно удаляет их из директории `/*`. После -чего удаляет запись о порте из базы данных. +**Дополнительные ключи:** -### Просмотр информации +- `--yes`, `-y` - утвердительно отвечать на все запросы продолжения. -Для того, чтобы просмотреть информацию о программном обеспечении, которое -находится в системе портов, используется ключ `--info` или его сокращённая -версия `-I`: +### Просмотр информации о ПО ```bash -cport --info [список портов, разделённых между собой ПРОБЕЛОМ] +cport info PORT<1> PORT<2> ... PORT ``` -![](pic/cport_info.gif) - -Данные выводятся из файла `port.toml` порта (конкретно - из секций `package` и -`deps`). Кроме того проверяется, установлен ли порт в систему. - -### Просмотр списка всех доступных портов - -Для того, чтобы просмотреть список всех доступных портов, используется опция -`list`. Это может не только вывести список -всего ПО, но ещё и выполнить первичную диагностику системы портов, так как при -выводе списка портов cport проверяет каждый из них на корректность: - -- Наличие нужных файлов в директории порта (`install`, `port.toml`, - `files.list`); -- Корректность конфига `port.toml`; -- Наличие директории порта в файловой системе `/usr/ports`. - -Кроме того, cport выделяет цветом каждый из портов. Первый символ вывода окрашен -в один из трёх цветов: - -1. **Зелёный:** порт присутствует в базе данных; -2. **Красный:** порт некорректен (возможно, в нём отсутствуют какие-либо файлы, - либо конфиг `port.toml` создан с ошибками); -3. **Серый:** порт полностью корректен, отсутствует в базе данных cport. - -![](pic/cport_list.gif) - -#### Ключи - -- `-a` - просмотр всех портов (также указывает, установлен ли порт или нет); -- `-i` - просмотр только установленных портов; -- `-I` - просмотр только НЕ установленных портов; -- `-b` - просмотр только битых портов. - -В том случае, если опции `list` не передан никакой из выше перечисленных ключей, -то будет выведен список _всех_ портов БЕЗ сортировки или цветового выделения. +**Дополнительные ключи:** -> **Note** +- `--get-deps` - получить информацию о зависимостях порта. -> Здесь были показаны только основные сведения. Для просмотра более подробной -> информации обо всех возможностях cport, пожалуйста, обратитесь к -> [**документации**](docs/README.md). +### Вывод списка портов -## Установка cport в Calmira GNU/Linux(-libre) - -Для установки нужны следующие зависимости: - -- `base/sqlite3` - `cport` использует базу данных SQLite3 для хранения - информации о портах; -- `base/pymo/wget` - используется для скачивания архивов с исходным кодом ПО. - -### Совместимость - -Совместимо со следующими релизами Calmira: - -- v2.0; - -## ROADMAP - -- [x] Разработка основных методов для установки порта, его удаления и просмотра - о нём информации, а также обновления системы портов; - - [x] build and install ports; - - [x] remove ports; - - [x] information about ports; - - [x] update port system; - - [x] update software; -- [x] Разработка методов для диагностики системы портов: - - [x] list software; - - [ ] checking port system (port linter); - - [x] rebuild database; - -## Документация - -На данный момент ведутся работы над [документацией](docs/README.md) по `cport`, -однако они ещё не закончены. Вы можете внести свой вклад в документирование -этого программного обеспечения, то сделайте форк этого репозитория, напишите -нужные вам страницы в формате Markdown и отправьте нам Merge Request с -изменениями. Либо просто оставьте Issue о необходимости добавления какой-либо -страницы документации. Кроме того, нужно написать страницы документации man, так -как Manual Pages - это основной способ хранения и передачи документации в -UNIX-системах. - -## Стек технологий - -- **ЯП:** Python; -- **БД:** SQLite3; -- **ОС:** Calmira GNU/Linux-libre. - -## Лицензия +``` +cport list [-i] +``` -Программное обеспечение распространяется под лицензией GNU GPLv3. +**Дополнительные ключи:** -![](pic/cport1.png) +- `--installed`, `-i` - вывод только установленных в систему портов. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index e8034f12a72e4cab4a80cd87650de5d302039b41..0000000000000000000000000000000000000000 --- a/TODO.md +++ /dev/null @@ -1,3 +0,0 @@ -# Обновление [ ] - -Не обрабатывается ситуация, когда отсутствует файл `/etc/calm-release` diff --git a/bs_test_conf.toml b/bs_test_conf.toml new file mode 100644 index 0000000000000000000000000000000000000000..ab049019c4be5e96d2f45c0b5195dcb6c7ccee03 --- /dev/null +++ b/bs_test_conf.toml @@ -0,0 +1,25 @@ +[settings] +build_dir = "каталог со сборочными инструкциями для конкретного этапа сборки" +path = "список директорий для переменной окружения PATH" +calm = "директория, в которой будет собираться система" +calm_tgt = "информация для кросс-компилятора" +build_threads = 6 +src_dir = "директория, в которую будет скачан исходный код необходимых компонентов" + +[packages] +required = [ + "список", + "необходимых", + "для сборки", + "пакетов", + "в указанном здесь", + "порядке" +] +optional = [ + "список", + "опциональных (необязательных)", + "для сборки", + "пакетов", + "в указанном здесь", + "порядке" +] diff --git a/cport_cli/Cargo.toml b/cport_cli/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..c5f87b7beaa266eb2b7722a92b8fa4455a08d7bc --- /dev/null +++ b/cport_cli/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cport_cli" +version = "2.0.0" +edition = "2021" +description = "Command Line Interface (CLI) for libcport" + +[dependencies] +anyhow = "1.0.69" +clap = { version = "4.1.7", features = ["derive"] } +colored = "2.0.0" +cursive = "0.20.0" +libcport = { version = "2.0.0", path = "../libcport" } diff --git a/cport_cli/src/bs.rs b/cport_cli/src/bs.rs new file mode 100644 index 0000000000000000000000000000000000000000..8b65881c14839e066ae0b4c86505e8488ff7d6c3 --- /dev/null +++ b/cport_cli/src/bs.rs @@ -0,0 +1,15 @@ +use anyhow::Result; +use std::path::Path; + +use crate::bs; +use crate::ui::formats::msg; + +pub fn build_pkgs>(mode: &P, optional: bool) -> Result { + let build_sys = bs::BuildSystemConfig::parse(mode)?; + + if let Err(why) = build_sys.build_pkgs(optional) { + msg::err(why, true); + } + + Ok(0) +} diff --git a/cport_cli/src/commands.rs b/cport_cli/src/commands.rs new file mode 100644 index 0000000000000000000000000000000000000000..b80a1ffefcf30f1541eeb2abbf9655b67330106b --- /dev/null +++ b/cport_cli/src/commands.rs @@ -0,0 +1,102 @@ +/*! +Arguments parser for cport_cli +*/ + +use clap::{Parser, Subcommand}; +use libcport::config; +use libcport::ui::formats::msg; + +struct PortConfig { + threads: u8, + dest: String, +} + +fn get_port() -> PortConfig { + let config = match config::Config::parse() { + Ok(c) => c, + Err(why) => { + msg::warn( + format!( + "Command line arguments parser error: parse cport config file: {}", + why, + ), + false + ); + msg::warn("Use default parameters", false); + config::Config::default() + } + }; + match config.port { + Some(c) => PortConfig { + threads: c.threads.unwrap_or(1), + dest: c.dest.unwrap_or("/".to_string()), + }, + None => PortConfig { threads: 1, dest: "/".to_string() }, + } +} + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct CommandArgs { + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand, Debug)] +pub enum Commands { + /// Build and install ports to the system + Install { + ports: Vec, + + /// Answer yes to all dialogues about the continuation of cport + #[arg(short)] + yes: bool, + + /// Set threads for build software (0-255) + #[arg(short, long, default_value_t = get_port().threads)] + jobs: u8, + + /// Set installation prefix of the port + #[arg(short, long, default_value_t = get_port().dest)] + destination: String, + + #[arg(short, long, default_value_t = false)] + test: bool, + }, + /// Remove ports from filesystem + Remove { + ports: Vec, + + /// Answer yes to all dialogues about the continuation of cport + #[arg(short)] + yes: bool, + }, + /// Print information about ports + Info { + ports: Vec, + + /// Print information about port dependencies + #[arg(long, short = 'g')] + get_deps: bool, + }, + /// List software in the ports + List { + /// Print only installed ports + #[arg(short, long, default_value_t = false)] + installed: bool, + }, + /// Upgrade ports system + Upgrade { + /// Upgrade mode + #[arg(short, long)] + mode: String + }, + /// Система сборки дистрибутива + BuildSys { + mode: String, + + /// Собирать ли опциональное ПО + #[arg(short, long, default_value_t = false)] + optional_pkgs: bool, + } +} \ No newline at end of file diff --git a/cport_cli/src/formats.rs b/cport_cli/src/formats.rs new file mode 100644 index 0000000000000000000000000000000000000000..8217774695cf15275b7ecd031f0e03be054aebcd --- /dev/null +++ b/cport_cli/src/formats.rs @@ -0,0 +1,49 @@ +/*! +Shared functions for TUI +*/ + +pub mod msg { + use colored::Colorize; + use anyhow::Result; + use std::process::exit; + use std::io::{Write, stdout, stdin}; + + pub fn err(msg: P, exit_pr: bool) { + eprintln!("[{}] cport: {}", "!".bold().red(), msg); + + if exit_pr { + exit(1) + } + } + + pub fn warn(msg: P, werr: bool) { + if werr { + err(&msg, true); + } + eprintln!("[{}] cport: {}", "W".bold().yellow(), msg); + } + + pub fn ok(msg: P) { + println!("[{}] {}", "✓".bold().green(), msg); + } + + pub fn dialog(default_no: bool) -> Result { + print!(":: Continue? "); + if default_no { + print!("[y/N] "); + } else { + print!("[Y/n] "); + } + stdout().flush()?; + + let mut dial = String::new(); + stdin() + .read_line(&mut dial)?; + + if dial.trim() == "n" || dial.trim() == "N" { + Ok(false) + } else { + Ok(true) + } + } +} diff --git a/cport_cli/src/info.rs b/cport_cli/src/info.rs new file mode 100644 index 0000000000000000000000000000000000000000..eda8340445139a844f8f95325c9275f89a12887c --- /dev/null +++ b/cport_cli/src/info.rs @@ -0,0 +1,97 @@ +/*! +Получает информацию о ПО и выводит её в stdout +*/ + +use libcport::port::Port; +use libcport::database; + +use colored::Colorize; +use anyhow::Result; + +pub enum Mode { + Install, + Remove, +} + +pub fn pre(port: &Port, mode: Mode) -> Result<()> { + println!("{}: {}", "Selected port".bold(), &port.name.bold().blue()); + let p_info = port.parse_conf()?; + let p_size = p_info.package.usage; + + let msg = match mode { + Mode::Install => "Total installation size", + Mode::Remove => "Total removal size", + }; + + println!( + "{}: {} {}", msg.bold(), + p_size, "MB".blue(), + ); + + Ok(()) +} + +/// Print information about port +/// +/// Usage: `[0] info PORT1 PORT2 ... PORT_N` +pub fn package(port: &Port) -> Result<()> { + println!( + "{}: {}", + "Information about port".bold().blue(), + &port, + ); + + let p_info = port.parse_conf()?; + let package = &p_info.package; + + let conn = database::connection_db()?; + let prts = database::get(&conn, port)?; + + println!("{:>11}: {}", "name".bold(), &package.name); + println!("{:>11}: {}", "description".bold(), &package.description); + println!("{:>11}: {}", "priority".bold(), &package.priority); + println!("{:>11}: {} MB", "usage".bold(), &package.usage); + println!( + "{:>11}: {}\n", + "installed".bold(), + if prts.is_empty() { "false" } else { "true" } + ); + + println!("{}", "Дополнительная информация:".bold()); + println!("Port section ('[port]'): {:#?}", &p_info.port); + + // println!("{:#?}", prts); + + Ok(()) +} + +fn print_deps(deps: &Option>, header: &str) { + if let Some(d) = deps { + println!("{:>11}: {:?}", header.bold(), d); + } +} + +/// Pring information about port from `deps` section +/// +/// Usage: `[0] info PORT1 PORT2 ... PORT_N --get-deps` +pub fn deps(port: &Port) -> Result<()> { + let p_info = port.parse_conf()?; + + let deps = match &p_info.deps { + Some(d) => d, + None => return Ok(()), + }; + + println!( + "{} {}", + &port.name.blue(), + "port dependencies".bold(), + ); + + print_deps(&deps.required, "required"); + print_deps(&deps.recommend, "recommend"); + print_deps(&deps.optional, "optional"); + println!(" "); + + Ok(()) +} diff --git a/cport_cli/src/install.rs b/cport_cli/src/install.rs new file mode 100644 index 0000000000000000000000000000000000000000..99cd93b8d34157ab20e4fde64e4d218d570ac981 --- /dev/null +++ b/cport_cli/src/install.rs @@ -0,0 +1,89 @@ +use anyhow::Result; +use std::path::Path; +use std::process::exit; + +use libcport::install; +use libcport::database; +use libcport::port::Port; + +use crate::info; +use crate::formats::msg; + +use cursive::views::TextView; +use cursive::views::Dialog; +use cursive::views::Panel; +use cursive::views::LinearLayout; +use cursive::traits::*; +use cursive::event::Key; + +pub fn install_port(port: &Port, yes: bool, jobs: u8, dest: &str, test: bool) -> Result { + if !port.is_exist { + msg::err(format!("Port '{}' not found!", &port.name), true); + } + + if let Err(why) = info::pre(port, info::Mode::Install) { + msg::err(format!("Prepare building error: {why}"), true); + } + + if !yes && !msg::dialog(false).unwrap() { + exit(0); + } + + match install::get_file(port) { + Ok(f) => msg::ok( + format!("Source code downloaded to: {f}"), + ), + Err(why) => msg::err( + format!("Download error: {why}"), + false + ), + } + + let mut code = install::run_install_script( + port, jobs, dest, test + ).unwrap_or(0); + + let mut conn = database::connection_db()?; + if let Err(why) = database::add(&mut conn, port, dest) { + msg::err( + format!("Adding a port to the database: {why}"), + false, + ); + code = 1; + } + show_message_content(port).unwrap(); + + Ok(code) +} + +/// Отображает содержимое файла `message` в случае его наличия +pub fn show_message_content(port: &Port) -> Result<()> { + let fname = Path::new(&port.path).join("message"); + + if fname.exists() { + let mut scr = cursive::default(); + scr.add_global_callback(Key::Enter, |s| s.quit()); + scr.add_global_callback(Key::Esc, |s| s.quit()); + + let fname_cont = std::fs::read_to_string(fname)?; + + let fname_widget = Panel::new( + TextView::new(fname_cont).scrollable(), + ); + let dialog_msg = TextView::new( + format!("The following message was received from port {}:", &port), + ); + + let window_layout = LinearLayout::vertical() + .child(dialog_msg) + .child(fname_widget); + let win = Dialog::around(window_layout) + .title(format!("Message from '{}'", &port.name)) + .button("Ok", |s| s.quit()); + + scr.add_layer(win); + scr.run(); + } + + Ok(()) +} diff --git a/cport_cli/src/lib.rs b/cport_cli/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..75212477ac541a116d6f39044a73b326ce105840 --- /dev/null +++ b/cport_cli/src/lib.rs @@ -0,0 +1,117 @@ +//! Command line interface for `cport_kernel` + +pub mod formats; +pub mod info; +pub mod install; +pub mod remove; +pub mod list; +pub mod update; +// pub mod bs; + +pub mod commands; + +use libcport::metadata::Metadata; +use libcport::consts::PORTS_DIR; +use libcport::port::Port; +use std::process::exit; +use std::path::Path; +use formats::msg; + +pub fn unimplemented_msg() { + msg::err("Error! The functionality you are trying to call is not currently implemented. However, if you still managed to refer to it, it will be done soon ;-)", true); +} + +pub fn install(ports: &Vec, yes: bool, jobs: u8, dest: &str, test: bool) { + for port in ports { + let port = Port::new(port); + + if !port.check_exists() { + msg::err( + format!("[install] check error: port '{port}' is damaged or missing!"), + true, + ); + } + + let code = match install::install_port(&port, yes, jobs, dest, test) { + Ok(c) => c, + Err(why) => { + msg::err(format!("[install] build error: {why}"), false); + 1 + } + }; + + if code != 0 { + msg::err( + format!( + "[install] error: port '{}' returned an exit code other than 0 ({})", + &port.name, code, + ), true + ); + } else { + msg::ok("Installation complete"); + } + } +} + +pub fn remove(ports: &Vec, yes: bool) { + for port in ports { + let port = Port::new(port); + + if !port.check_exists() { + msg::err( + format!("[remove] check error: port '{port}' is damaged or missing!"), + true, + ); + } + + if let Err(why) = remove::remove_port(&port, yes) { + msg::err(format!("[remove] error: {why}"), true); + } + } +} + +pub fn info(ports: &Vec, deps: bool) { + for port in ports { + let port = Port::new(port); + + if !port.check_exists() { + msg::err( + format!("[info] check error: port '{}' is damaged or missing!", &port.name), + true + ); + } + + if let Err(why) = info::package(&port) { + msg::err(format!("[info] error: {why}"), true); + } + + if deps { + if let Err(why) = info::deps(&port) { + msg::err(format!("[info] error: {why}"), true); + } + } + } +} + +pub fn list(installed: bool) { + if installed { + if let Err(why) = list::print_installed() { + msg::err( + format!("[list] Database error: {why}"), true, + ); + } + } else { + let md = match Metadata::parse( + &Path::new(&PORTS_DIR).join("metadata.toml") + ) { + Ok(m) => m, + Err(why) => { + msg::err( + format!("[list] Metadata parsing error: {why}"), false, + ); + exit(2) + } + }; + list::print_all(&md); + } +} diff --git a/cport_cli/src/list.rs b/cport_cli/src/list.rs new file mode 100644 index 0000000000000000000000000000000000000000..b419a6808fdd41d009fdcdebdc5dfcae715de1f6 --- /dev/null +++ b/cport_cli/src/list.rs @@ -0,0 +1,32 @@ +/*! +List software +*/ + +use anyhow::Result; +use libcport::database; +use libcport::metadata::Metadata; + +pub fn print_all(md: &Metadata) { + let ports = &md.port_sys.ports; + for port in ports { + println!("{port}"); + } +} + +pub fn print_installed() -> Result<()> { + let db = database::connection_db()?; + + /* + * Ранее мы получали список всех портов из метаданных и + * последовательно проверяли наличие порта в базе данных. + * Но такой способ медленнее, чем то, что есть сейчас. + */ + let ports = database::get_all(&db)?; + + for port in ports { + println!( + "{}:{}", port.name, port.version, + ); + } + Ok(()) +} diff --git a/cport_cli/src/main.rs b/cport_cli/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..0bb464cbc173d33758ef688babebecaab830462b --- /dev/null +++ b/cport_cli/src/main.rs @@ -0,0 +1,50 @@ +use clap::Parser; +use libcport::utils; + +use cport_cli::commands; +use libcport::ui::formats::msg; +use cport_cli::*; + +fn main() { + let args = commands::CommandArgs::parse(); + + match &args.command { + commands::Commands::Install {ports, yes, jobs, destination, test} => { + if !utils::check_root_user() { + msg::err("Permission denied!", true); + } + + install(ports, *yes, *jobs, destination, *test); + }, + + commands::Commands::Remove { ports, yes } => { + if !utils::check_root_user() { + msg::err("Permission denied!", true); + } + + remove(ports, *yes); + }, + + commands::Commands::Info {ports, get_deps} => { + info(ports, *get_deps); + }, + + commands::Commands::List{ installed } => { + list(*installed); + }, + + // commands::Commands::RebuildDb { } => { + // println!("Rebuild"); + // }, + + // commands::Commands::Upgrade { .. } => { + // cport::update::get_difference().unwrap(); + // }, + + // commands::Commands::BuildSys {mode, optional_pkgs} => { + // println!("Work mode: {mode}, build optional: {optional_pkgs}"); + // }, + + _ => unimplemented_msg(), + } +} diff --git a/cport_cli/src/remove.rs b/cport_cli/src/remove.rs new file mode 100644 index 0000000000000000000000000000000000000000..1a62d5ec44b79ee1df2b200796a8b18ce6d6b269 --- /dev/null +++ b/cport_cli/src/remove.rs @@ -0,0 +1,59 @@ +/*! +Remove software from the filesystem and database +*/ + +use anyhow::Result; + +use crate::info; +use libcport::remove; +use libcport::database; +use libcport::port::Port; +use libcport::ui::formats::msg; + +pub fn remove_port(port: &Port, yes: bool) -> Result<()> { + if !port.is_exist { + msg::err(format!("Port '{}' not found!", &port.name), true); + } + + if let Err(why) = info::pre(port, info::Mode::Remove) { + msg::err(format!("Prepare removing error: {why}"), true); + } + + if !yes && !msg::dialog(true)? { + return Ok(()); + } + + let mut db = database::connection_db()?; + let port_inf = database::get(&db, port)?; + + if port_inf.is_empty() { + msg::err( + format!("Port {} is not in the database!", port), + true, + ); + } + + let port_dest_prefix = &port_inf[0].prefix; + + if port_inf.len() > 1 { + msg::err( + format!( + "Ambiguous db entry! There are {} records in the database containing {:#?}!", + port_inf.len(), &port_inf, + ), true, + ); + } else if port_inf.is_empty() { + msg::err( + format!("Port {} is not in the database!", &port), true, + ); + } + + match remove::remove_files(port, port_dest_prefix) { + Ok(result) => { + if result { database::remove(&mut db, port)?; } + }, + Err(why) => msg::err(format!("[ui][remove] {why}"), true), + } + + Ok(()) +} diff --git a/cport_cli/src/update.rs b/cport_cli/src/update.rs new file mode 100644 index 0000000000000000000000000000000000000000..d1d648345cc3f312327ea4aa429a9c6369a27777 --- /dev/null +++ b/cport_cli/src/update.rs @@ -0,0 +1 @@ +// use crate::update; diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 9c915625e282d9ac14f2ce0a7bdacf5d1f477a07..0000000000000000000000000000000000000000 --- a/docs/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# cport - -cport - это менеджер системы портов дистрибутива Calmira GNU/Linux-libre. В его -возможности входит установка и удаление портов, просмотр информации о -программном обеспечении, которое присутствует в портах, а также обновление -системы портов. Утилита написана на Python и использует SQLite3 для хранения -информации о портах. - -Этот менеджер системы портов решает многие проблемы в управлении программным -обеспечении в дистрибутиве Calmira GNU/Linux(-libre). В системе долгое -время не было утилиты для управления ПО, которая бы работала стабильно и -надёжно. Конечно, cport'у далеко до настоящей стабильности, но сейчас ведутся -работы по её повышению. - -## TODO - -- [x] Написание документации cport: - - [x] [Установка программного обеспечения;](install.md); - - [x] [Удаление программного обеспечения](remove.md); - - [x] [Просмотр информации о ПО](info.md); - - [x] [Обновление системы портов](update_ports.md); - - [x] [Обновление программного обеспечения](upgrade.md); -- [x] [Написание документации системы портов](https://gitlab.com/calmiralinux/cabs/Ports): - - [x] Краткий обзор системы портов; - - [x] Создание своего порта; diff --git a/docs/info.md b/docs/info.md deleted file mode 100644 index 8647d624263aedcb3ef757b5df6a10ea0fdb5a90..0000000000000000000000000000000000000000 --- a/docs/info.md +++ /dev/null @@ -1,35 +0,0 @@ -# Просмотр информации о портах - -Просмотр информации о программном обеспечении, которое присутствует в портах - -это очередная проблема, которую решает cport. Если ранее пользователю -приходилось запоминать, какая программа установлена, какой версии, а, самое -главное, для чего она нужна, то cport знает это для всего программного -обеспечения, которое представлено в портах. - -## Принцип работы - -В директории с системой портов содержится файл `port.toml`, который содержит 3 -секции: - -1. `package` - содержит информацию о пакете; -2. `deps` - содержит информацию о зависимостях этого пакета; -3. `port` - информация, необходимая только для cport - это ссылка для скачивания - архива с исходным кодом и MD5 сумма для проверки целостности скачивания. - -Выводятся сведения из секций `package` и `deps`. - -## Использование - -```bash -# Использование ключа '--info': -cport --info editors/gvim - -# Использование ключа '-I': -cport -I editors/gvim -``` - -При желании вы можете просматривать информацию о нескольких портах: - -```bash -cport --info editors/gvim base/acl base/binutils -``` diff --git a/docs/install.md b/docs/install.md deleted file mode 100644 index 852f6710f705a886d7305d7bfd9a207611beef58..0000000000000000000000000000000000000000 --- a/docs/install.md +++ /dev/null @@ -1,63 +0,0 @@ -# Установка портов - -Одна из самых важных функций, которую в значительной степени упрощает и -автоматизирует cport - это сборка пакета из исходного кода и установка его в -систему. Для корректной сборки пакета иногда приходится вводить достаточно -большое число команд в терминал, и далеко не все из этих команд короткие и/или -простые. cport же это всё упрощает тем, что все необходимые для сборки действия -записаны в BASH-скрипт, являющийся инструкцией по сборке. При желании или при -необходимости пользователь может отредактировать эти сборочные инструкции в -зависимости от целей и запросов. - -Для установки порта используется ключ `--install` (сокращённая версия: `-i`). -Далее этому ключу указывается порт или несколько портов, которые необходимо -собрать. - -Сначала менеджер системы портов выведет подробную информацию о программном -обеспечении, которое хочет собрать и установить пользователь, а потом запросит -подтверждение действий (нужно ввести `y` или `n` и нажать `Enter` для -продолжения или прекращения действий соотв.). В том случае, если пользователь -ответил утвердительно, начнётся скачивание архива с исходным кодом порта, его -распаковка (на данный момент поддерживаются только архивы tar) и выполнение -сборочных инструкций. - -Например: - -```bash -cport -i xorg/gtk3 editors/gvim -``` - -Эта команда установит порты `xorg/gtk3` и `editors/gvim`. - -Подтверждение вызывается для каждого действия. Т.е. если вы, например, указали -сборку одного порта, то у пользователя будет запрошено лишь одно подтверждение -действий. Если пользователь указал сборку десяти портов, то запрос о продолжении -действий будет задан десять раз. - -Если пользователь по каким-то причинам не сможет ответить на запросы, то он -может отключить их, указав ключ `--yes` (сокращённая версия: `-y`). В таком -случае запрос о продолжении действий не будет задан ни разу. Считайте, что -`cport` будет автоматически отвечать утвердительно на все запросы. - -Например: - -``` -cport -i editors/gvim -``` - -![](pic/cport1.png) - -Здесь мы видим, что после того, как была выведена информация о порте, cport -запрашивает пользователя, продолжить ли ему работу или нет. В том случае, если -пользователь введёт `y` и нажмёт `Enter`, то cport скачает архив с исходным -кодом порта, распакует его и приступит к сборке. - -``` -cport --yes -i editors/gvim -``` - -![](pic/cport2.png) - -Во втором случае мы указали ключ `--yes` (или его сокращённую версию `-y`). -Тогда cport не будет ничего запрашивать у пользователя и сразу присупит к -сборке. diff --git a/docs/pic/cport1.png b/docs/pic/cport1.png deleted file mode 100644 index acf381b5db00d5883ea8673be73447f3af9d14d6..0000000000000000000000000000000000000000 Binary files a/docs/pic/cport1.png and /dev/null differ diff --git a/docs/pic/cport2.png b/docs/pic/cport2.png deleted file mode 100644 index a0578fcbc8f6a9bf7915a121951d5eba827d067b..0000000000000000000000000000000000000000 Binary files a/docs/pic/cport2.png and /dev/null differ diff --git a/docs/remove.md b/docs/remove.md deleted file mode 100644 index 2c5513f3dcf4cfa0e01423bef3b170a5873956d5..0000000000000000000000000000000000000000 --- a/docs/remove.md +++ /dev/null @@ -1,48 +0,0 @@ -# Удаление программного обеспечения - -Удаление программного обеспечения - это одна из самых важных проблем, которую -решает cport. Если с установкой всё легко, то с удалением программного -обеспечения могут возникать всевозможные проблемы, так как на жёстком диске ПК -придётся хранить архив с исходным кодом пакета, в котором содержится `Makefile` -или какие-то подобные файлы, содержащие инструкции как по сборке и установке -пакета, так и для его удаления из системы. Но тут есть один маленький нюанс: не -все разработчики пакетов добавляют инструкции по удалению своего программного -обеспечения из системы. Тогда пользователю придётся решать эту проблему -самостоятельно: находить все файлы, которые принадлежат нужному пакету, и -вручную удалять их. - -Но что если пользователь не удалит какие-то файлы до конца? Что если -пользователь удалит не те файлы? Те, которые принадлежат другому пакету? В таком -случае он может вообще сломать либо какое-то другое ПО, либо вообще всю систему. -Для того, чтобы обезопасить пользователя, и была реализована функция удаления ПО -из системы средствами cport, а не средствами, про которые многие разработчики -забывают или принципиально не добавляют. - -## Принцип работы - -В директории с нужным портом содержится файл `files.list`. Он как раз содержит -список того, что требуется удалить. cport читает этот файл, разбивает его -содержимое на список (каждый файл в `files.list` указывается с новой строки) и -пофайлово из системы удаляет. - -## Использование cport - -Для удаления пакета с помощью cport используются ключи `--remove` и `-r`: - -```bash -cport -r editors/gvim -``` - -Вы можете указать несколько портов, тогда они будут последовательно удалены из -системы: сначала первый, потом второй и так далее: - -```bash -cport -r editors/gvim editors/vim general/git -``` - -> **Обратите внимание**, что cport удаляет только те порты, которые имеют -> пользовательский статус. Это значит, что все порты из категории `base` удалить -> вы не сможете. - -Кроме того, вы не сможете удалить порт, упоминание о котором отсутствует в базе -данных cport. diff --git a/docs/update_ports.md b/docs/update_ports.md deleted file mode 100644 index 6c9bb3fb2d1be1c60e8c19daf09496955fa22b96..0000000000000000000000000000000000000000 --- a/docs/update_ports.md +++ /dev/null @@ -1,36 +0,0 @@ -# Обновление системы портов - -Система портов всё время обновляется: туда добавляется новое программное -обеспечение, обновляется уже существующее и удаляется устаревшее. Для упрощения -и автоматизации установки обновления именно системы портов в cport используется -ключ `--update` или его сокращённая версия: `-u`. Версия системы портов -скачивается из той ветки, которая указана в параметре `repo.branch` файла -`/etc/cport.toml`. - -Использование: - -```bash -cport --update -# Или: -cport -u -``` - -В том случае, если обновлений нет, будет выведено следующее сообщение: - -``` -==> Downloading the ports system... -100% [......................................................................] 37532 / 37532 -==> Unpacking ports in '/usr/src'... -==> Generating list of changes... -[!] No updates. -``` - -В противном случае будут выведены таблицы, показывающие список изменений в новой -версии портов и в той, которая установлена в системе: - -- Какие порты были удалены и/или добавлены; -- Какие порты были обновлены (таблица из трёх колонок - название, старая и новая - версии). - -> Для работы модуля обновлений системы портов требуется, чтобы размер терминала -> был не менее 80 колонок (символов в ширину). diff --git a/docs/upgrade.md b/docs/upgrade.md deleted file mode 100644 index 8ec3534417731fe9188daa1f028c6383e6af6200..0000000000000000000000000000000000000000 --- a/docs/upgrade.md +++ /dev/null @@ -1,36 +0,0 @@ -# Обновление программного обеспечения - -cport поддерживает двухрежимное обновление программного обеспечения до новой -версии (в случае, если это ПО, конечно, установлено в систему). Режимы `soft` и -`hard`. - -## `soft` - -В первом случае новая версия порта просто устанавливается поверх старой и в базе -данных установленного ПО обновляются сведения о порте. - -## `hard` - -Порт удаляется из базы данных и из системы, после чего устанавливается новая -версия порта. Данный функционал не рекомендуется использовать для системного ПО -или компиляторов. По сути, ПО просто переустанавливается. - -## Проведение обновления ПО - -Для начала актуализируйте систему портов: - -```bash -cport -u -``` - -В выводе будут указаны все порты, в которых произошли изменения. Если порт, -который вы хотите обновить, присутствует в списке, то используйте опцию `-U` для -обновления: - -```bash -cport -U [список портов, которые требуется обновить до новой версии] -``` - -Если версия установленного порта не соответствует версии присутствующего в -системе портов, то начнётся обновление. В противном случае обновление завершится -с ошибкой из-за совпадения версий портов. diff --git a/libcport/Cargo.toml b/libcport/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..1fbb6c6b91bdca14157d8c32ea5da54c32263165 --- /dev/null +++ b/libcport/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "libcport" +version = "2.0.0" +edition = "2021" +authors = ["Michail Krasnov "] +license = "GPL-3.0-or-later" +description = "Library for managing software in Calmira GNU/Linux-libre" + +[dependencies] +anyhow = "1.0.69" +colored = "2.0.0" +futures = "0.3.26" +futures-util = "0.3.28" +indicatif = "0.17.3" +nix = "0.26.2" +reqwest = { version = "0.11.18", features = ["stream"] } +rusqlite = "0.28.0" +serde = { version = "1.0.152", features = ["derive"] } +tokio = { version = "1.25.0", features = ["full"] } +toml = "0.7.2" diff --git a/libcport/src/bs.rs b/libcport/src/bs.rs new file mode 100644 index 0000000000000000000000000000000000000000..ab5c1e3ec03879fadfd82f5955ecc15a84c4ffed --- /dev/null +++ b/libcport/src/bs.rs @@ -0,0 +1,111 @@ +/*! +Модуль с функциями для автоматизации сборки дистрибутива Calmira GNU/Linux-libre из исходного кода + +## Описание + +Сборка системы поделена на несколько определяемых разработчиком этапов. +На каждом этапе собирается своё ПО. Для этих этапов определены свои собственные +параметры, включая значение переменной `PATH`, `CALM`. Должна присутствовать +поддержка изменения числа потоков сборки. Для каждого этапа указывается свой +список ПО для сборки в нужном порядке. + +`cport` читает этот список и последовательно исполняет указанные в нём +сборочные инструкции ПО, передавая им указанные в конфиге параметры в качестве +переменных окружения. + +## Конфигурация + +```toml +[settings] +build_dir = "каталог со сборочными инструкциями для конкретного этапа сборки" +path = "список директорий для переменной окружения PATH" +calm = "директория, в которой будет собираться система" +calm_tgt = "информация для кросс-компилятора" +build_threads = 666 # число_потоков_сборки +src_dir = "директория, в которую будет скачан исходный код необходимых компонентов" + +[packages] +required = [ + "список", + "необходимых", + "для сборки", + "пакетов", + "в указанном здесь", + "порядке" +] +optional = [ + "список", + "опциональных (необязательных)", + "для сборки", + "пакетов", + "в указанном здесь", + "порядке" +] +``` +*/ + +use serde::Deserialize; +use anyhow::Result; +use std::{fs, path::Path}; +use toml; + +/// Конфигурационный файл этапов сборки системы +#[derive(Debug, Deserialize)] +pub struct BuildSystemConfig { + pub settings: SettingsSection, + pub packages: PackagesSection, +} + +/// Параметры сборки программного обеспечения +#[derive(Debug, Deserialize)] +pub struct SettingsSection { + /// Каталог со сборочными инструкциями для конкретного этапа сборки + pub build_dir: String, + + /// Список директорий для переменной окружения `PATH` + /// + /// **Например:** `/bin:/sbin:/usr/bin:/usr/sbin:/mnt/calm/tools/bin` + pub path: String, + + /// Директория, в которой будет собираться система + pub calm: String, + + /// Информация для кросс-компилятора + pub calm_tgt: String, + + /// Директория, в которую будет скачан исходный код необходимых компонентов + pub src_dir: String, + + /// Число потоков сборки ПО + pub build_threads: u8, +} + +/// Список и порядок сборки программного обеспечения +#[derive(Debug, Deserialize)] +pub struct PackagesSection { + /// Список необходимых для исполнения сборочных инструкций + pub required: Vec, + + /// Список необязательных для исполнения сборочных инструкций + /// + /// **Внимание:** инструкции отсюда исполняются **после** того, как закончилось + /// исполнение необходимых инструкций и только при условии, что пользователь + /// **явно указал** исполнение необязательных инструкций. + pub optional: Vec, +} + +impl BuildSystemConfig { + /// Parse build system's configuration file + /// + /// **Example:** + /// + /// ``` + /// use cport::bs::BuildSystemConfig; + /// let conf = BuildSystemConfig::parse("./bs_test_conf.toml").unwrap(); + /// println!("path to distro: {}", &conf.settings.calm); + /// ``` + pub fn parse>(conf_file: P) -> Result { + let conf = fs::read_to_string(conf_file)?; + Ok(toml::from_str(&conf)?) + } +} diff --git a/libcport/src/config.rs b/libcport/src/config.rs new file mode 100644 index 0000000000000000000000000000000000000000..2d4866ea604d33cc4fcf72650a03c84cfc3f96d4 --- /dev/null +++ b/libcport/src/config.rs @@ -0,0 +1,68 @@ +/*! +Модуль для парсинга конфигурационных файлов `cport` + +## Структура конфига + +```ignore +[base] +url = "URL до репозитория с системой портов" +branch = "ветка" + +# В секции `port` содержатся стандартные параметры, +# которые могут быть быстро изменены на время +# установки конкретных портов путём передачи cport +# аргументов `-j` и `-d`. +[port] +threads = число потоков сборки +dest = куда устанавливать порт +``` +*/ + +use serde::Deserialize; +use toml; + +use anyhow::Result; +use std::fs; +use crate::consts; + +#[derive(Debug, Deserialize)] +pub struct Config { + pub base: BaseConfig, + pub port: Option, +} + +#[derive(Debug, Deserialize)] +pub struct BaseConfig { + pub url: String, + pub branch: String, +} + +#[derive(Debug, Deserialize)] +pub struct PortConfig { + pub threads: Option, + pub dest: Option, +} + +impl Config { + /// Parse `/etc/cport.conf` file + pub fn parse() -> Result { + let config_content = fs::read_to_string(consts::CONFIG)?; + let config_data: Self = toml::from_str(&config_content)?; + Ok(config_data) + } +} + +impl Default for Config { + fn default() -> Self { + Self { + base: BaseConfig { + url: "https://gitlab.com/calmiralinux/cabs/Ports/-/raw/".to_string(), + branch: "testing".to_string(), + }, + port: Some(PortConfig { + threads: Some(1), + dest: Some("/".to_string()), + }), + } + } +} diff --git a/libcport/src/consts.rs b/libcport/src/consts.rs new file mode 100644 index 0000000000000000000000000000000000000000..deceada5c2c01b144c1776535297a3d7e98033dc --- /dev/null +++ b/libcport/src/consts.rs @@ -0,0 +1,9 @@ +//! Constants for `cport_kernel` + +pub const PORTS_DIR: &str = "/usr/ports"; +pub const TMP_PORTS_DIR: &str = "/var/cache/cport/ports"; +pub const SRC_DIR: &str = "/usr/src"; +pub const DB_FILE: &str = "/var/lib/Calmira/software.db"; +pub const INTERPRETER: &str = "/bin/bash"; +pub const DEFAULT_PORT: &str = "general/vagina"; +pub const CONFIG: &str = "/etc/cport.conf"; diff --git a/libcport/src/database.rs b/libcport/src/database.rs new file mode 100644 index 0000000000000000000000000000000000000000..23a966fe69fb146f1398a34c4d599bc94fce4ff7 --- /dev/null +++ b/libcport/src/database.rs @@ -0,0 +1,164 @@ +/*! +Модуль для работы с базой данных cport. + +## Предназначение БД + +БД хранит сведения об установленном в операционную систему +ПО и о ПО, которое присутствует в системе портов. + +## Usage + +``` +use cport::database; +use cport::port::Port; + +let mut conn = database::connection_db().unwrap(); +let port = Port::new("general/vagina"); + +// Add port +// | connection | port | install +// | | name | prefix +database::add(&mut conn, &port, "/"); + +// Remove port +database::remove(&mut conn, &port); +``` + +## Table + +### `installed` + +| name | version | usage | prefix | +*/ + + +use anyhow::Result; +use std::path::Path; +use rusqlite::Connection; + +use crate::port::Port; +use crate::consts::DB_FILE; + +#[derive(Debug)] +pub struct DatabaseTable { + pub name: String, + pub version: String, + pub usage: f32, + pub prefix: String, +} + +/// Create database +fn create_db() -> Result<()> { + let c = Connection::open(DB_FILE)?; + c.execute( + "CREATE TABLE installed_ports (name TEXT, version TEXT, usage REAL, prefix TEXT)", + [] + )?; + + Ok(()) +} + +/// Open `/var/lib/Calmira/software.db` +pub fn connection_db() -> Result { + let pth = Path::new(&DB_FILE); + if !pth.exists() { create_db()?; } + Ok(Connection::open(pth)?) +} + +/// Add port information to the database +pub fn add(conn: &mut Connection, port: &Port, prefix: &str) -> Result<()> { + let p_conf = port.parse_conf()?; + let tx = conn.transaction()?; + + tx.execute( + "INSERT INTO installed_ports VALUES (?1, ?2, ?3, ?4)", + [&port.name, &p_conf.package.version, &p_conf.package.usage.to_string(), prefix], + )?; + + tx.commit()?; + + Ok(()) +} + +/// Remove port information from the database +pub fn remove(conn: &mut Connection, port: &Port) -> Result<()> { + let tx = conn.transaction()?; + tx.execute("DELETE FROM installed_ports WHERE name = (?1)", [&port.name])?; + tx.commit().map_err(|why| anyhow::anyhow!("{why}")) +} + +/// Get information from database +pub fn get(conn: &Connection, port: &Port) -> Result> { + let mut stmt = conn.prepare( + &format!("SELECT * FROM installed_ports WHERE name = '{}'", &port.name), + )?; + + let ports = stmt.query_map([], |row| { + Ok( + DatabaseTable { + name: row.get(0)?, + version: row.get(1)?, + usage: row.get(2)?, + prefix: row.get(3)?, + } + ) + })?; + + // С `ports` работать неудобно. Проще обернуть всё + // в структуру и вернуть вектор со всеми структурами + let mut rows = Vec::new(); + for port in ports { + rows.push(port?); + } + + Ok(rows) +} + +/// Get ALL information from database +pub fn get_all(conn: &Connection) -> Result> { + let mut stmt = conn.prepare( + "SELECT * FROM installed_ports", + )?; + let ports = stmt.query_map([], |row|{ + Ok( + DatabaseTable { + name: row.get(0)?, + version: row.get(1)?, + usage: row.get(2)?, + prefix: row.get(3)?, + } + ) + })?; + + let mut rows = Vec::new(); + for port in ports { + rows.push(port?); + } + + Ok(rows) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::consts::DEFAULT_PORT; + + #[test] + fn check_connection() { + assert!(connection_db().is_ok()); + } + + /* + * NOTE: проводится на сгенерированной БД! + */ + #[test] + fn check_get() { + let conn = connection_db().unwrap(); + let ports = get( + &conn, + &Port::new(DEFAULT_PORT), + ).unwrap(); + + assert!(!ports.is_empty()); + } +} diff --git a/libcport/src/install.rs b/libcport/src/install.rs new file mode 100644 index 0000000000000000000000000000000000000000..f12d8c23423c1cf324ac07352cd8456937b414cf --- /dev/null +++ b/libcport/src/install.rs @@ -0,0 +1,144 @@ +/*! +Модуль для установки портов в систему + +## Описание процессов, реализуемых в данном модуле + +На стороне `cport` выполняется скачивание порта, исполнение его +сборочных инструкций и добавление порта в базу данных программного +обеспечения операционной системы. + +Распаковка архивов с исходным кодом ПО и прочие подобные действия +должны выполняться непосредственно в сборочных инструкциях +порта. Ранее распаковку производил `cport` самостоятельно, однако +для этого требовалось обеспечить поддержку большого числа форматов +сжатия файлов вместо того, чтобы начать наконец разработку +действительно полезных функций. Поэтому начиная с версии +`cport-v2.0` распаковка архивов с исходным кодом ПО производится +в сборочных инструкциях непосредственно. + +## Порядок запуска сборочных инструкций порта + +Сборочные инструкции содержатся в BASH-скрипте `install`. При +исполнении инструкций наследуется окружение BASH, из которого +был запущен `cport`; помимо наследуемых оттуда переменных и +констант к ним добавляются новые переменные: + +- `CPORT_BUILD_THREADS` - число потоков сборки ПО; +- `CPORT_INSTALLATION_PREFIX` - путь до директории, в которую будут скопированы файлы собранного ПО; +- `SRC_DIR` - путь до директории с архивами исходного кода ПО. + +Первые две переменные могут быть изменены путём передачи +`cport` определённых параметров и ключей (`-j` для изменения числа +потоков сборки ПО, `-d` для изменения директории установки ПО). +Не подлежит изменению лишь переменная `SRC_DIR`. +*/ + +use crate::utils; +use crate::consts; +use crate::port::Port; + +use std::process::Command; +use anyhow::Result; +use std::fs::File; +use std::io::Write; +use std::cmp::min; +use std::path::Path; + +use tokio; +use reqwest; +use indicatif::{ProgressBar, ProgressStyle}; +use futures_util::StreamExt; + +/// Download files from remote repositories +/// +/// Returns `String` with path to the downloaded file +#[tokio::main] +pub async fn get_file(port: &Port) -> Result { + let conf = port.parse_conf()?; + + let port_section = match &conf.port { + Some(section) => section, + None => return Err(anyhow::anyhow!("PORT section not found")), + }; + let url = match &port_section.url { + Some(u) => u, + None => return Err(anyhow::anyhow!("URL section not found")), + }; + + let res = reqwest::get(url).await?; + let total_size = res.content_length().unwrap(); + + let pb = ProgressBar::new(total_size); + pb.set_style(ProgressStyle::default_bar() + .template( + "{msg} {elapsed} [{bar}] {bytes}/{total_bytes} ETA: {eta}" + )? + .progress_chars("=> ")); + pb.set_message(format!("Download '{}'", &port)); + + let tmp_dir = Path::new(&consts::SRC_DIR); + let mut dest = { + let fname = match &port_section.file { + Some(f) => f, + None => { + let (_, fname) = url + .rsplit_once('/') + .unwrap_or(("", "tmp.bin")); + fname + } + }; + let file_name = tmp_dir.join(fname); + + (File::create(&file_name)?, file_name) + }; + + let mut downloaded: u64 = 0; + let mut stream = res.bytes_stream(); + + while let Some(item) = stream.next().await { + let chunk = item?; + dest.0.write_all(&chunk)?; + let new = min(downloaded + (chunk.len() as u64), total_size); + downloaded = new; + pb.set_position(new); + } + + pb.finish(); + Ok(utils::pth_to_str(dest.1)) +} + +/// Проверяет наличие свободного места на диске перед установкой порта. +/// +/// **Принцип работы:** +/// 1. Получение инф-ции о потреблении порта. +/// 2. Получение инф-ции о свободном в корневом разделе месте +/// 3. Если свободное на диске место меньше потребляемого портом места + 100 Мб, то вернуть `false` +/// 4. В противном случае вернуть `true`. +pub fn check_usage(_port_usage: u64) -> bool { + unimplemented!() +} + +/// Исполняет сборочные инструкции порта +pub fn run_install_script(port: &Port, jobs: u8, prefix: &str, test: bool) -> Result { + let command = Command::new(consts::INTERPRETER) + .arg("-c") + .arg("/usr/share/cport/install.sh") + .env("PORT_DIR", &port.path) + .env("CPORT_BUILD_THREADS", jobs.to_string()) + .env("CPORT_INSTALLATION_PREFIX", prefix) + .env("SRC_DIR", consts::SRC_DIR) + .env("IS_TEST", if test { "YES" } else { "NO" }) + .status()?; + Ok(command.code().unwrap_or(0)) +} + +// #[cfg(test)] +// mod tests { + // use super::*; + + // #[test] + // fn check_run() { + // let run = run_install_script(&Port::new(consts::DEFAULT_PORT), 0, "").unwrap_or(1); + // assert_eq!(0, run); + // } +// } diff --git a/libcport/src/install.sh b/libcport/src/install.sh new file mode 100644 index 0000000000000000000000000000000000000000..a9f10839045f7008b3ebaf508ba44a85f55203f5 --- /dev/null +++ b/libcport/src/install.sh @@ -0,0 +1,94 @@ +#!/bin/bash -e +# (C) 2023 Michail Krasnov + +# Variables: +# $CPORT_BUILD_THREADS +# $CPORT_INSTALLATION_PREFIX +# $SRC_DIR +# $PORT_DIR +# $IS_TEST + +log_msg() { + echo "[ $(date) ] [cprt_inst_provider] [user: $(whoami)] $1" >> /var/log/cport.log +} + +print_msg() { + echo -e "\e[1;33m===>\e[0m $1" + log_msg $1 +} + +print_msg1() { + echo -e " \e[1;32m-->\e[0m $1\n" + log_msg " --> $1" +} + +err_msg() { + echo -e "\n\n\a\e[1;31mERROR:\e[0m \e[1m$1\e[0m" + log_msg "ERROR: $1" +} + +func_msg() { + CODE=$1 + if [ $CODE != 0 ]; then + err_msg "Error with '$2': $CODE code returned!" + exit $CODE + else + print_msg1 "Execution of the '$2' function was successful." + fi +} + +. $PORT_DIR/install + +if [ $ARCHIVE != "none" ]; then + cd $SRC_DIR + print_msg "Extracting the package source code..." + if [ -z "$ARCHIVE" ]; then + tar -xf ${NAME}${VERSION}.* + else + tar -xf $ARCHIVE + fi + + print_msg1 "Successful." + + if [ -z "$BUILD_DIR" ]; then + cd ${NAME}-${VERSION} + else + cd $BUILD_DIR + fi +fi + +print_msg "Executing the 'prepare()' function..." +prepare +CODE=$? +func_msg $CODE "prepare()" + +print_msg "Executing the 'build()' function..." +build +CODE=$? +func_msg $CODE "build()" + +if [ $IS_TEST == "yes" ]; then + print_msg "Executing the 'test()' function..." + test + CODE=$? + func_msg $CODE "test()" +fi + +if grep 'function preinst() {' $PORT_DIR/install; then + print_msg "Executing the 'preinst()' function..." + preinst + CODE=$? + func_msg $CODE "preinst()" +fi + +print_msg "Executing the 'install()' function..." +install +CODE=$? +func_msg $CODE "install()" + +if grep 'function postinst() {' $PORT_DIR/install; then + print_msg "Executing the 'postinst()' function..." + postinst + CODE=$? + func_msg $CODE "postinst()" +fi diff --git a/libcport/src/lib.rs b/libcport/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..c5952b2a50f708a27d56f02f3fc8a4581f6e3f9e --- /dev/null +++ b/libcport/src/lib.rs @@ -0,0 +1,10 @@ +pub mod consts; +pub mod config; +pub mod port; +pub mod utils; +pub mod database; +pub mod install; +pub mod remove; +pub mod metadata; +pub mod update; +pub mod ui; diff --git a/libcport/src/metadata.rs b/libcport/src/metadata.rs new file mode 100644 index 0000000000000000000000000000000000000000..c67ece0adaf7f8b60fffe146648ba5c91d109df0 --- /dev/null +++ b/libcport/src/metadata.rs @@ -0,0 +1,55 @@ +/*! +Parse `/usr/ports/metadata.toml` +*/ + +use std::path::Path; +use serde::Deserialize; +use anyhow::Result; +use std::fs; +use toml; + +#[derive(Deserialize)] +pub struct Metadata { + pub port_sys: PortSys, + pub system: System, +} + +#[derive(Deserialize)] +pub struct PortSys { + pub categories: Vec, + pub ports: Vec, +} + +#[derive(Deserialize)] +pub struct System { + pub release: Vec, +} + +impl Metadata { + /// Parse metadata `file` + pub fn parse>(file: &P) -> Result { + let md_content = fs::read_to_string(file)?; + let md_data: Self = toml::from_str(&md_content)?; + Ok(md_data) + } + + /// Checks if the currently installed distribution + /// release is supported by the ports system + pub fn check_release(&self) -> bool { + unimplemented!() + } +} + +impl Default for Metadata { + fn default() -> Self { + Self { + port_sys: PortSys { + categories: vec!["base/".to_string()], + ports: vec!["base/acl".to_string()], + }, + system: System { + release: vec!["2021.1".to_string()], + } + } + } +} diff --git a/libcport/src/port.rs b/libcport/src/port.rs new file mode 100644 index 0000000000000000000000000000000000000000..4a86fb174eeb27ebd86576680a1d9c5c094ff477 --- /dev/null +++ b/libcport/src/port.rs @@ -0,0 +1,227 @@ +/*! +A module that provides basic abstractions for working with ports. + +Usage: + +``` +use cport::port::Port; + +let port = Port::new("base/acl"); +``` + +By default, ports are located in the `/usr/ports` directory. This directory does +not need to be specified. However, if you wish, you can change the standard +ports directory to a third-party one using the `set_path()` method: + +``` +use cport::port::Port; + +let port = Port::new("base/acl").set_root_path("/tmp/ports"); +``` + +The `Port` structure from this module contains methods that allow not only to +read the contents of existing ports, but also to create new ports: + +``` +use cport::port::Port; + +let port = Port::new("base/acl_new") + .set_root_path("./Ports"); + // .create_port(); +``` +*/ + +use serde::{Serialize, Deserialize}; +use toml; + +use crate::consts; + +use std::fs; +use std::fmt; +use std::path::Path; +use std::io::{BufReader, Lines, Write}; +use std::io::prelude::*; +use anyhow::Result; + +/// Initial type for `cport` +/// +/// Предоставляет базовые абстракции для работы с портами. Может создавать +/// порты, парсить их конфигурационные файлы и выполнять прочие подобные +/// действия. +#[derive(Debug)] +pub struct Port { + /// Port name (e.g. `base/acl`) + pub name: String, + + /// Path in file system to port (e.g. `self.root_pth/base/acl`) + pub path: String, + + /// Check exists + pub is_exist: bool, + + root_pth: String, +} + +/// Structure for port configuration file (`port.toml`) +/// +/// ## Строение +/// +/// 1. **Параметры:** +/// - `std_ver` - версия стандарта порта +/// 2. **Секции:** +/// - `package` - описание портируемого пакета +/// - `deps` - зависимости пакета +/// - `port` - параметры для `cport` - URL, SHA256/MD5 и т.д. +#[derive(Serialize, Deserialize, Debug)] +pub struct PortConfig { + pub package: PackageConfSection, + pub deps: Option, + pub port: Option, + pub std_ver: Option, // TODO: убрать `Option` в стабильных релизах +} + +/// `[package]` section +#[derive(Serialize, Deserialize, Debug)] +pub struct PackageConfSection { + pub name: String, + pub version: String, + pub description: String, + pub maintainers: Vec, + pub usage: f32, + pub build_time: f32, + pub priority: String, + pub license: Option>, +} + +/// `[deps]` section (optional) +#[derive(Serialize, Deserialize, Debug)] +pub struct DepsConfSection { + pub required: Option>, + pub recommend: Option>, + pub optional: Option>, +} + +/// `[port]` section (optional) +#[derive(Serialize, Deserialize, Debug)] +pub struct PortConfSection { + pub url: Option, + pub file: Option, + pub md5: Option, + pub sha256: Option, +} + +impl Port { + pub fn new(name: &str) -> Self { + let pth = format!("{}/{}", consts::PORTS_DIR, name); + Self { + name: name.to_string(), + path: pth.to_string(), + is_exist: Path::new(&pth).exists(), + root_pth: consts::PORTS_DIR.to_string(), + } + } + + /// Устанавливает путь до системы портов + /// + /// **Default:** `cport_kernel::consts::PORTS_DIR` + pub fn set_root_path(&self, path: &str) -> Self { + let pth = format!("{}/{}", &path, &self.name); + Self { + root_pth: path.to_string(), + path: pth.to_string(), + is_exist: Path::new(&pth).exists(), + name: self.name.to_string(), + } + } + + /// Возвращает путь до системы портов + /// + /// **Example:** `/usr/ports/base/acl` + pub fn get_root_path(&self) -> &Path { + Path::new(&self.root_pth) + } + + /// Парсит конфигурационный файл порта + pub fn parse_conf(&self) -> Result { + let pth = Path::new(&self.path).join("port.toml"); + let conf = toml::from_str(&fs::read_to_string(pth)?)?; + + Ok(conf) + } + + /// Устанавливает указанную конфигурацию порта + pub fn set_conf(&self, conf: &PortConfig) -> Result<()> { + let conf_str = toml::to_string(&conf)?; + let mut f = fs::File::open(format!("{}/port.toml", &self.path))?; + write!(f, "{}", conf_str)?; + + Ok(()) + } + + /// Получает список файлов, которые порт устанавливает в систему + pub fn get_flist(&self) -> Result>> { + let f = fs::File::open(format!("{}/files.list", &self.path))?; + let fl = BufReader::new(f); + + let lines = fl.lines(); + Ok(lines) + } + + /// Проверяет порт на наличие всех файлов + pub fn check_exists(&self) -> bool { + if self.is_exist { + // let flist = ["install", "port.toml", "files.list"]; + let flist = ["install", "port.toml"]; + + for file in flist { + let file = Path::new(consts::PORTS_DIR) + .join(&self.path) + .join(file); + if !file.exists() { + return false; + } + } + true + } else { + false + } + } +} + +impl Default for PortConfig { + fn default() -> Self { + Self { + package: PackageConfSection { + name: "".to_string(), + version: "".to_string(), + description: "".to_string(), + maintainers: vec!["".to_string()], + usage: 0.0, + build_time: 0.0, + priority: "user".to_string(), + license: None, + }, + deps: None, + port: Some( + PortConfSection { + url: Some("".to_string()), + file: None, + md5: None, + sha256: None + } + ), + std_ver: Some("2.0".to_string()), + } + } +} + +impl fmt::Display for Port { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let p_ver = match self.parse_conf() { + Ok(c) => c.package.version, + Err(why) => format!("unknown (err: {why})"), + }; + + write!(f, "{}:{}", &self.name, &p_ver) + } +} diff --git a/libcport/src/remove.rs b/libcport/src/remove.rs new file mode 100644 index 0000000000000000000000000000000000000000..339b0f1129809b68329ddfb40e8fc58299c24851 --- /dev/null +++ b/libcport/src/remove.rs @@ -0,0 +1,35 @@ +//! Remove all port files from the filesystem + +use crate::port::Port; + +use std::path::Path; +use anyhow::Result; +use std::fs; + +pub fn remove_files(port: &Port, prefix: &str) -> Result { + let files = port.get_flist()?; + let mut result = true; + + for file in files { + let file = file?; + let pth = Path::new(&prefix).join(&file); + + if !pth.exists() { + crate::ui::formats::msg::warn( + format!("file '{file}' not found"), false + ); + result = false; + continue; + } + + if pth.is_dir() { + fs::remove_dir_all(pth)?; + } else if pth.is_file() || pth.is_symlink() { + fs::remove_file(pth)?; + } else { + result = false; + println!("Unknown file type or file not exits: {file}"); + } + } + Ok(result) +} diff --git a/libcport/src/ui.rs b/libcport/src/ui.rs new file mode 100644 index 0000000000000000000000000000000000000000..861238fda9cd4956c74a6ec406712fc8d0e9cb49 --- /dev/null +++ b/libcport/src/ui.rs @@ -0,0 +1 @@ +pub mod formats; diff --git a/libcport/src/ui/formats.rs b/libcport/src/ui/formats.rs new file mode 100644 index 0000000000000000000000000000000000000000..8217774695cf15275b7ecd031f0e03be054aebcd --- /dev/null +++ b/libcport/src/ui/formats.rs @@ -0,0 +1,49 @@ +/*! +Shared functions for TUI +*/ + +pub mod msg { + use colored::Colorize; + use anyhow::Result; + use std::process::exit; + use std::io::{Write, stdout, stdin}; + + pub fn err(msg: P, exit_pr: bool) { + eprintln!("[{}] cport: {}", "!".bold().red(), msg); + + if exit_pr { + exit(1) + } + } + + pub fn warn(msg: P, werr: bool) { + if werr { + err(&msg, true); + } + eprintln!("[{}] cport: {}", "W".bold().yellow(), msg); + } + + pub fn ok(msg: P) { + println!("[{}] {}", "✓".bold().green(), msg); + } + + pub fn dialog(default_no: bool) -> Result { + print!(":: Continue? "); + if default_no { + print!("[y/N] "); + } else { + print!("[Y/n] "); + } + stdout().flush()?; + + let mut dial = String::new(); + stdin() + .read_line(&mut dial)?; + + if dial.trim() == "n" || dial.trim() == "N" { + Ok(false) + } else { + Ok(true) + } + } +} diff --git a/libcport/src/update.rs b/libcport/src/update.rs new file mode 100644 index 0000000000000000000000000000000000000000..490307b10c87af6f48b2c4c567b2bd66d08f246e --- /dev/null +++ b/libcport/src/update.rs @@ -0,0 +1,120 @@ +//! Update ports system + +use anyhow::Result; +use std::path::Path; + +use crate::port::Port; +use crate::metadata::Metadata; +use crate::consts::PORTS_DIR; +use crate::consts::TMP_PORTS_DIR; +use crate::config::Config; + +/// Download archive with ports system +pub fn download_ports() -> Result { + let conf = Config::parse()?; + let _url = format!("{}/{}/ports.txz", &conf.base.url, &conf.base.branch); + unimplemented!() +} + +/// Unpack archive with ports system to the cache +pub fn unpack_ports() -> Result { + unimplemented!() +} + +/// Get removed ports from downloaded ports system +pub fn get_removed() -> Result> { + let ports_pth = Path::new(PORTS_DIR).join("metadata.toml"); + let ports_tmp_pth = Path::new(TMP_PORTS_DIR).join("metadata.toml"); + + let ports = Metadata::parse(&ports_pth)?; + let tmp_ports = Metadata::parse(&ports_tmp_pth)?; + + let mut removed_prts = Vec::new(); + + let ports_lst = ports.port_sys.ports; + let tmp_ports_lst = tmp_ports.port_sys.ports; + + /* + Нам нужно проверить, какие порты были удалены из скачанной системы портов. + + Для этого проходимся по установленной системе портов и проверяем, + существует ли конкретный порт из установленной системы в скачанной. + + В итоге получим список портов, которые в новую систему портов + были добавлены. + */ + for port in ports_lst { + if !tmp_ports_lst.contains(&port) { + removed_prts.push(port); + } + } + + Ok(removed_prts) +} + +/// Get added ports to downloaded ports system +pub fn get_added() -> Result> { + let ports_pth = Path::new(PORTS_DIR).join("metadata.toml"); + let ports_tmp_pth = Path::new(TMP_PORTS_DIR).join("metadata.toml"); + + let ports = Metadata::parse(&ports_pth)?; + let tmp_ports = Metadata::parse(&ports_tmp_pth)?; + + let mut added_prts = Vec::new(); + + let ports_lst = ports.port_sys.ports; + let tmp_ports_lst = tmp_ports.port_sys.ports; + + /* + Нам нужно проверить, какие порты были добавлены в скачанную систему портов. + + Для этого проходимся по скачанной системе портов и проверяем, + существует ли конкретный порт из скачанной системы в установленной. + + В итоге получим список портов, которые в новую систему портов + были добавлены. + */ + for port in tmp_ports_lst { + if !ports_lst.contains(&port) { + added_prts.push(port); + } + } + + Ok(added_prts) +} + +/// Get updated ports +pub fn get_updated() -> Result> { + let ports_pth = Path::new(PORTS_DIR).join("metadata.toml"); + let ports_tmp_pth = Path::new(TMP_PORTS_DIR).join("metadata.toml"); + + let ports = Metadata::parse(&ports_pth)?; + let tmp_ports = Metadata::parse(&ports_tmp_pth)?; + + let mut updated_prts = Vec::new(); + + let ports_lst = ports.port_sys.ports; + let tmp_ports_lst = tmp_ports.port_sys.ports; + + for port in ports_lst { + /* + * Проходимся по существующим в обоих системах портам + * и проверяем, равны ли параметры `port.version` (из + * конфига `port.toml`) в обоих портах. Если нет - + * добавляем порт с изменённой версией в вектор. + */ + if tmp_ports_lst.contains(&port) { + let port_inst = Port::new(&port) + .parse_conf()?; + let port_dwnl = Port::new(&port) + .set_root_path(TMP_PORTS_DIR) + .parse_conf()?; + + if port_inst.package.version != port_dwnl.package.version { + updated_prts.push(port); + } + } + } + + Ok(updated_prts) +} \ No newline at end of file diff --git a/libcport/src/utils.rs b/libcport/src/utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..a373c83533a26bdd161a319e85349ba7254de29e --- /dev/null +++ b/libcport/src/utils.rs @@ -0,0 +1,19 @@ +/*! +Shared methods and variables +*/ + +use std::path::PathBuf; +use nix::unistd; + +/// Проверяет, запущен ли процесс cport от имени root +/// +/// Если `cport` запущен от имени пользователя `root`, то возвращает `True` +pub fn check_root_user() -> bool { + let uid = unistd::getuid(); + uid.is_root() +} + +/// Convert `Path` -> `&str` +pub fn pth_to_str(pth: PathBuf) -> String { + String::from(pth.to_str().unwrap_or("")) +} diff --git a/mkport/Cargo.toml b/mkport/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..04c430972af257e263b5e582ab1dacdcbbf9c572 --- /dev/null +++ b/mkport/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "mkport" +version = "0.1.0" +edition = "2021" +description = "Utility to create software ports for Calmira GNU/Linux" +authors = ["Michail Krasnov "] +license = "GPL-3.0-or-later" + +[dependencies] +libcport = { version = "2.0.0", path = "../libcport" } +anyhow = "1.0.69" +cursive = "0.20.0" +serde = { version = "1.0.152", features = ["derive"] } +toml = "0.7.2" diff --git a/mkport/src/lib.rs b/mkport/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..f678336b9e88b9958c4d075c8d887049cab30277 --- /dev/null +++ b/mkport/src/lib.rs @@ -0,0 +1,2 @@ +pub mod port; +pub mod utils; diff --git a/mkport/src/main.rs b/mkport/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..1c9b64ed23f6a693fdc28a7944864b01c087bcb3 --- /dev/null +++ b/mkport/src/main.rs @@ -0,0 +1,137 @@ +use mkport::port::Port; +// use toml; + +use cursive::Cursive; +use cursive::views::ListView; +use cursive::views::TextView; +use cursive::views::Dialog; +use cursive::views::EditView; +use cursive::views::Panel; +use cursive::views::LinearLayout; +use cursive::views::RadioGroup; +use cursive::traits::*; + +fn main() { + let mut scr = cursive::default(); + new_port(&mut scr); + scr.run(); +} + +fn new_port(scr: &mut Cursive) { + let port = Port::default(); + enter_data(scr, &port); +} + +fn enter_data(scr: &mut Cursive, port: &Port) { + scr.pop_layer(); + let package_section = ListView::new() + .child( + "Name:", + EditView::new() + .content(port.package.name.clone()) + .with_name("package.name") + ) + .child( + "Version:", + EditView::new() + .content(port.package.version.clone()) + .with_name("package.version"), + ) + .child( + "Description:", + EditView::new() + .content(port.package.description.clone()) + .with_name("package.description"), + ) + .child( + "Priority:", + EditView::new() + .content(port.package.priority.clone()) + .with_name("package.priority"), + ) + .child( + "Category:", + EditView::new().with_name("package.category"), + ); + let deps_section = ListView::new() + .child( + "Required: ", + EditView::new() + .with_name("deps.required"), + ) + .child( + "Recommend:", + EditView::new().with_name("deps.recommend"), + ) + .child( + "Optional:", + EditView::new().with_name("deps.optional"), + ); + let port_section = ListView::new() + .child( + "URL: ", + EditView::new().with_name("port.url").min_width(40) + ) + .child( + "File name:", + EditView::new().with_name("port.file"), + ); + + let port_toml_main = LinearLayout::vertical() + .child( + Panel::new(package_section).title("[package]") + ) + .child( + Panel::new(deps_section).title("[deps]") + ) + .child( + Panel::new(port_section).title("[port]") + ); + + let mut build_sys_list = RadioGroup::new(); + let build_sys = Panel::new( + LinearLayout::vertical() + .child(build_sys_list.button_str("make")) + .child(build_sys_list.button_str("cmake")) + .child(build_sys_list.button_str("meson")) + .child(build_sys_list.button_str("cargo")) + .child(build_sys_list.button_str("other")) + ).title("BS"); + + let mut licenses_list = RadioGroup::new(); + let licenses = Panel::new( + LinearLayout::vertical() + .child(licenses_list.button_str("GPL-2.0")) + .child(licenses_list.button_str("GPL-3.0")) + .child(licenses_list.button_str("LGPL-3.0")) + .child(licenses_list.button_str("BSD-3-cls")) + .child(licenses_list.button_str("MIT")) + .child(licenses_list.button_str("Apache")) + .child(licenses_list.button_str("other")) + ) + .title("License"); + + let port_files_main = LinearLayout::vertical() + .child(build_sys) + .child(licenses); + + let main_layout = LinearLayout::horizontal() + .child(port_toml_main) + .child(port_files_main).scrollable(); + + let window = Dialog::around(main_layout) + .title("mkport v0.1") + .button("Create port", move |s| { + let port = Port::from_cursive(s).to_port_dt().unwrap(); + let port_str = toml::to_string(&port).unwrap(); + + s.add_layer( + Dialog::around( + TextView::new(port_str).scrollable() + ).button("Quit", |s| { s.pop_layer(); }) + .title("Summary") + ); + }) + .button("Quit", |s| s.quit()); + scr.add_layer(window); +} diff --git a/mkport/src/port.rs b/mkport/src/port.rs new file mode 100644 index 0000000000000000000000000000000000000000..3cf9183e2f78f03df93d221fb2600b9f0492e3c5 --- /dev/null +++ b/mkport/src/port.rs @@ -0,0 +1,206 @@ +use serde::Deserialize; +use serde::Serialize; +use anyhow::Result; +use std::fs; +use toml; + +use libcport::port as prt; + +use cursive::Cursive; +use cursive::views::EditView; + +#[derive(Debug, Deserialize, Serialize)] +pub struct Port { + pub package: PackageSection, + pub deps: DepsSection, + pub port: PortSection, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PackageSection { + pub category: String, + pub name: String, + pub version: String, + pub description: String, + pub maintainers: String, + pub priority: String, + pub usage: f32, + pub upgrade_mode: String, + pub build_time: f32, + pub license: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct DepsSection { + pub required: String, + pub recommend: String, + pub optional: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PortSection { + pub url: String, + pub md5: String, + pub sha256: String, +} + +impl Port { + pub fn create_new(path: &str) -> Result<()> { + let str_data = toml::to_string(&Self::default())?; + fs::write(path, str_data)?; + Ok(()) + } + + pub fn parse(path: &str) -> Result { + let str_data = fs::read_to_string(path)?; + let data: Self = toml::from_str(&str_data)?; + + Ok(data) + } + + pub fn from_cursive(scr: &mut Cursive) -> Self { + Self { + package: PackageSection { + category: scr.call_on_name("package.category", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + name: scr.call_on_name("package.name", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + version: scr.call_on_name("package.version", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + description: scr.call_on_name("package.description", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + maintainers: scr.call_on_name("package.maintainer", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + priority: scr.call_on_name("package.priority", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + usage: 0.0, + upgrade_mode: "soft".to_string(), + build_time: 0.0, + license: scr.call_on_name("package.license", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + }, + deps: DepsSection { + required: scr.call_on_name("deps.required", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + recommend: scr.call_on_name("deps.recommend", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + optional: scr.call_on_name("deps.optional", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + }, + port: PortSection { + url: scr.call_on_name("port.url", |s: &mut EditView| { + s.get_content().as_ref().to_string() + }).unwrap_or_default(), + md5: "".to_string(), + sha256: "".to_string(), + } + } + + } + + pub fn to_port_dt(&self) -> Result { + let mut _prt = prt::PortConfig::default(); + + _prt.package.name = self.package.name.clone(); + _prt.package.version = self.package.version.clone(); + _prt.package.description = self.package.description.clone(); + _prt.package.usage = self.package.usage; + _prt.package.build_time = self.package.build_time; + _prt.package.priority = self.package.priority.clone(); + + let maintainers = self.package.maintainers.split(','); + _prt.package.maintainers = maintainers.map(|s| s.trim().to_string()).collect(); + + let licenses = self.package.license.split(','); + _prt.package.license = Some(licenses.map(|s| s.trim().to_string()).collect()); + + let req_deps: Option> = if self.deps.required.is_empty() { + None + } else { + let deps = self.deps.required.split(','); + Some( + deps.map(|s| s.trim().to_string()) + .collect() + ) + }; + + let rec_deps: Option> = if self.deps.recommend.is_empty() { + None + } else { + let deps = self.deps.recommend.split(','); + Some( + deps.map(|s| s.trim().to_string()) + .collect() + ) + }; + + let opt_deps: Option> = if self.deps.optional.is_empty() { + None + } else { + let deps = self.deps.optional.split(','); + Some( + deps.map(|s| s.trim().to_string()) + .collect() + ) + }; + + _prt.deps = Some(prt::DepsConfSection { + required: req_deps, + recommend: rec_deps, + optional: opt_deps, + }); + + let url = Some(self.port.url.clone()); + let md5 = Some("".to_string()); + let sha256 = Some("".to_string()); + let file = None; + + _prt.port = Some(prt::PortConfSection { + url, + md5, + sha256, + file, + }); + + Ok(_prt) + } +} + +impl Default for Port { + fn default() -> Self { + Self { + package: PackageSection { + category: "general".to_string(), + name: "".to_string(), + version: "".to_string(), + description: "".to_string(), + maintainers: "".to_string(), + priority: "user".to_string(), + usage: 0.0, + upgrade_mode: "soft".to_string(), + build_time: 0.0, + license: "".to_string() + }, + deps: DepsSection { + required: "".to_string(), + recommend: "".to_string(), + optional: "".to_string(), + }, + port: PortSection { + url: "".to_string(), + md5: "".to_string(), + sha256: "".to_string(), + } + } + } +} diff --git a/mkport/src/utils.rs b/mkport/src/utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..c941daf5432cae3c3ca366abb92d8707d521cdcd --- /dev/null +++ b/mkport/src/utils.rs @@ -0,0 +1,53 @@ +use std::env; +use std::path::Path; + +fn search_editor_pth() -> String { + let pathes = [ + "/usr/bin/vim", + "/usr/bin/nvim", + "/usr/bin/mcedit", + "/usr/bin/nano", + "/usr/bin/micro", + "/usr/bin/gedit", + "/usr/bin/gnome-text-editor", + ]; + + let mut editor = String::new(); + for pth in pathes { + if Path::new(pth).exists() { + editor = pth.to_string(); + break; + } + } + + editor +} + +pub fn get_home() -> String { + match env::var("HOME") { + Ok(h) => h, + Err(_) => "/tmp".to_string(), + } +} + +/// Gets the path to the text editor to use +/// +/// **Note:** before use, we advise you to set the `EDITOR` environment +/// variable containing the path to the ELF file of the text editor +/// (e.g. `/usr/bin/vim`). +pub fn get_editor_path() -> String { + match env::var("EDITOR") { + Ok(mut ed) => { + if ed.contains("~/") { + ed = ed.replace("~/", &get_home()); + } + + if !Path::new(&ed).exists() { + search_editor_pth() + } else { + ed + } + }, + Err(_) => search_editor_pth(), + } +} diff --git a/pic/cport.png b/pic/cport.png deleted file mode 100644 index 5167463add3ff628463b7092021c8d9e7fbeae55..0000000000000000000000000000000000000000 Binary files a/pic/cport.png and /dev/null differ diff --git a/pic/cport1.png b/pic/cport1.png deleted file mode 100644 index 6db19fb38413459eea27644c52fa725fe9351498..0000000000000000000000000000000000000000 Binary files a/pic/cport1.png and /dev/null differ diff --git a/pic/cport_check.gif b/pic/cport_check.gif deleted file mode 100644 index f818dabad537546704b5ecdbfc9b1deccb184da5..0000000000000000000000000000000000000000 Binary files a/pic/cport_check.gif and /dev/null differ diff --git a/pic/cport_info.gif b/pic/cport_info.gif deleted file mode 100644 index c2ecc4c4d3755af308f6f73f045d70474e541835..0000000000000000000000000000000000000000 Binary files a/pic/cport_info.gif and /dev/null differ diff --git a/pic/cport_list.gif b/pic/cport_list.gif deleted file mode 100644 index bb694b0f36c5ccc75aadb3a294b37dcf0bb30dbf..0000000000000000000000000000000000000000 Binary files a/pic/cport_list.gif and /dev/null differ diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 6ecd3b98da1848d6861c2010c72d4f58e4f24088..0000000000000000000000000000000000000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -toml~=0.10.2 -wget~=3.2 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 3e6c1a0ba4869e902a3b982bb248d7251d706cc9..0000000000000000000000000000000000000000 --- a/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from distutils.core import setup - -setup( - name='cport', - version='v1.0b2', - packages=['libcport', 'cport_cli'], - package_dir={'libcport': 'src/libcport', 'cport_cli': 'src/cport_cli'}, - url='https://gitlab.com/calmiralinux/cabs/cport', - license='GNU GPLv3', - author='cov_id111', - author_email='michail383krasnov@mail.ru', - description='cport - менеджер системы портов дистрибутива Calmira GNU/Linux' -) diff --git a/src/cport.py b/src/cport.py deleted file mode 100755 index 0d7dc850e1d3376a75c1de7faabd8c00e99db4d8..0000000000000000000000000000000000000000 --- a/src/cport.py +++ /dev/null @@ -1,292 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -import argparse -import os -import sys - -import cport_cli.args_parser as ccap # cport CLI Arguments Parser - CCAP -import libcport.core as cc -import libcport.datatypes as cd -from libcport.constants import DATABASE_FILE, DATABASE_USER_FILE, LOCAL_LIB_DIR, VERSION - -import cport_cli.info as cci -# import cport_cli.upgrade as ccU - -VER_MSG = f"""cport {VERSION} - the port manager for Calmira GNU/Linux-libre -Copyright (C) 2021, 2022 Michail Krasnov - -License GPLv3+: GNU GPL version 3 or later - -This is free software; you are free to change and redistribute it. -There is NO WARRANTY, to the extent permitted by law. -""" - -parser = argparse.ArgumentParser() -subparser = parser.add_subparsers() - -parser.add_argument( - "--install", "-i", nargs="+", help="Скачать, распаковать, собрать и установить порт в систему" -) - -parser.add_argument( - "--remove", "-r", nargs="+", help="Удалить порт из системы" -) - -parser.add_argument( - "--info", "-I", dest="info", nargs="+", - help="Просмотреть информацию о программном обеспечении в портах" -) - -parser.add_argument( - "--info-json", dest="info_json", nargs="+", - help="Просмотреть информацию о ПО в портах в формате JSON" -) - -parser.add_argument( - "--update", "-u", dest="update", action="store_true", - help="Проверить обновления системы портов" -) - -parser.add_argument( - "--upgrade", "-U", dest="upgrade", nargs="+", - help="Обновить указанное пользователем программное обеспечение" -) - -parser.add_argument( - "--get-flist", dest="get_flist", - help="Вывести список файлов указанного порта" -) - -list_parser = subparser.add_parser('list', help='list of ports') -list_parser.add_argument( - '-a', '--all', dest='list_all', action='store_true', - help="Просмотреть список всех портов" -) -list_parser.add_argument( - '-i', '--installed', dest='list_installed', action='store_true', - help="Просмотреть список установленных портов" -) -list_parser.add_argument( - '-I', '--not-installed', dest='list_not_installed', action='store_true', - help="Просмотреть список НЕ установленных портов" -) -list_parser.add_argument( - '-b', '--broken', dest='list_broken', action='store_true', - help="Просмотреть список битых портов" -) -list_parser.add_argument( - '-j', '--json', dest='list_json', action='store_true', - help="Вывести информацию о портах в формате JSON" -) -list_parser.set_defaults(func=ccap.ports_list) - -deps_parser = subparser.add_parser('deps', help='getting a list of packages that have the required one as a dependency') -deps_parser.add_argument( - "--all", "-a", dest="deps_all", help="all types of dependencies", - action="store_true" -) -deps_parser.add_argument( - "--required", "-r", dest="required_deps", help="required dependencies", - action="store_true" -) -deps_parser.add_argument( - "--recommend", "-R", dest="recommend_deps", help="recommend dependencies", - action="store_true" -) -deps_parser.add_argument( - "--optional", "-o", dest="optional_deps", help="optional dependencies", - action="store_true" -) -deps_parser.add_argument( - "--name", "-n", dest="dest_name", required=True, type=str, - help="set the name of port" -) -deps_parser.add_argument( - "--json", "-j", dest="deps_json", action="store_true" -) -deps_parser.set_defaults(func=ccap.port_deps) - -check_parser = subparser.add_parser('check', help='check all ports') -check_parser.add_argument( - '--is-exist', dest='check_exist', help="Проверка наличия порта в СП" -) -check_parser.add_argument( - '--is-installed', dest='check_installed', help="Проверка наличия порта в БД" -) -check_parser.add_argument( - '--check-ports', dest='check_all', action='store_true', - help="Проверка наличия зависимостей всех портов в СП" -) -check_parser.add_argument( - '--ports-linter', dest='plint', action='store_true', - help="Проверка портов на корректность" -) -check_parser.set_defaults(func=cci.ports_check) - -parser.add_argument( - "--yes", "-y", dest="confirm_proc", action="store_true", - help="Отвечать утвердительно на выполнение всех операций" -) - -parser.add_argument( - "--ignore-db", dest="ignore_db", type=bool, default=False, - help="Игнорировать проверку базы данных перед установкой или удалением порта" -) - -parser.add_argument( - "--skip-check", "-C", dest="skip_check", default=False, - action="store_true", help="Не проверять наличие или отсутствие файлов порта после установки или удаления" -) - -parser.add_argument( - "--rebuild-db", dest="rebuild_db", action="store_true", - help="Обновить базу данных cport" -) - -parser.add_argument( - "--wipe-cache", "-W", dest="wipe_cache", action="store_true", - help="Удалить все файлы, которые находятся в кеше" -) - -parser.add_argument( - "-v", "--version", action="store_true", - help="Print the cport version" -) - -args = parser.parse_args() - -if not args: - parser.print_usage() - exit(1) - -skip_checks = False -if args.skip_check: - skip_checks = True - -ignoredb = False -if args.ignore_db: - ignoredb = True - -confirm = False -if args.confirm_proc: - confirm = True - - -def main() -> int: - try: - if not os.path.exists(DATABASE_FILE): - cc.Msg.warn(f"File '{DATABASE_FILE}' not found! Rebuilding...") - if not cc.ckuser(): - cc.Msg.err("Permission error!") - return 126 - - cc.Msg.msg("-- cport init -- rebuild the database...") - db = cc.CDatabase(cd.port("general/vagina")) - - db.create_initial_db() - db.gen_all_ports() - - cc.Msg.msg("-- cport init -- regenerate the database...") - cc.rebuild() - - if not os.path.exists(LOCAL_LIB_DIR): - cc.Msg.warn(f"Directory '{LOCAL_LIB_DIR}' not found! Creating...") - os.makedirs(LOCAL_LIB_DIR) - - if not os.path.isfile(DATABASE_USER_FILE): - cc.Msg.warn(f"File '{DATABASE_USER_FILE}' not found! Rebuilding...") - - db = cc.CDatabase(cd.port("general/vagina"), dbfile=DATABASE_USER_FILE) - db.create_initial_db() - - if not vars(args): - parser.print_usage() - return 1 - - elif args.install: - if not cc.ckuser(): - cc.Msg.err("Permission denied!") - return 126 - return ccap.install(args.install, ignoredb, confirm, skip_checks) - - elif args.remove: - if not cc.ckuser(): - cc.Msg.err("Permission denied!") - return 126 - return ccap.remove(args.remove, ignoredb, confirm, skip_checks) - - elif args.info: - return ccap.info(args.info) - - elif args.info_json: - return ccap.info(args.info_json, json_enable=True) - - elif args.update: - if not cc.ckuser(): - cc.Msg.err("Permission denied!") - return 126 - return ccap.update(confirm) - - elif args.upgrade: - return ccap.upgrade(args.upgrade) - - elif args.get_flist: - return ccap.get_port_flist(args.get_flist) - - elif args.wipe_cache: - if not cc.ckuser(): - cc.Msg.err("Permission denied!") - return 126 - - return ccap.wipe_cache() - - elif args.rebuild_db: - if not cc.ckuser(): - cc.Msg.err("Permission denied!") - return 126 - - return ccap.rebuild_database() - - elif args.version: - print(VER_MSG) - return 0 - - else: - try: - code = args.func(args) - return code - except AttributeError: - parser.print_usage() - return 1 - - except KeyboardInterrupt: - cc.Msg.err("Keyboard interrupt.") - return 130 - - except EOFError: - cc.Msg.err("Keyboard interrupt.") - return 130 - - # except PermissionError: - # cc.Msg.err("Permission denied!") - # return 126 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/cport_cli/__init__.py b/src/cport_cli/__init__.py deleted file mode 100644 index d8d9549d301d9b3abeb14af8521919b278c30f84..0000000000000000000000000000000000000000 --- a/src/cport_cli/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/python3 diff --git a/src/cport_cli/args_parser.py b/src/cport_cli/args_parser.py deleted file mode 100644 index cd51eba6e8053cbfec246c75e1246c9bf310eb23..0000000000000000000000000000000000000000 --- a/src/cport_cli/args_parser.py +++ /dev/null @@ -1,449 +0,0 @@ -#!/usr/bin/python3 -# -# args_parser.py -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -"""This module concatenates all other modules from `cport_shared`, creating -functions that will be used in `cport` when parsing command line arguments. The -module is a simple set of functions to abstract away the logic of the Calmira -GNU/Linux-libre distribution's port management utility. - -Examples: ---------- - ->>> import cport_cli.args_parser as ccap # (Cport Cli Argument Parser - ccap) ->>> import argparse ->>> parser = argparse.ArgumentParser() ->>> parser.add_argument( -... "--wipe-cache", "-W", action="store_true", dest="clean_cache", -... help="Clean the cache" -... ) -... ->>> args = parser.parse_args() ->>> if args.clean_cache: -... exit_value = ccap.wipe_cache() -... exit(exit_value) -... -""" -import json -import time - -import cport_cli.info -import cport_cli.install -import cport_cli.remove -import cport_cli.update -import cport_cli.upgrade - -import libcport.core as cc -import libcport.constants as ccc -import libcport.datatypes as cd -import libcport.exceptions as ce - -import toml - - -def wipe_cache() -> int: - r"""Function to delete all data in the cache - - Principle of operation - ---------------------- - - 1. A list of all files in the cache is calculated (which is only needed - to output to the console how many files have been deleted). - 2. If there are 0 files, then an error message is displayed: there is - nothing to delete. - - Returns 1 - 3. If there are more than zero files, then all of them are deleted. A - message is printed to `stdout` stating that they have all been removed. - - Returns 0 - """ - - files = cc.get_files_in_cache() - cc.wipe_cache() - if files > 0: - cc.Msg.ok(f"{files} files have been deleted") - return 0 - else: - cc.Msg.err("Nothing to delete") - return 1 - - -def port_flist(prt: str) -> int: - """Function to list port files to stdout - - Principle of operation - ---------------------- - - 1. The `files.list` file of the port is opened and read. - 2. Its content is output to stdout. - - Returns 0. - 3. If the `files.list` file or the port itself is missing, a message about - this error is displayed in stderr. - - Returns 1. - """ - - try: - port = cd.port(prt) - fl = f"{port.path}/files.list" - with open(fl) as f: - print(f.read()) - return 0 - except ce.PortNotFoundError: - cc.Msg.err(f"Port '{prt}' not found!") - return 1 - except FileNotFoundError: - cc.Msg.err(f"File 'files.list' of port '{prt} not found") - return 1 - - -def install(ports: list[str], ign_db: bool = False, cnfrm: bool = True, skip_chk: bool = False) -> int: - try: - _info = cport_cli.info.CPortDependenciesCLI(list(map(cd.port, ports))) - except ce.PortNotFoundError: - cc.Msg.err(f"Some of ports not found!") - return 1 - - _info.print_selected_ports() - _info.print_deps() - _info.print_usage() - - if not cnfrm and not cc.Msg.dialog(): - return 1 - - for port in ports: - try: - port = cd.port(port) - - cc.Msg.msg(f"Installing port '{port.name}'") - - # run = cport_cli.install(ignore_db=ign_db, confirm=cnfrm, skip_check=skip_chk) - run = cport_cli.install.install(port, ign_db, skip_chk) - - if run != 0: - return run - except ce.PortNotFoundError: - cc.Msg.err(f"Port '{port}' not found!") - return 1 - except toml.decoder.TomlDecodeError: - cc.Msg.err(f"Error decoding the configuration file of the port '{port}'") - return 1 - # except ValueError: - # cc.Msg.err(f"File '{port.path}/port.toml' is broken!") - # return 1 - return 0 - - -def remove(ports: list[str], ignore_db: bool = False, cnfrm: bool = False, skip_check: bool = False) -> int: - for port in ports: - try: - port = cd.port(port) - - cc.Msg.header(f"Removing port '{port.name}'") - run = cport_cli.remove.remove(port, ignore_db, cnfrm, skip_check) - - if run != 0: - return run - except ce.PortNotFoundError: - cc.Msg.err(f"Port '{port}' not found!") - return 1 - except toml.decoder.TomlDecodeError: - cc.Msg.err(f"Error decoding the configuration file of the port '{port}'") - return 1 - return 0 - - -def info(ports: list[str], json_enable: bool = False) -> int: - try: - ports = list(map(cd.port, ports)) - - if json_enable: - inf = cport_cli.info.CPortInfoJSON(ports) - else: - inf = cport_cli.info.CPortInfoCLI(ports) - - inf.info() - - return 0 - except ce.PortNotFoundError: - cc.Msg.err("Unknown port not found!") - return 1 - - -def is_installed(port: str) -> int: - return cport_cli.info.is_installed(port) - - -def is_exist(port: str) -> int: - return cport_cli.info.is_exist(port) - - -def ports_list(args) -> int: - cl = cc.CList() - # dplst = ('required', 'recommend', 'optional') - - pkg_list = cl.all() - - pkg_list_i = cl.installed() - pkg_list_n = cl.not_installed() - pkg_list_b = cl.broken() - - pkg_list_it = [] - pkg_list_nt = [] - pkg_list_bt = [] - - for pkg in pkg_list_i: - pkg_list_it.append(pkg[0]) - for pkg in pkg_list_n: - pkg_list_nt.append(pkg) - for pkg in pkg_list_b: - pkg_list_bt.append(pkg) - - if args.list_json: - json_enable = True - else: - json_enable = False - - def get_json(mode: str = "all") -> str: - # dt = {} - if mode == "all": - dt = { - "installed": [], - "not_installed": [], - "broken": [], - "unknown_status": [] - } - - for _package in pkg_list: - if _package in pkg_list_it: - dt['installed'].append(_package) - elif _package in pkg_list_n: - dt['not_installed'].append(_package) - elif _package in pkg_list_b: - dt['broken'].append(_package) - else: - dt['unknown_status'].append(_package) - - elif mode == "installed": - dt = { - "installed": [] - } - for _package in pkg_list_i: - _dt = { - "name": _package[0], - "installation_date": _package[1] - } - dt['installed'].append(_dt) - - elif mode == "not_installed": - dt = { - "not_installed": [] - } - for _package in pkg_list_n: - dt['not_installed'].append(_package) - - elif mode == "broken": - dt = { - "broken": [] - } - for _package in pkg_list_b: - dt['broken'].append(_package) - - else: - raise ValueError - - return json.dumps(dt, indent=4, ensure_ascii=False) - - if args.list_all: - if json_enable: - print(get_json("all")) - return 0 - - print("\033[3m\033[42m \033[0m - installed ports;", end=" ") - print("\033[3m\033[47m \033[0m - not installed ports;", end=" ") - print(f"\033[3m\033[41m \033[0m - broken ports\n{'-' * 80}") - - for package in pkg_list: - if package in pkg_list_it: - print(f"\033[3m\033[42m \033[0m {package}") - elif package in pkg_list_nt: - print(f"\033[3m\033[47m \033[0m {package}") - elif package in pkg_list_bt: - print(f"\033[3m\033[41m \033[0m {package} - \033[31mbroken\033[0m") - - return 0 - - elif args.list_installed: - if json_enable: - print(get_json("installed")) - return 0 - pkg_lst_all = cl.installed() - - print("\033[1m{0:30} {1}\n{2}\033[0m".format( - "port name", "installation date", 55 * '-' - )) - - for pkg in pkg_lst_all: - tp_str = "{0:30} {1}".format(pkg[0], time.ctime(float(pkg[1]))) - print(tp_str) - - return 0 - - elif args.list_not_installed: - if json_enable: - print(get_json("not_installed")) - return 0 - pkg_list = pkg_list_n - elif args.list_broken: - if json_enable: - print(get_json("broken")) - return 0 - pkg_list = pkg_list_b - - else: - if json_enable: - print(get_json()) - return 0 - - for package in pkg_list: - print(package) - - return 0 - - -def update(confirm: bool = False) -> int: - dt = cport_cli.update.check() - if dt == 1: - return 1 - elif dt == -1: - return 0 - - if not confirm and not cc.Msg.dialog(): - return 1 - - cc.Msg.msg("Updating the ports system...") - u = cport_cli.update.Update(f"{ccc.CACHE_UNPACKED_DIR}/ports") - if u.install(): - cc.Msg.ok("Update complete!") - cc.Msg.msg("Update the database...") - - db = cc.CDatabase(cd.port("general/vagina")) - db.gen_all_ports() - db.close() - - return 0 - else: - cc.Msg.err("Update FAIL!") - return 1 - - -def upgrade(args) -> int: - return cport_cli.upgrade.main(args) - - -def rebuild_database() -> int: - cc.rebuild() - return 0 - - -""" - elif args.list_deps: - if json_enable: - print(get_json("deps")) - return 0 - dps = [] - for dep in dplst: - for pkg in pkg_list_d[dep]: - dps.append(f"\033[1m{dep}:\033[0m\t{pkg}") - pkg_list = dps -""" - - -def port_deps(args) -> int: - def get_json(_mode: str = "all") -> str: - """Work modes: - - * all [default]; - * required; - * recommend; - * optional - """ - - dt = { - "port_name": prt.name - } - - if _mode == "all": - for _dep in dplst: - dt[_dep] = pkg_lst.get(_dep) - else: - dt[_mode] = pkg_lst.get(_mode) - - return json.dumps(dt, indent=4, ensure_ascii=False) - - dplst = ccc.PORT_TOML_PARAMS['deps'] - - if args.deps_json: - json_enable = True - else: - json_enable = False - - prt_name = args.dest_name - - try: - prt = cd.port(prt_name) - except ce.PortNotFoundError: - cc.Msg.err(f"Port '{prt_name}' not found!") - return 1 - - pkg_lst = cport_cli.info.get_deps(prt) - - if args.required_deps: - mode = 'required' - elif args.recommend_deps: - mode = 'recommend' - elif args.optional_deps: - mode = 'optional' - else: - mode = 'all' - - if json_enable: - print(get_json(mode)) - return 0 - - if mode == "all": - for mode in dplst: - for dep in pkg_lst[mode]: - print(f"\033[1m{mode}:\t\033[0m {dep}") - return 0 - - for dep in pkg_lst[mode]: - print(dep) - - -def get_port_flist(port: str) -> int: - try: - port = cd.port(port) - with open(f"{port.path}/files.list") as f: - print(f.read()) - return 0 - except FileNotFoundError: - cc.Msg.err(f"File '{port.path}/files.list' not found!") - return 1 - except ce.PortNotFoundError: - cc.Msg.err(f"Port '{port}' not found!") - return 1 diff --git a/src/cport_cli/bs.py b/src/cport_cli/bs.py deleted file mode 100644 index 3b1f41d58e66c20fde0eae6458680715405614a4..0000000000000000000000000000000000000000 --- a/src/cport_cli/bs.py +++ /dev/null @@ -1,282 +0,0 @@ -#!/usr/bin/python3 -# -# bs.py - build system for Calmira GNU/Linux-libre -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -raise DeprecationWarning - -import os -import shutil -import subprocess -import tarfile -import time - -import libcport.constants as cC -import libcport.datatypes as cd -import toml -import wget -from cport_cli.shared import Msg - -CACHE_DIR = "/mnt/calm/sources" -INFO_FILE = "/mnt/calm/build.toml" -PREPARATION_SCRIPTS_DIR = f"{cC.PORTS_DIR}/bs/scripts" -CREATE_DIRS = f"{PREPARATION_SCRIPTS_DIR}/dirs.sh" -CREATE_USER = f"{PREPARATION_SCRIPTS_DIR}/users.sh" - -""" -Automated Building System for Calmira GNU/Linux-libre - -Запускается от имени пользователя `calm'. Собирает порты для временного иструментания, -после входит в chroot и собирает порты core-редакции. -""" - - -class BuildingPreparations(object): - """Carrying out preliminary preparations: - - - Creation of necessary files and directories for system building; - - Checking the correctness of the host system for the building; - """ - - def __init__(self): - if os.geteuid() != 0: - raise PermissionError - - def _run(self, script: str) -> int: - run = subprocess.run(script, shell=True) - return run.returncode - - def create_limited_dirs_lyout(self) -> int: - return self._run(CREATE_DIRS) - - def create_user(self) -> int: - return self._run(CREATE_USER) - - -class BuildMachine(object): - """Класс для сборки конкретного порта. - - Все необходимые порты [в нужном порядке] для сборки перечислены в файле - '/usr/ports/bs/build_order.toml'. Там же содержатся сведения о системе, - которую требуется собрать: её название, версия, номер билда и кодовое - имя релиза. - """ - - def __init__(self, port: cd.port): - self.port = port - self.metadata = toml.load(f"{port.path}/port.toml") - - def _get_fname(self): - url = self.metadata['port'].get('url') - file = self.metadata['port'].get('file') - - if url is None: - raise ValueError - - if file is not None: - return file - - return wget.detect_filename(url) - - def _get_port_info(self) -> None: - return - - def download(self): - if self.metadata['port']['url'] == "none": - return True - - file = f"{CACHE_DIR}/{self._get_fname()}" - - wget.download(self.metadata['port']['url'], CACHE_DIR) - print() - - return os.path.isfile(file) - - def unpack(self) -> bool: - if self.metadata['port']['url'] == "none": - return True - - if self.metadata['port'].get('file') is not None: - file = self.metadata['port']['file'] - else: - file = f"{CACHE_DIR}/{self._get_fname()}" - - try: - with tarfile.open(file, 'r') as f: - f.extractall(path=CACHE_DIR) - except tarfile.ReadError: - Msg.err(f"Error reading file '{file}'!") - return False - except tarfile.CompressionError: - Msg.err(f"File '{file}' compression error!") - return False - except tarfile.ExtractError: - Msg.err(f"Error unpacking file '{file}'!") - return False - - return True - - def install(self) -> int: - script = f"{self.port.path}/install" - run = subprocess.run(script, shell=True) - - return run.returncode - - -class BuildProgress(object): - """A class for getting information about ports that have already been collected. - - Arguments: - - :arg port: str # Optional argument. If not specified, the - # 'check_builded' and 'set_builded' methods will not - # work. - - Methods: - - - check_builded() - checks if the specified port is built or not - - set_builded() - sets the specified port as built - - set_build_time() - sets the time of building all ports - - get_builded_ports() - Gets a list of all built ports - """ - - def __init__(self, port=None): - self.port = port - self.data = toml.load(INFO_FILE) - - if not os.path.isfile(INFO_FILE): - Msg.err(f"File '{INFO_FILE}' is not a file or not found") - - if os.path.isdir(INFO_FILE): shutil.rmtree(INFO_FILE) - - if not os.path.exists(INFO_FILE): - Msg.err(f"File '{INFO_FILE}' not found in the filesystem!") - Msg.msg("Create a base configuration in the '{INFO_FILE}'...") - - self._create_initial_conf() - - def _create_initial_conf(self) -> None: - """Creates a base configuration file ('cport_cli.bs.INFO_FILE') if - assigned.""" - - data = { - "ports": { - "builded": [], - "build_time": 0 - } - } - - with open(INFO_FILE, 'w') as f: - toml.dump(data, f) - - def _set_config(self) -> None: - """This method writes the 'self.data' value to the INFO_FILE config file - after doing something to change the config""" - - with open(INFO_FILE, 'w') as f: - toml.dump(self.data, f) - - def check_builded(self) -> bool: - if self.port is not None: - return self.port in self.data['ports']['builded'] - else: - raise ValueError - - def set_builded(self) -> None: - self.data['ports']['builded'].append(self.port) - self._set_config() - - # with open(INFO_FILE, 'w') as f: - # toml.dump(self.data, f) - - def set_build_time(self, tm: float) -> None: - _tm = self.data['ports']['build_time'] - self.data['ports']['build_time'] = _tm + tm - self._set_config() - - # with open(INFO_FILE, 'w') as f: - # toml.dump(self.data, f) - - def get_builded_ports(self) -> list[str]: - return self.data['ports']['builded'] - - -def build_pkgs(edition: str = "toolchain") -> int: - md = toml.load(f"{cC.PORTS_DIR}/bs/build_order.toml") - ports = md['editions'][edition] - - count = 1 - count_all = len(ports) - - Msg.msg("Start the build machine...") - time_start_bs = time.time() - - for port in ports: - progress = BuildProgress(port) - if progress.check_builded(): - Msg.msg(f"Port '{port}' is already builded") - continue - - Msg.msg(f"({count}/{count_all}) Install port '{port}'...") - - bs = BuildMachine(cd.port(port)) - time_start = time.time() - - Msg.msg(" Download port...") - r = bs.download() - - if not r: - return 1 - - Msg.msg(" Unpack port...") - r = bs.unpack() - - if not r: - return 1 - - Msg.msg(" Build port...") - r = bs.install() - - time_end = time.time() - Msg.msg(f"Build time (seconds): {time_end - time_start}") - - if r != 0: - Msg.err(f"Port '{port}' returned a value other then 0! Rebuild it (1), break (2) or continue building (3)?") - run = input() - Msg.log_msg(f" input: '{run}'") - - if run == "1": - build_pkgs(edition) - elif run == "3": - progress.set_builded() - continue - elif run == "2": - return r - else: - Msg.err("Unknown input!") - return r - - count += 1 - progress.set_builded() - - time_end_bs = time.time() - - progress_t = BuildProgress() - progress_t.set_build_time(time_end_bs - time_start_bs) - - return 0 diff --git a/src/cport_cli/info.py b/src/cport_cli/info.py deleted file mode 100644 index 5a8c0b5f003e0f22b793cc3e2c613359a14c5450..0000000000000000000000000000000000000000 --- a/src/cport_cli/info.py +++ /dev/null @@ -1,540 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -import json -import toml - -import libcport.constants as ccc -import libcport.exceptions as ce -import libcport.core as cc -import libcport.datatypes as cd -import libcport.info as ci -import libcport.dependencies as ccd - - -class CPortDependenciesCLI: - - def __init__(self, ports: list[cd.port]): - self.ports: list[cd.port] = ports - self.port_deps: list[cd.port] = [] - - def _get_selected_ports(self) -> list[dict[str, str, bool]]: - """Returns information about user-selected ports: - - [ - { - "name": "base/acl", - "version": "1.1", - "installed": True - }, - { - "name": "base/attr", - "version": "1.2", - "installed": False - } - ... - ] - """ - - ports: list[dict] = [] - - for port in self.ports: - try: - _info = ci.CPortInfo(port) - pkg: dict = _info.package() - - data = { - "name": pkg['name'], - "version": pkg['version'], - "installed": pkg['installed'] - } - - if port not in ports: - ports.append(data) - except ce.PortNotFoundError as e: - cc.Msg.err(f"Port '{e}' not found!") - break - - return ports - - def _get_dependencies(self) -> list[dict[str, str]]: - """Returns a list of all required software for installation: - - [ - {'name': 'base/m4', 'version': '1.4.19'}, - {'name': 'base/shadow', 'version': '4.12.3'}, - {'name': 'base/openssl', 'version': '3.0.5'}, - {'name': 'base/perl', 'version': '5.36.0'}, - {'name': 'base/readline', 'version': '8.1.2'}, - {'name': 'base/ncurses', 'version': '6.3'} - ]""" - - ports: list[dict] = [] - ports_processed: list[str] = [] - - for port in self.ports: - try: - cdr = ccd.CDependencyResolver(port) - deplst = cdr.gen_deplist() - - for dep in deplst: - _info = ci.CPortInfo(cd.port(dep)) - pkg = _info.package() - - data = { - "name": dep, - "version": pkg['version'] - } - - condition = all([ - data not in self.port_deps, - cd.port(data['name']) not in self.ports, - data['name'] not in ports_processed - ]) - - if condition: - self.port_deps.append(cd.port(dep)) - ports.append(data) - ports_processed.append(data['name']) - - except ce.PortNotFoundError as e: - cc.Msg.err(f"Port '{e}' not found!") - break - - return ports - - def _calculate_usage(self) -> float: - usage = 0.0 - for category in self.ports, self.port_deps: - for port in category: - _info = ci.CPortInfo(port) - pkg = _info.package() - - usage += float(pkg['usage']) - - return usage - - def print_selected_ports(self) -> None: - ports = self._get_selected_ports() - - print("\033[1mSelected ports:\033[0m") - print("\t", end=" ") - - for port in ports: - print(f"{port['name']}-{port['version']}", end=" ") - print("\n") - - def print_deps(self) -> None: - ports = self._get_dependencies() - - if len(ports) == 0: - return - - print("\033[1mPorts that are not installed on the system:\033[0m") - print("\t", end=" ") - - for port in ports: - print(f"{port['name']}-{port['version']}", end=" ") - print("\n") - - def print_usage(self) -> None: - print("\033[1mHow much space will be taken up on the disk:\033[0m", end=" ") - print(f"{self._calculate_usage()} Mb") - - -class CPortDependenciesJSON(CPortDependenciesCLI): - - def __init__(self, ports: list[cd.port]): - super().__init__(ports) - - def print_selected_ports(self) -> None: - print("\033[1mSelected ports:\033[0m") - data: str = json.dumps(self._get_selected_ports(), indent=4) - print(data) - - def print_deps(self) -> None: - print("\033[1mPorts that are not installed on the system:\033[0m") - data: str = json.dumps(self._get_dependencies(), indent=4) - print(data) - - -class CPortInfoCLI: - - def __init__(self, ports: list[cd.port]): - self.ports: list[cd.port] = ports - - def _data(self, port: cd.port, mode: str): - info = ci.CPortInfo(port) - modes = { - "package": { - "func": info.package, - "msg": "Base information:" - }, - "deps": { - "func": info.deps, - "msg": "\nDependencies:" - } - } - return modes[mode]['func'], modes - - def _print(self, port: cd.port, mode: str): - if port not in self.ports: - return - - tp_str = "" - func, modes = self._data(port, mode) - - if func() is None or len(func().keys()) == 0: - return - - print(f"\033[1m{modes[mode]['msg']}") - - for i in func().keys(): - if type(func()[i]) == list: - for index in func()[i]: - tp_str += str(index) + " " - else: - tp_str = func()[i] - - print("\033[1m{0:12}:\033[0m {1}".format(i, tp_str)) - tp_str = "" # Clear string... - - def info(self): - for port in self.ports: - cc.Msg.header(f"Information about port '{port.name}'") - - for mode in "package", "deps": - self._print(port, mode) - - print("\n") - - -class CPortInfoJSON(CPortInfoCLI): - - def __init__(self, ports: list[cd.port]): - super().__init__(ports) - - def info(self): - lst = [] - for port in self.ports: - dt = {'name': port.name} - - for mode in "package", "deps": - md, _ = self._data(port, mode) - dt[mode] = md() - lst.append(dt) - - print(json.dumps(lst, indent=4)) - # print(lst) - - -def is_installed(name: str) -> int: - try: - name = cd.port(name) - db = cc.CDatabase(name) - except ce.PortNotFoundError: - cc.Msg.err(f"Port '{name}' not found!") - return 1 - except ce.PortBrokenError: - cc.Msg.err(f"Port '{name}' is broken!") - return 1 - except toml.decoder.TomlDecodeError: - cc.Msg.err(f"Error decoding the configuration file of the port '{name}'!") - return 1 - - print("{:15} :".format(name.name), end=" ") - if db.check(): - print("installed") - code = 0 - else: - print("not installed") - code = 1 - - db.close() - return code - - -def is_exist(port: str) -> bool: - try: - cd.port(port) - print("{0:15}: {1}".format(port, "exist")) - return True - except ce.PortNotFoundError: - print("{0:15}: {1}".format(port, "not exist")) - return False - except ce.PortBrokenError: - cc.Msg.err(f"Port '{port}' is broken!") - return False - except toml.decoder.TomlDecodeError: - cc.Msg.err(f"Error decoding the configuration file of the port '{port}'!") - return False - - -def ports_check(args) -> int: - """ - elif args.check_all: - return check_deps() - elif args.plint: - return port_linter() - """ - - if args.check_exist: - if is_exist(args.check_exist): - return 0 - else: - return 1 - elif args.check_installed: - return is_installed(args.check_installed) - else: - data = toml.load(ccc.METADATA_FILE) - - releases = "" - categories = "" - - for i in data['system']['release']: - releases += str(i) + " " - - for i in data['port_sys']['categories']: - categories += str(i) + " " - - print("\033[1mBase information about ports system:\033[0m") - print("{0:16}: {1}".format('calmira releases', releases)) - print("{0:16}: {1}".format('categories', categories)) - - return 0 - - -""" -def json_info(name: port) -> None: - conf = toml.load(f"{name.path}/port.toml") - print(json.dumps(conf, ensure_ascii=False, indent=4)) - - -def info(name: port) -> None: - conf = toml.load(f"{name.path}/port.toml") - package = [ - "name", "version", "maintainer", - "releases", "priority", "usage", - "upgrade_mode", "build_time", - "description" - ] - deps = [ - "required", "recommend", - "optional", "conflict" - ] - - package_conf = conf.get('package') - deps_conf = conf.get('deps') - - def form(params: list, data, header: str, prt: bool = False) -> None: - tp_str = "" - if data is None: - return - - print(f"\033[1m{header}:\033[0m") - for param in params: - if data.get(param) is not None: - print("{:15}:".format(param), end=" ") - if type(data[param]) == list: - for i in data[param]: - if prt: - # TODO: добавить выделение портов жёлтым цветом в - # случае, если порт установлен в систему, однако в - # системе отсутствуют какие-либо файлы (статусы 2 и - # 3), либо версия в базе данных не соответствует - # версии в системе портов. - try: - # В том случае, если функция вызывается для - # показа зависимостей, то мы берём каждую из - # зависимостей и проверяем её наличие в базе - # данных. В том случае, если порт в базе - # присутствует, то выделить его зелёным цветом. - # Если порт в БД отсутствует, то не выделять - # никак. Если порт отсутствует в системе портов - # вообще, то выделить его красным цветом. - port_i = port(i) - db = CDatabase(port_i) - if db.check(): - i = f"\033[32m{i}\033[0m" - db.close() - except ce.PortNotFoundError: - i = f"\033[31m{i}\033[0m" - tp_str += str(i) + " " - print(tp_str) - tp_str = "" - else: - print(data[param]) - - cd = CDatabase(name) - form(package, package_conf, "Base information") - print("{0:15}: {1}".format("installed", cd.check())) - form(deps, deps_conf, "\nDependencies", prt=True) - cd.close() -""" - - -def port_linter() -> int: - cl = cc.CList() - code = 0 - - ports = cl.all() - - for prt in ports: - try: - data = cc.p_lint(prt) - - if data: - cc.Msg.ok(f"Port '{prt}': ok") - else: - cc.Msg.err(f"Port '{prt}': unknown error!") - code = 1 - except ce.FilePortTomlError: - cc.Msg.err(f"Port '{prt}': 'port.toml' file is broken or doesn't exist!") - except ce.FileFilesListError: - cc.Msg.err(f"Port '{prt}': 'files.list' file is broken or doesn't exist!") - - return code - - -def check_deps() -> int: - cl = cc.CList() - ports = cl.all() - ports_broken = cl.broken() - - deps_types = ('required', 'recommend', 'optional', 'conflict') - - used_prts = [] # Уже проверенные порты - errors = [] # Отсутствующие порты - broken = 0 # Битые порты - oks = [] # Присутствующие порты - - print("\033[3m\033[41m \033[0m - depends exists;", end=" ") - print(f"\033[3m\033[42m \033[0m - depends does not exist\n{'-' * 80}") - - for name in ports: - if name in ports_broken: - cc.Msg.err(f"Port '{name}' is broken!") - broken += 1 - continue - - name = cd.port(name) - - prt_data = toml.load(f"{name.path}/port.toml") - prt_deps = prt_data['deps'] - - for dep_type in deps_types: - if prt_deps.get(dep_type) is None: - # Пропуск в том случае, если у порта нет зависимостей - continue - deps = prt_deps[dep_type] - - for dep in deps: - if dep in used_prts: - # Пропуск в том случае, если порт уже проверен - проверенные - # порты добавляются в список 'used_prts' - continue - - if dep in ports: - oks.append(dep) - used_prts.append(dep) - else: - errors.append(dep) - used_prts.append(dep) - - if len(errors) > 0 or len(oks) > 0: - used_prts.sort() - for name in used_prts: - if name in errors: - print(f"\033[3m\033[41m \033[0m {name}") - if name in oks: - print(f"\033[3m\033[42m \033[0m {name}") - - print(f"Existing depends: \033[32m{len(oks)}\033[0m") - print(f"Non-existing depends: \033[31m{len(errors)}\033[0m") - print(f"Broken depends: \033[31m{broken}\033[0m") - return 0 - else: - return 0 - - -def get_deps(name: cd.port) -> dict: - """ - Returns a dictionary listing all ports that have the specified port as a - dependency. - - Return value: - - { - "required": [], - "recommend": [], - "optional": [] - } - """ - - cl = cc.CList() - all_ports = cl.all() - suggest = { - "required": [], - "recommend": [], - "optional": [] - } - dplst = ('required', 'recommend', 'optional') - - for prt in all_ports: - prt_dt = cd.port(prt) - data = toml.load(f"{prt_dt.path}/port.toml") - deps = {} - - for dep in dplst: - deps[dep] = data['deps'].get(dep) - - for dep in dplst: - if deps[dep] is not None: - if name.name in deps[dep]: - suggest[dep].append(prt) - return suggest - - -def ports_check(args) -> int: - # FIXME: функция не работает!!! - if args.check_exist: - return is_exist(args.check_exist) - elif args.check_installed: - return is_installed(args.check_installed) - elif args.check_all: - return check_deps() - elif args.plint: - return port_linter() - else: - cl = cc.CList() - data = cl.all() - - releases = "" - categories = "" - - for i in data['system']['release']: - releases += str(i) + " " - - for i in data['port_sys']['categories']: - categories += str(i) + " " - - print("\033[1mBase information about ports system:\033[0m") - print("{0:16}: {1}".format('calmira releases', releases)) - print("{0:16}: {1}".format('categories', categories)) - - return 0 diff --git a/src/cport_cli/install.py b/src/cport_cli/install.py deleted file mode 100644 index c9791432299688b5c6aadfaf87c6a779b659bca3..0000000000000000000000000000000000000000 --- a/src/cport_cli/install.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -import tarfile -import zipfile -from urllib.error import URLError - -import libcport.core as cc -import libcport.datatypes as cd -import libcport.install as ci - - -def install(port: cd.port, ignore_db: bool, skip_check: bool) -> int: - db = cc.CDatabase(port) - inst = ci.CInstall(port) - - if db.check() and not ignore_db: - cc.Msg.warn(f"Port '{port.name}' already in the database!") - cc.Msg.msg("Rebuilding...") - - try: - if inst.check_fname(): - cc.Msg.msg(f"Download port '{port.name}'...") - if inst.download(): - cc.Msg.ok("Download OK") - else: - cc.Msg.err("Download \033[1mERROR\033[0m") - return 1 - - cc.Msg.msg("Validating source file with md5sums...", endl=" ") - if inst.cksum(): - print("Passed") - else: - cc.Msg.warn("NOT passed") - run = cc.Msg.dialog() - - if not run: - cc.Msg.err("Aborted!") - return 1 - - cc.Msg.msg(f"Extracting '{port.name}'...") - inst.unpack() - - cc.Msg.msg(f"Building port '{port.name}'...") - - run = inst.install() - - if run != 0: - cc.Msg.err(f"Build instructions was returned a value other then 0!") - return run - - cc.Msg.ok("Build OK!") - - install_status = 0 - - if not ignore_db: - try: - install_status = cc.check(port) - cc.Msg.msg("Updating the database...", endl=" ") - db.add(status=install_status) - print("OK") - except FileNotFoundError: - cc.Msg.err(f"File '{port.path}/files.list' not found!") - return 1 - - if not skip_check: - cc.Msg.msg(f"Checking the port '{port.name}' for correct installation...", endl=" ") - if install_status == 0: - cc.Msg.warn("Some port files were not found!") - elif install_status == 3: - cc.Msg.err("Port installation error!") - run = 1 - else: - cc.Msg.ok("Checking completed successfully") - - cc.Msg.msg("Clearing the cache...") - fcount = cc.get_files_in_cache() - cc.wipe_cache() - cc.Msg.ok(f"{fcount} files was deleted!") - - return run - except zipfile.BadZipFile: - cc.Msg.err(f"Bad ZIP file '{inst.get_fname()}'!") - return 1 - except tarfile.ReadError: - cc.Msg.err(f"Error reading file '{inst.get_fname()}'!") - return 1 - except tarfile.CompressionError: - cc.Msg.err(f"File '{inst.get_fname()}' compression error!") - return 1 - except tarfile.ExtractError: - cc.Msg.err(f"Error unpacking file '{inst.get_fname()}'!") - return 1 - except URLError: - cc.Msg.err("Temporary failure in name resolution!") - return 1 diff --git a/src/cport_cli/remove.py b/src/cport_cli/remove.py deleted file mode 100644 index fc1d1ab9b2a8c8e836c021272a50aa74432ac887..0000000000000000000000000000000000000000 --- a/src/cport_cli/remove.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -import libcport.core as cc -import libcport.exceptions as ce -import libcport.remove as cr -import libcport.datatypes as cd - - -def remove(port: cd.port, ignore_db: bool = False, confirm: bool = False, skip_check: bool = False) -> int: - db = cc.CDatabase(port) - - if not db.check() and not ignore_db: - cc.Msg.err(f"Port '{port.name}' doesn't exist in the database!") - return 1 - - rm = cr.CRemove(port) - - if rm.check_priority(): - cc.Msg.err(f"Port '{port.name}' has system priority") - return 1 - - if not confirm and not cc.Msg.dialog(default_no=True): - return 1 - - cc.Msg.msg(f"Remove the '{port.name}' files from the system...") - - try: - prerem = rm.run_preremove() - - if prerem != 0: - cc.Msg.err(f"Execution of pre-remove script failed (code: {prerem}") - return prerem - - rm.remove() - - postrem = rm.run_postremove() - - if postrem != 0: - cc.Msg.err( - f"Execution of post-remove script failed (code: {postrem}" - ) - return postrem - - except FileNotFoundError as e: - cc.Msg.err(f"File not found: {e}") - return 1 - except ce.PortFilesBrokenError: - cc.Msg.err(f"Port '{port.name}' is broken!") - return 1 - - if not skip_check: - cc.Msg.msg("System check...") - check = cc.check(port) - - if check: - cc.Msg.err("System check error!") - # TODO: переделать поведение cc.check(). - # Пусть она возвращает dict со списком - # присутствующих и отсутствующих файлов - - return 1 - - code = 0 - if not ignore_db: - cc.Msg.msg(f"Remove port '{port.name}' from database...", endl=" ") - - db.remove() - if not db.check(): - print("OK") - else: - print("ERROR!") - code = 1 - - db.close() - - return code - return 0 diff --git a/src/cport_cli/shared.py b/src/cport_cli/shared.py deleted file mode 100644 index 6bc1329949c804166e5189519d38672ef1e70387..0000000000000000000000000000000000000000 --- a/src/cport_cli/shared.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - diff --git a/src/cport_cli/shared.sh b/src/cport_cli/shared.sh deleted file mode 100644 index 6cc7d927585f35dd369910502a00f274040f3c5f..0000000000000000000000000000000000000000 --- a/src/cport_cli/shared.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash -e -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -set +h -umask 022 - -CALM="/mnt/calm" -CALM_TGT=$(uname -m)-calm-linux-gnu -LC_ALL=POSIX -PATH=$CALM/tools/bin:$PATH -CONFIG_SITE=$CALM/usr/share/config.site - -export CALM CALM_TGT LC_ALL PATH CONFIG_SITE - -# Log messages -# Usage: log_msg [message] [status] -function log_msg() { - # shellcheck disable=SC2046 - if [ $(whoami) != "root" ]; then - log_file="$HOME/.cache/cport.log" - else - log_file="/var/log/cport.log" - fi - msg="[ `LC_ALL=C date` ] [ $2 ] [ from 'install' file ] $1" - echo "$msg" >> $log_file -} - -# Usage: $1 port -function configuration_log_msg() { - log_msg "[Step 1] Port configuration" "info" -} - -function make_log_msg() { - log_msg "[Step 2] Performing a Port Assembly" "info" -} - -function install_log_msg() { - log_msg "[Step 3] Performing a port install" "info" -} - -function setup_log_msg() { - log_msg "[Step 4] Performing a port setup" "info" -} - -# Usage: -# dialog_msg [default_no = 1 or 0] -function dialog_msg() { - echo -e -n "\nContinue? " - if [ $1 == "0" ]; then - echo -n "[y/N] " - else - echo -n "[Y/n] " - fi - - read run - - if [ $run == "n" ]; then - return 1 - elif [ $run == "y" ]; then - return 0 - else - echo "Unknown input!" - return dialog_msg - fi -} - -function msg() { - echo -n -e "\e[1m==>\e[0m $1" - log_msg $1 "info" -} - -function err_msg() { - echo -n -e "[\e[1;31m!\e[0m] $1" - log_msg $1 "fail" -} - -function warn_msg() { - echo -n -e "[\e[1;33mW\e[0m] $1" - log_msg $1 "warn" -} - -function ok_msg() { - echo -n -e "[\e[1;32m✓\e[0m] $1" - if [ $2 == "0" ]; then - log_msg $1 " ok " - fi -} - -function warn_header() { - echo -e "\n\n\a\e[1;33m--==\e[0m \e[31mWARNING!\e[0m \e[1;33m==--\e[0m\n$1" -} - -function err_header() { - echo -e "\n\n\a\e[1;31m--==\e[0m \e[31mERROR!\e[0m \e[1;31m==--\e[0m\n$1" -} - -function note_header() { - echo -e "\n\n\a\e[1;32m--==\e[0m \e[1;33mNOTE!\e[0m \e[1;32m==--\e[0m\n$1" -} diff --git a/src/cport_cli/update.py b/src/cport_cli/update.py deleted file mode 100644 index e9950f69fc2c0e39f3836f85440debeb88f1d52d..0000000000000000000000000000000000000000 --- a/src/cport_cli/update.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -from shutil import get_terminal_size -from urllib.error import URLError - -import toml - -import libcport.core as cc -from libcport.constants import CACHE_UNPACKED_DIR -from libcport.constants import CALMIRA_RELEASE -from libcport.datatypes import port -from libcport.update import Update - - -def check() -> int: - """ - Принцип работы `check()`: - - - Скачивание архива системы портов из репозитория; - - Распаковка архива системы портов в кеш (`CACHE_UNPACKED_DIR`); - - Получение номера версии Calmira GNU/Linux-libre; - - Сравнение версии дистрибутива Calmira и релиза, указанного в метаданных СП: - - если версия установленного дистрибутива не содержится в метаданных СП, прервать обновление; - - Получение списка изменений: - - все изменения; - - добавленные порты; - - удалённые порты; - - обновлённые порты. - - Формирование списка изменений в том случае, если хотя бы один из списков >= 1: - - вывод всего, что добавлено; - - вывод всего, что удалено; - - вывод обновлений (указание старой версии - которая в установленной СП, указаное новой версии из новой СП); - - Обновление системы портов: - - Удаление существующей СП в `PORTS_DIR`; - - Копирование скачанной распакованной СП в `PORTS_DIR`; - """ - - u = Update(CACHE_UNPACKED_DIR) - try: - cc.Msg.msg("Downloading the port system...") - u.get() - except URLError: - cc.Msg.err("Temporary failure in name resolution!") - return 1 - - cc.Msg.msg(f"Unpacking ports in '{CACHE_UNPACKED_DIR}'...") - u.unpack() - - try: - rel = u.check_release() - except KeyError: - cc.Msg.err(f"File '{CALMIRA_RELEASE}' is not valid file!") - return 1 - - if not rel: - ports_rel_metadata = u._get_metadata(mode=1)['system']['release'] - sys_rel = cc.get_calm_release() - - ports_rel = "" - for i in ports_rel_metadata: - ports_rel += str(i) + " " - - print(f"The port system is for version: {ports_rel}") - print(f"Version system installed: {sys_rel}") - err_msg = "The ports system is not compatible with the current " \ - "release of Calmira GNU/Linux(-libre)!" - cc.Msg.err(err_msg) - - return 1 - - cc.Msg.msg("Generating list of changes...") - - try: - added = u.get_added_ports() - removed = u.get_removed_ports() - updates = u.get_updated_ports() - other = u.get_other_ports() - except KeyError: - cc.Msg.err( - "Error parsing the metadata of the installed or updated ports system" - ) - return 1 - - width = get_terminal_size()[0] - - if width < 80: - cc.Msg.warn("The terminal is very small! Required width: 80 or more chars.") - return 1 - - if len(added) > 0 or len(removed) > 0 or len(updates) > 0 or len(other) > 0: - pass - else: - cc.Msg.err("No updates.") - return -1 - - if len(added) > 0 or len(removed) > 0: - print("\n\033[1mAdditions/deletions:\033[0m") - print("| {0:58} | {1:15} |".format("name", "status")) - print("|{0}|{1}|".format(60 * '-', 17 * '-')) - - if len(added) > 0: - for i in added: - print("| {0:58} | {1:15} |".format(str(i), "added")) - if len(removed) > 0: - for i in removed: - print("| {0:58} | {1:15} |".format(str(i), "removed")) - - if len(updates) > 0: - print("\n\033[1mUpgrades/downgrades:\033[0m") - print( - "| {0:40} | {1:15} | {2:15} |".format( - "name", "old version", "new version" - ) - ) - print("|{0}|{1}|{1}|".format(42 * '-', 17 * '-')) - - for change in updates: - name = change['name'] - vers = change['version'] - - db = cc.CDatabase(port(name)) - if db.check(): - name = f"[i] {name}" - - print( - "| {0:40} | {1:15} | {2:15} |".format( - name, vers['old'], vers['new'] - ) - ) - - if len(other) > 0: - print("\n\033[1mOther changes:\033[0m") - print("| {0:58} | {1:15} |".format("name", "version")) - print("|{0}|{1}|".format(60 * '-', 17 * '-')) - - for name in other: - port_name = port(name) - data = toml.load(f"{port_name.path}/port.toml") - vers = data['package']['version'] - print("| {0:58} | {1:15} |".format(name, vers)) - - return 0 diff --git a/src/cport_cli/upgrade.py b/src/cport_cli/upgrade.py deleted file mode 100644 index a6ec5ea5ad0ebacfeceb0ef6ed3c15b26067277a..0000000000000000000000000000000000000000 --- a/src/cport_cli/upgrade.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/python3 -# -# 'upgrade.py' -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -import cport_cli.install as ci -import cport_cli.remove as cr -import libcport.core as cc -import libcport.exceptions as ce -import toml -from libcport.datatypes import port - - -def check(name: port) -> bool: - # TODO: добавить проверку соответствия версии установленного - # порта и порта в СП - - db = cc.CDatabase(name) - code = db.check() - db.close() - - return code - - -def upgrade_soft(name: port) -> int: - db = cc.CDatabase(name) - if not db.check(): - cc.Msg.err(f"Error: port '{name.name}' not in database!") - return 1 - - db.remove() - db.close() - - run = ci.install(name, False, False) - - if run != 0: - return run - return 0 - - -def upgrade_hard(name: port) -> int: - if not check(name): - cc.Msg.err(f"Error: port '{name.name}' not in database!") - return 1 - - run = cr.remove(name) - - if run != 0: - return run - - run = ci.install(name, False, False) - - if run != 0: - return run - return 0 - - -def main(args: list[str]) -> int: - for arg in args: - try: - arg = port(arg) - data = toml.load(f"{arg.path}/port.toml") - upgrade_mode = data['package']['upgrade_mode'] - - if upgrade_mode == "soft": - run = upgrade_soft(arg) - elif upgrade_mode == "hard": - run = upgrade_hard(arg) - else: - cc.Msg.err(f"Port '{arg.name}' is broken:" \ - f" 'package.upgrade_mode' is invalid: '{upgrade_mode}'") - cc.Msg.err("Valid names: soft & hard") - return 1 - - if run != 0: - return run - except ce.PortNotFoundError: - cc.Msg.err(f"Error: port '{arg}' not found!") - return 1 - except toml.decoder.TomlDecodeError: - cc.Msg.err("Error decoding the configuration file of the port" \ - f" '{arg}'!") - return 1 - return 0 diff --git a/src/libcport/__init__.py b/src/libcport/__init__.py deleted file mode 100644 index 8d1c8b69c3fce7bea45c73efd06983e3c419a92f..0000000000000000000000000000000000000000 --- a/src/libcport/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/libcport/constants.py b/src/libcport/constants.py deleted file mode 100644 index 3d864dca4e0e9db859ea529a31f0960266285808..0000000000000000000000000000000000000000 --- a/src/libcport/constants.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/python -# -# constants.py - base constants, variables and settings from the -# '/etc/cport.toml' file for cport -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -import os -import toml - -home_dir = os.environ.get('HOME') - -BASE_CONFIG = { - "repo": { - "url": "https://gitlab.com/calmiralinux/cabs/Ports", - "branch": "testing" - }, - "ports": { - "path": "/usr/ports", - "werror": "no" - }, - "build_system": { - "use_cache": "yes", - "resolve_deps": "no" - } -} - -if not os.path.exists('/etc/cport.conf'): - print("-- cport kernel -- create initial config...") - try: - with open('/etc/cport.conf', 'w') as f: - toml.dump(BASE_CONFIG, f) - except PermissionError: - print("-- cport kernel -- Permission denied!") - -data = toml.load('/etc/cport.conf') - -# CONSTANTS -NAME: str = "cport" -VERSION: str = "1.0current_beta" -PORTS_DIR: str = data['ports'].get('path') -CACHE_DIR: str = "/var/cache/cport" -CACHE_DOWNLOADED_DIR: str = f"{CACHE_DIR}/downloads" -CACHE_UNPACKED_DIR: str = "/usr/src" -CONF_FILE: str = "/etc/cport.conf" -LIB_DIR: str = "/var/lib/Calmira" -LOCAL_LIB_DIR: str = f"{home_dir}/.local/share/Calmira" -DATABASE_FILE: str = f"{LIB_DIR}/software.db" -DATABASE_USER_FILE: str = f"{LOCAL_LIB_DIR}/software.db" -METADATA_FILE: str = f"{PORTS_DIR}/metadata.toml" -CALMIRA_RELEASE: str = "/etc/calm-release" -LOG_FILE: str = "/var/log/cport.log" -USER_LOG_FILE: str = f"{home_dir}/.cache/cport.log" - -UPDATE_BRANCH: str = data['repo'].get('branch') -PORTS_REPO_URL: str = data['repo'].get('url') -WERROR: bool = data['ports'].get('werror') -USE_CACHE: str = data['build_system'].get('use_cache') -RESOLVE_DEPS: str = data['build_system'].get('resolve_deps') - -PORT_TOML_PARAMS: dict = { - "package": [ - "name", "version", "description", "maintainer", - "releases", "priority", "usage", "upgrade_mode", - "build_time", "description" - ], - "deps": [ - "required", "recommend", "optional" - ], - "port": [ - "url", "md5", "sha256", "file" - ] -} -# Статусы секций: обязательная (True) и необязательная (False) -PORT_TOML_STATUSES: dict = { - "package": True, - "deps": False, - "port": False -} diff --git a/src/libcport/core.py b/src/libcport/core.py deleted file mode 100644 index 505fd199a8bef75825d12c8ecd2286eb956adb3c..0000000000000000000000000000000000000000 --- a/src/libcport/core.py +++ /dev/null @@ -1,520 +0,0 @@ -#!/usr/bin/python3 -# -# core.py - core objects for cport package manager -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -""" -'core.py' is the main cport source code that is the core of this package -manager. Contains basic functionality for getting information about the Calmira -GNU/Linux-libre distribution (required to check the port for compatibility with -the current release of Calmira). - -Tasks solved by this module: - -* Obtaining information about the installed system; -* Checking for files belonging to a specific port; -* More high-level interaction with the database; -""" - -import libcport.datatypes as cd -import libcport.exceptions as ce -import libcport.constants as cc - -import os -import sys -import toml -import time -import shutil -import sqlite3 - - -def dbg_msg(msg: str, endl="\n") -> None: - # CPORT_DBG='yes' or CPORT_DBG='no' or CPORT_DBG is not set - cport_dbg: str | None = os.environ.get('CPORT_DBG') - - if cport_dbg is not None and cport_dbg == "yes": - print(f"[DEBUG] {msg}", end=endl) - - with open('.cport.debug.log', 'a') as f: - msg = fr"{msg}" - f.write(f"{msg}\n") - - -def ckuser() -> bool: - """Checks if cport is running as root. - If running, returns True, otherwise - returns False.""" - - return os.geteuid() == 0 - - -def get_calm_release() -> str: - data = toml.load(cc.CALMIRA_RELEASE) - return data['system']['version'] - - -def check(name: cd.port) -> int | None: - """Checks if a port is installed to the system or not. - - Statuses: - - 0 - 100% files; - - 1 - 75-99% files; - - 2 - 50-74% files; - - 3 - 5-49% files; - """ - - files_list = f"{name.path}/files.list" - fcount = len(files_list) - f_install_count = 0 - - with open(files_list) as f: - dbg_msg(f"[check] open '{files_list}'...") - files = f.read().splitlines() - - for file in files: - if not os.path.exists(file): - dbg_msg(f"[files checking...] File '{file}':\033[31m not found\033[0m") - else: - f_install_count += 1 - - if f_install_count == fcount: - return 0 - elif f_install_count >= (fcount / 0.75): - return 1 - elif f_install_count >= (fcount / 0.5): - return 2 - elif f_install_count >= (fcount / 0.05): - return 3 - - -def p_lint(name: str) -> bool: - """Ports linter. Checks that the `port.toml` and - `files.list` files of the port are correct""" - - try: - path = cd.port(name).path - except ce.PortBrokenError: - return False - except ce.PortNotFoundError: - return False - - ret_code = True - - # Checking the "port.toml" file - try: - dbg_msg("[p_lint] checking the 'port.toml' file") - - data = toml.load(f"{path}/port.toml") - - # Проверка наличия секций "package" и "port" - if data.get('package') is None or data.get('port') is None: - raise ce.FilePortTomlError - - # Проверка наличия необходимых параметров в секции "package" - # Параметры в секции "port" являются необязательными - for param in cc.PORT_TOML_PARAMS["package"]: - if data["package"].get(param) is None: - raise ce.FilePortTomlError - - except toml.TomlDecodeError: - raise ce.FilePortTomlError - except FileNotFoundError: - raise ce.FilePortTomlError - - # Checking the "files.list" file - try: - dbg_msg("Checking the 'files.list' file") - dbg_msg(f"{path}/files.list") - - with open(f"{path}/files.list") as f: - flist = f.read().splitlines() - - if len(flist) < 1: - raise ce.FileFilesListError - - except FileNotFoundError: - raise ce.FileFilesListError - - return ret_code - - -def get_files_in_cache() -> int: - dirs = (cc.CACHE_UNPACKED_DIR, cc.CACHE_DOWNLOADED_DIR) - files = 0 - - for _dir in dirs: - dbg_msg(f"[get_files_in_cache] work directory: '{_dir}'") - - fls = os.listdir(_dir) - files += len(fls) - for _ in os.walk(_dir): - files += 1 - - dbg_msg(f"Files count: {files}") - return files - - -def wipe_cache() -> None: - """This method wipes all data in the cache""" - - dirs = (cc.CACHE_UNPACKED_DIR, cc.CACHE_DOWNLOADED_DIR) - - for _dir in dirs: - dbg_msg(f"Clearing the '{_dir}' directory...") - fls = os.listdir(_dir) - for file in fls: - file = f"{_dir}/{file}" - dbg_msg(f"Detect the '{file}' file") - if os.path.isfile(file): - os.remove(file) - dbg_msg("This is file. Removed") - elif os.path.isdir(file): - shutil.rmtree(file) - dbg_msg("This is a dir. Removed") - - -class CDatabase(object): - - def __init__(self, name: cd.port, dbfile: str = cc.DATABASE_FILE): - self.name = name - - dbg_msg(f"Работа с БД по порту '{name.name}'") - - with open(f"{name.path}/port.toml") as f: - self.port_data = toml.load(f) - - self.con = sqlite3.connect(dbfile) - self.cur = self.con.cursor() - - def close(self) -> None: - self.cur.close() - - def create_initial_db(self) -> None: - queries = """CREATE TABLE ports_in_fs ( - name TEXT, version TEXT, description TEXT, maintainer TEXT, - releases TEXT, priority TEXT, usage REAL, upgrade_mode TEXT, - build_time REAL - ); - CREATE TABLE installed_ports ( - name TEXT, version TEXT, description TEXT, maintainer TEXT, - releases TEXT, priority TEXT, usage REAL, upgrade_mode TEXT, - build_time REAL, status INT, build_date REAL - );""" - - self.cur.executescript(queries) - - def add(self, status: int = 0, table: str = "installed_ports") -> None: - """Statuses: - - 0 - 100% files; - - 1 - 75-99% files; - - 2 - 50-74% files; - - 3 - 5-49% files; - """ - - conf = self.port_data - params = ( - 'version', 'description', - 'maintainer', 'releases', 'priority', - 'usage', 'upgrade_mode', 'build_time' - ) - - data = [self.name.name] - - for prm in params: - tp_str = "" - # The next step is to check the received data. In the event that the - # data type 'list' is received, then it is converted to a 'str' - if type(conf['package'].get(prm)) == list: - for i in conf['package'][prm]: - tp_str += str(i) + " " - else: - tp_str = conf['package'].get(prm) - data.append(tp_str) - - data.append(status) # Status - - if table == "installed_ports": - data.append(time.time()) - vlist = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - else: - vlist = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - - db_query = f"INSERT INTO {table} VALUES {vlist}" - self.cur.execute(db_query, data) - self.con.commit() - - def remove(self, table: str = "installed_ports") -> None: - name = self.name.name - self.cur.execute(f"DELETE FROM {table} WHERE name = '{name}'") - self.con.commit() - - def get_one(self, table: str = "installed_ports"): - name = self.name.name - dt = self.cur.execute(f"SELECT * FROM {table} WHERE name = ?", (name,)) - - return dt.fetchone() - - def get_all(self, table: str = "installed_ports"): - """Warning: this method is too slow!""" - - name = self.name.name - dt = self.cur.execute(f"SELECT * FROM {table} WHERE NAME = ?", (name,)) - - return dt.fetchall() - - def check(self, table: str = "installed_ports") -> bool: - query = f"SELECT * FROM {table} WHERE NAME = ?" - dt = self.cur.execute(query, (self.name.name,)) - - return dt.fetchone() is not None - - def execute(self, query: str, params: tuple, commit: bool = False): - data = self.cur.execute(query, params) - - if commit: - self.con.commit() - - return data.fetchall() - - def gen_all_ports(self) -> None: - metadata = toml.load(cc.METADATA_FILE) - ports = metadata['port_sys']['ports'] - - self.cur.execute("DELETE FROM ports_in_fs") - - for port in ports: - port = cd.port(port) - port_conf = toml.load(f"{port.path}/port.toml") - data = [port.name] - params = ( - 'version', 'description', 'maintainer', - 'releases', 'priority', 'usage', - 'upgrade_mode', 'build_time' - ) - - for param in params: - tp_str = "" - if type(port_conf['package'].get(param)) == list: - # Converting list to str - for i in port_conf['package'][param]: - tp_str += str(i) + " " - else: - tp_str = port_conf['package'].get(param) - data.append(tp_str) - - db_query = "INSERT INTO ports_in_fs VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" - self.cur.execute(db_query, data) - self.con.commit() - - -class CList(object): - - def __init__(self): - self.md = toml.load(cc.METADATA_FILE) - self.ports = self.md['port_sys']['ports'] - - def all(self) -> list[str]: - return self.ports - - def installed(self) -> list[tuple[str, float]]: - """Returns list of tuples with values: - - (port name, installation date) - - Types: - - (str, float) - - The installation time is returned in UNIX Time format. - """ - - ports_list = [] - - for port in self.ports: - dbg_msg(f"Selected port '{port}'...") - - try: - db = CDatabase(cd.port(port)) - - if db.check(): - dbg_msg("This port is installed") - - data = db.get_one() - ports_list.append((data[0], data[-1])) - - db.close() - except ce.PortNotFoundError: - dbg_msg(f"Port '{port}' not found") - continue - except ce.PortBrokenError: - dbg_msg(f"Port '{port}' is broken") - continue - - return ports_list - - def not_installed(self) -> list[str]: - ports_list = [] - for port in self.ports: - try: - db = CDatabase(cd.port(port)) - if not db.check(): - db.close() - ports_list.append(port) - db.close() - except ce.PortNotFoundError: - dbg_msg(f"Port '{port}' not found") - continue - except ce.PortBrokenError: - dbg_msg(f"Port '{port}' is broken") - continue - return ports_list - - def broken(self) -> list[str]: - ports_list = [] - for port in self.ports: - try: - cd.port(port) - except ce.PortBrokenError: - ports_list.append(port) - return ports_list - - -def rebuild() -> None: - cl = CList() - ports = cl.all() - - for port in ports: - try: - port = cd.port(port) - db = CDatabase(port) - except ce.PortNotFoundError: - dbg_msg(f"-- cport kernel -- port '{port}' not found in the filesystem") - continue - - files_list_file = f"{port.path}/files.list" - - if not os.path.exists(files_list_file): - dbg_msg(f"File '{files_list_file}' of port '{port.name}' not found") - continue - - if db.check(): - dbg_msg(f"port '{port.name}' already in DB! Skipping...") - continue - - with open(files_list_file) as f: - files = f.read().splitlines() - - files_count = 0 - - for file in files: - if os.path.exists(file): - files_count += 1 - - if files_count == len(files): - port_status = 0 - elif len(files) / 0.75: - port_status = 1 - elif files_count >= (len(files) / 0.5): - port_status = 2 - elif files_count >= (len(files) / 0.05): - port_status = 3 - else: - port_status = 4 - - if port_status == 4: - db.close() - continue - - db.add(status=port_status) - db.close() - - -def log_msg(msg: str, status: str = "info") -> None: - file = cc.LOG_FILE if ckuser() else cc.USER_LOG_FILE - - with open(file, 'a') as f: - msg = f"[ {time.ctime()} ] [ {status} ] {msg}" - f.write(msg) - - -class Msg: - - @staticmethod - def log_msg(msg: str, status: str = "info") -> None: - file = cc.LOG_FILE if ckuser() else cc.USER_LOG_FILE - - with open(file, 'a') as f: - msg = f"[ {time.ctime()} ] [ {status} ] {msg}" - f.write(msg) - - @staticmethod - def dialog(default_no: bool = False) -> bool: - print("\n:: Continue?", end=" ") - if default_no: - print("[y/N]", end=" ") - else: - print("[Y/n]", end=" ") - run = input() - if run == "N" or run == "n": - return False - elif run == "Y" or run == "y": - return True - elif run == "": - if default_no: - Msg.warn("I choose the negative option (N)!", log=False) - return False - else: - Msg.warn("I choose the affirmative option (Y)!", log=False) - return True - else: - print("Unknown input!") - return Msg.dialog(default_no) - - @staticmethod - def msg(msg: str, startl="", endl="\n", log: bool = True) -> None: - print(f"{startl}\033[1m==>\033[0m {msg}", end=endl) - if log: - log_msg(f"{startl}{msg}{endl}") - - @staticmethod - def err(msg: str, startl="", endl="\n", log: bool = True) -> None: - print(f"{startl}[\033[1m\033[31m!\033[0m] {msg}", file=sys.stderr, end=endl) - if log: - log_msg(f"{startl}{msg}{endl}", status="fail") - - @staticmethod - def warn(msg: str, startl="", endl="\n", log: bool = True) -> None: - print(f"{startl}[\033[33mW\033[0m] {msg}", file=sys.stderr, end=endl) - if log: - log_msg(f"{startl}{msg}{endl}", status="warn") - - @staticmethod - def ok(msg: str, startl="", endl="\n", log: bool = True) -> None: - print(f"{startl}[\033[32m✓\033[0m] {msg}", end=endl) - if log: - log_msg(f"{startl}{msg}{endl}", status=" ok ") - - @staticmethod - def header(msg: str, color: str = "\033[1m"): - try: - term_size = os.get_terminal_size()[0] - msg = f" {msg} " # В пробелах для того, чтобы выглядело нормально - hdr_msg = msg.center(term_size, '=') - except BrokenPipeError: - hdr_msg = f"--== {msg} --==" - - print(f"{color}{hdr_msg}\033[0m") - log_msg(f"{msg}\n", status="info") diff --git a/src/libcport/datatypes.py b/src/libcport/datatypes.py deleted file mode 100644 index 24507b68a3899f833802bef4b7d5a8b64b9a929e..0000000000000000000000000000000000000000 --- a/src/libcport/datatypes.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/python3 -# -# datatypes.py - the 'port' data type for Python -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -import os -from dataclasses import dataclass -from dataclasses import field - -from libcport.constants import PORTS_DIR -from libcport.exceptions import PortBrokenError -from libcport.exceptions import PortNotFoundError - - -@dataclass -class port: - """ - Usage: - name: libcport.datatypes.port - - Data: - * name - port name; - * path - path to port's files ('/usr/ports/...'); - * files - список файлов из path; - """ - - name: str - path: str = field(init=False) - files: list = field(init=False) - - def __post_init__(self): - self.path = f"{PORTS_DIR}/{self.name}" - - if not os.path.isdir(self.path): - raise PortNotFoundError - - self.is_found = True - self.files = os.listdir(self.path) - files = ('install', 'port.toml') # TODO: in the stable version return file 'files.list' - - for file in files: - if file not in self.files: - raise PortBrokenError(file) diff --git a/src/libcport/dependencies.py b/src/libcport/dependencies.py deleted file mode 100644 index 26fb7c065bb0c616fb45a8af289607904da9aece..0000000000000000000000000000000000000000 --- a/src/libcport/dependencies.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/python3 -# -# dependencies.py -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -from typing import Any - -import libcport.datatypes as cd -import libcport.core as cc -import libcport.exceptions as ce -import libcport.info as ci - - -class CDependencyGenerator: - - """Получает список всех зависимостей указанного порта""" - - def __init__(self, port: cd.port): - self.port = port - - info = ci.CPortInfo(port) - self.port_deps = info.deps() - - self.dependency_list = {} - - def _deps(self, port: cd.port = None) -> list[str]: - if port is None: - # Подразумевается, что в данном случае - # используется имя искомого порта - - return self.port_deps.get('required') - - p_info = ci.CPortInfo(port) - p_deps = p_info.deps() - - return p_deps.get('required') - - def deplist(self) -> dict: - """Gets a list of all dependencies of the specified ports - - Return value: - - >>> a = CDependencyGenerator(cd.port('base/bash')) - >>> a.deplist() - {'base/bash': ['base/binutils', 'base/bison', 'base/coreutils', ...], 'base/binutils': ['base/coreutils', - 'base/diffutils', ...], 'base/bison': [...]} - """ - - deps = self._deps() - if not deps: - return {} - - for dep in deps: - try: - dependency_list = self._deps(cd.port(dep)) - if dependency_list is not None: - self.dependency_list[dep] = dependency_list - except ce.PortNotFoundError: - cc.dbg_msg(f"[CDependencyResolve.deplist] port '{dep}' not found") - - return self.dependency_list - - -class CDependencyResolver(CDependencyGenerator): - - """Форматирует список всех зависимостей порта""" - - def __init__(self, port: cd.port): - super().__init__(port) - self.dependency_form_list = [] - - def _append(self, portname: str) -> None: - """Adds ports to the list if all conditions are true: - - * The port is not yet in the list; - * The port name to add is not the source port name; - * The port to add is not in the 'installed' table of the cport database;""" - - try: - db = cc.CDatabase(cd.port(portname)) - except ce.PortNotFoundError: - return - - data = all([ - portname not in self.dependency_form_list, - portname != self.port.name, - not db.check() - ]) - - if data: - self.dependency_form_list.append(portname) - - def gen_deplist(self) -> list: - """Generates a list of dependencies of the specified ports - - Return value: - - >>> a = CDependencyResolver(cd.port('base/bash')) - >>> a.gen_deplist() - ['base/shadow', 'base/openssl', 'base/m4', 'base/perl', 'base/readline', 'base/ncurses', 'base/glibc'] - """ - - deplist = self.deplist() - - for port in deplist.keys(): - for subport in deplist[port]: - if subport not in self.dependency_form_list: - self._append(subport) - self._append(port) - - return self.dependency_form_list[::-1] - - -def check_conflicts(port1: cd.port, port2: cd.port) -> dict[str, list[Any] | bool]: - """Performs conflict checking between two ports. Return value: - - >>> check_conflicts(cd.port("base/acl"), cd.port("base/attr")) - { - "status": True, - "conflict_files": ["/usr/bin/foo", "/usr/share/foo"] - } - - If there are no conflicts, then 'status' = False, and the - 'conflict_files' list is empty. Otherwise, 'status' = True, - and 'conflict_files' contains a list of conflicting files. - """ - - with open(f"{port1.path}/files.list") as f: - port1_flist = f.read().splitlines() - with open(f"{port2.path}/files.list") as f: - port2_flist = f.read().splitlines() - - data = { - "status": False, - "conflict_files": [] - } - - for file in port1_flist: - if file in port2_flist: - cc.dbg_msg(f"Conflict: \n\t{port1.name} and {port2.name}:\n\t\t{file}") - data['status'] = True - data['conflict_files'].append(file) - - return data diff --git a/src/libcport/download.py b/src/libcport/download.py deleted file mode 100644 index 6ff903e095d5c393319c3934d4d4260643766bf1..0000000000000000000000000000000000000000 --- a/src/libcport/download.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/python3 -# -# download.py -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -import os -import shutil -import tempfile -import urllib.parse -import urllib.request - -import libcport.core as cc - - -def fname_from_url(url: str) -> str | None: - fname = os.path.basename(urllib.parse.urlparse(url).path) - if len(fname.strip(" \n\t.")) == 0: - return None - return fname - - -def fname_from_headers(headers) -> str | None: - if type(headers) == str: - headers = headers.splitlines() - if type(headers) == list: - headers = dict([x.split(':', 1) for x in headers]) - - cdisp = headers.get("Content-Disposition") - if not cdisp: - return None - - cdtype = cdisp.split(";") - if len(cdtype) == 1: - return None - - if cdtype[0].strip().lower() not in ('inline', 'attachment'): - return None - - fnames = [x for x in cdtype[1:] if x.strip().startwith('filename=')] - if len(fnames) > 1: - return None - - name = fnames[0].split('=')[1].strip(' \t"') - name = os.path.basename(name) - - if not name: - return None - return name - - -def detect_filename(url: str, tgt: str = None, headers: list = None) -> str: - names = dict(tgt='', url='', headers='') - if tgt: - names['tgt'] = tgt or '' - if url: - names['url'] = fname_from_url(url) or '' - if headers: - names['headers'] = fname_from_headers(headers) or '' - return names['tgt'] or names['headers'] or names['url'] - - -def download(url: str, tgt: str = None) -> str: - outdir = None - if tgt and os.path.isdir(tgt): - outdir = tgt - - # get filename from temp file in current dir. - prefix = detect_filename(url, tgt) - fd, tmpfile = tempfile.mkstemp(".tmp", prefix=prefix, dir=".") - os.close(fd) - os.unlink(tmpfile) - - binurl = list(urllib.parse.urlsplit(url)) - binurl[2] = urllib.parse.quote(binurl[2]) - binurl = urllib.parse.urlunsplit(binurl) - - tmpfile, headers = urllib.request.urlretrieve(binurl, tmpfile) - fname = detect_filename(url, tgt, headers) - - cc.dbg_msg(f"{url=}\n{headers=}\n\n{fname=}\n{tmpfile=}") - - shutil.move(tmpfile, fname) - - return fname diff --git a/src/libcport/exceptions.py b/src/libcport/exceptions.py deleted file mode 100644 index 774fdccffc9dd86280ee9cda8820357d24b17dec..0000000000000000000000000000000000000000 --- a/src/libcport/exceptions.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/python3 -# -# exceptions.py - base exceptions for cport package manager -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -class PortError(Exception): - - def __init__(self, value: str = None): - self.value = value - - def __str__(self): - return self.value - - -class CSystemError(PortError): - - def __init__(self, value: str = None): - self.value = value - - def __str__(self): - return self.value - - -class PortNotFoundError(PortError): - - def __init__(self, value=None): - self.value = value - - def __str__(self): - return self.value - - -class PortBrokenError(PortError): - - def __init__(self, value: str = None): - self.value = value - - def __str__(self): - return self.value - - -class PortFilesBrokenError(PortError): - - def __init__(self, value=None): - self.value = value - - def __str__(self): - return self.value - - -class FilePortTomlError(PortFilesBrokenError): - - def __init__(self, value=None): - self.value = value - - def __str__(self): - return self.value - - -class FileFilesListError(PortFilesBrokenError): - - def __init__(self, value=None): - self.value = value - - def __str__(self): - return self.value diff --git a/src/libcport/info.py b/src/libcport/info.py deleted file mode 100644 index 68b47dd956dcb335d23401f873e5a060b20eceeb..0000000000000000000000000000000000000000 --- a/src/libcport/info.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/python3 -# -# info.py - get information about ports -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . -# - -import os -import toml - -import libcport.core as cc -import libcport.datatypes as cd -import libcport.exceptions as ce - - -class CPortInfo: - - """Get data from the 'port.toml' file of the selected port""" - - def __init__(self, port: cd.port): - self.port: cd.port = port - conf_file: str = f"{port.path}/port.toml" - - if not os.path.isfile(conf_file): - raise ce.FilePortTomlError - - self.data: dict = toml.load(conf_file) - - def package(self) -> dict: - """Return all data from the '[package]' section (port.toml file) + installed status (True or False) - - Returned value: - - { - "name": str, - "version": str, - "description": str, - "maintainer": str, - "releases": list[str], - "priority": str, - "usage": float, - "upgrade_mode": str, - "build_time": float, - "installed": bool - } - """ - - db = cc.CDatabase(self.port) - - installed: bool = db.check() - - db.close() - - self.data['package']['installed'] = installed - return self.data['package'] - - def deps(self) -> dict: - """Return all data from the '[deps]' section - - Return value: - - { - "required": str | None, - "recommend": str | None, - "optional": str | None - } - """ - - return self.data.get('deps') - - def port(self) -> dict: - """Return all data from the '[port]' section - - Return value: - - { - "url": str | None, - "file": str | None, - "md5": str | None, - "sha256": str | None - } - """ - - return self.data['port'] - - -def version_compare(ver_first: str, ver_second: str) -> bool: - """Function to compare two versions - - :arg ver_first - first software version; - :arg ver_second - second software version; - - If they are equal, True is returned. Otherwise, there is a check that - ver_first > ver_second. Accordingly, True or False is returned. - - NOTE: only works with versions that consist only of numbers: "INT.INT" - """ - - v1_n = ver_first.split('.') - v2_n = ver_second.split('.') - - for v1, v2 in zip(v1_n, v2_n): - if int(v1) == int(v2): - continue - return int(v1) > int(v2) - - return ver_first > ver_second diff --git a/src/libcport/install.py b/src/libcport/install.py deleted file mode 100644 index 2286d1629a5ccb8f271cc81f8d6eb52490598480..0000000000000000000000000000000000000000 --- a/src/libcport/install.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/python3 -# -# install.py - module with functions for build, install and check ports -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -import libcport.datatypes as cd -import libcport.constants as ccc -from libcport.core import dbg_msg - -import os -import toml -import wget -import tarfile -import zipfile -import subprocess -import hashlib - - -class CInstall: - - def __init__(self, port: cd.port): - self.port = port - self.data = toml.load(f"{port.path}/port.toml") - - # Optional parameters in the '[port]' section - # Type: str | None - self.url = self.data['port'].get('url') - self.md5 = self.data['port'].get('md5') - self.sha256 = self.data['port'].get('sha256') - self.fname = self.data['port'].get('file') - - def check_fname(self) -> bool: - dbg_msg(f"[check_fname()]: URL: {self.url}") - - if self.url is None or self.url == "none": - return False - - return True - - def get_fname(self) -> str: - """Method for getting the name of the file that will be downloaded from - the link from the 'port.url' parameter. The archive name is calculated - from the data in this parameter.""" - - if self.fname is not None: - return self.fname - - return wget.detect_filename(self.url) - - def download(self) -> bool: - if self.url is None or self.url == "none": - return True - - file = f"{ccc.CACHE_DOWNLOADED_DIR}/{self.get_fname()}" - - if ccc.USE_CACHE == "yes" and os.path.exists(file): - return True - - wget.download(self.url, ccc.CACHE_DOWNLOADED_DIR) - print() - - return os.path.isfile(file) - - def cksum(self) -> bool: - fname = f"{ccc.CACHE_DOWNLOADED_DIR}/{self.get_fname()}" - - dbg_msg(f"Checking the '{fname}' file...") - dbg_msg(f"URL: {self.url}") - - if self.sha256 is not None: - dbg_msg("Use the sha256") - - _hash = hashlib.sha256() - tgt = self.sha256 - else: # self.md5 exist; self.sha256 - doesn't exist - dbg_msg("Use the md5") - assert self.md5 is not None - - _hash = hashlib.md5() - tgt = self.md5 - - if not os.path.isfile(fname): - raise FileNotFoundError - - with open(fname, 'rb') as f: - # The file is read in blocks of 4 KB, since there may be a case when - # the file is large, and its "full" opening may either be incorrect - # or fill up a fairly large amount of memory, which is not - # recommended. - for blk in iter(lambda: f.read(4096), b""): - _hash.update(blk) - - return _hash.hexdigest() == tgt - - def unpack(self) -> None: - """Supported archives formats: - * tar; - * zip. - """ - - file = f"{ccc.CACHE_DOWNLOADED_DIR}/{self.get_fname()}" - - dbg_msg(f"Unpacking file '{file}'...") - - # Проверка типа файла - if zipfile.is_zipfile(file): - dbg_msg("This is zip file") - with zipfile.ZipFile(file, mode='r') as f: - f.extractall(ccc.CACHE_UNPACKED_DIR) - else: - dbg_msg("This is tar file") - with tarfile.open(file, 'r') as f: - f.extractall(path=ccc.CACHE_UNPACKED_DIR) - - def install(self, command: str = "") -> int: - build_script = f"{self.port.path}/install" - - dbg_msg(f"Build script: '{build_script}'") - - run = subprocess.run(f"{command} {build_script}", shell=True) - return run.returncode diff --git a/src/libcport/remove.py b/src/libcport/remove.py deleted file mode 100644 index b9a7d5c6de76451043f139ca6e3b83589581e322..0000000000000000000000000000000000000000 --- a/src/libcport/remove.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python3 -# -# remove.py - module with functions for remove ports -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -import libcport.datatypes as cd -from libcport.core import dbg_msg - -import os -import toml -import shutil -import subprocess - - -class CRemove: - - def __init__(self, port: cd.port): - self.port = port - self.port_conf = toml.load(f"{self.port.path}/port.toml") - - def check_priority(self) -> bool: - """Returned True if port has a 'system' priority - or False if port has a 'user' priority""" - - return self.port_conf['package']['priority'] == 'system' - - def files(self) -> list[str]: - fls = f"{self.port.path}/files.list" - with open(fls) as f: - data = f.read().splitlines() - - for file in data: - dbg_msg(f"File '{file}:", endl=" ") - - if os.path.exists(file): - dbg_msg("found") - else: - dbg_msg("\033[31mNOT found\033[0m") - return data - - def run_preremove(self) -> int: - prerem = f"{self.port.path}/preremove.sh" - if os.path.isfile(prerem): - dbg_msg(f"Run pre-remove script of port '{self.port.name}'") - run = subprocess.run(prerem, shell=True) - return run.returncode - return 0 - - def run_postremove(self) -> int: - postrem = f"{self.port.path}/postremove.sh" - if os.path.isfile(postrem): - dbg_msg(f"Run post-remove script of port '{self.port.name}'") - run = subprocess.run(postrem, shell=True) - return run.returncode - return 0 - - def remove(self) -> None: - for file in self.files(): - if os.path.isfile(file): - dbg_msg(f"Removing file '{file}'") - os.remove(file) - elif os.path.isdir(file): - dbg_msg(f"Removing directory '{file}'") - shutil.rmtree(file) - elif os.path.islink(file): - dbg_msg(f"Removing link '{file}'") - os.remove(file) diff --git a/src/libcport/update.py b/src/libcport/update.py deleted file mode 100644 index 634e917ac593a6dfdc23c60e10dc33894a7ea707..0000000000000000000000000000000000000000 --- a/src/libcport/update.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/python3 -# -# update.py -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -import os -import shutil -import tarfile - -import libcport.constants as ccc -import libcport.core as cc -import toml -import wget - -URL = ccc.PORTS_REPO_URL -BRANCH = ccc.UPDATE_BRANCH - - -class Check: - - def __init__(self, path: str): - self.path = path - - def _get_metadata(self, mode: int = 1) -> dict: - data = [ - ccc.PORTS_DIR, - f"{self.path}/ports" - ] - return toml.load(f"{data[mode]}/metadata.toml") - - def _get_ports(self) -> tuple[list, list]: - d0 = self._get_metadata(mode=0) - d1 = self._get_metadata(mode=1) - - return d0['port_sys']['ports'], d1['port_sys']['ports'] - - def _clean_sys(self) -> None: - try: - shutil.rmtree(f"{self.path}/ports") - os.remove(f"{ccc.CACHE_DOWNLOADED_DIR}/ports.txz") - except FileNotFoundError: - pass - - def check_release(self) -> bool: - rel = cc.get_calm_release() - ports_rel = self._get_metadata(mode=1)['system']['release'] - - return rel in ports_rel - - def get_added_ports(self) -> list[str]: - ports_inst = self._get_ports()[0] - ports_dwnl = self._get_ports()[1] - changes = [] - - for port in ports_dwnl: - if port not in ports_inst: - changes.append(port) - - return changes - - def get_updated_ports(self) -> list[dict]: - ports_dwnl = self._get_ports()[1] - changes = [] - - for port in ports_dwnl: - ignore_ports = self.get_added_ports() - if port in ignore_ports: - continue - - path_tmp = f"{self.path}/ports/{port}/port.toml" - path_ins = f"{ccc.PORTS_DIR}/{port}/port.toml" - - cc.dbg_msg(f"checking port '{port}'...") - - data_tmp = toml.load(path_tmp) - data_ins = toml.load(path_ins) - - version_tmp = data_tmp['package']['version'] - version_ins = data_ins['package']['version'] - - if version_tmp != version_ins: - data = { - 'name': port, - 'version': { - 'old': version_ins, - 'new': version_tmp - } - } - changes.append(data) - - return changes - - def get_removed_ports(self) -> list[str]: - ports_inst = self._get_ports()[0] - ports_dwnl = self._get_ports()[1] - changes = [] - - for port in ports_inst: - if port not in ports_dwnl: - changes.append(port) - - return changes - - def get_other_ports(self, file: str = "install") -> list[str]: - ports_inst = self._get_ports()[0] - changes = [] - - for port in ports_inst: - path_ins = f"{ccc.PORTS_DIR}/{port}" - path_tmp = f"{self.path}/ports/{port}" - - if not os.path.exists(path_tmp): - continue - - with open(f"{path_ins}/{file}") as f: - inst_ins = f.read() - - with open(f"{path_tmp}/{file}") as f: - inst_tmp = f.read() - - if inst_ins != inst_tmp: - changes.append(port) - - return changes - - -class Update(Check): - - def __init__(self, path: str): - super().__init__(path) - self.path = path - self.archive = f"{ccc.CACHE_DOWNLOADED_DIR}/ports.txz" - self.url = f"{URL}/-/raw/{BRANCH}/ports.txz" - - def get(self) -> bool: - if os.path.isfile(self.archive): - os.remove(self.archive) - - wget.download(self.url, self.archive) - print() - - return os.path.exists(self.archive) - - def unpack(self) -> bool: - with tarfile.open(self.archive, 'r') as f: - f.extractall(ccc.CACHE_UNPACKED_DIR) - - return os.path.isfile( - f"{ccc.CACHE_UNPACKED_DIR}/ports" - ) - - def install(self) -> bool: - if os.path.isdir( - ccc.PORTS_DIR - ) and os.path.isdir(self.path): - shutil.rmtree(ccc.PORTS_DIR) - else: - raise FileNotFoundError - - shutil.copytree(self.path, ccc.PORTS_DIR) - - return os.path.isdir(ccc.PORTS_DIR) diff --git a/src/libcport/upgrade.py b/src/libcport/upgrade.py deleted file mode 100644 index 8cda26a6be3637839b584de778eb8e37fc5d9d46..0000000000000000000000000000000000000000 --- a/src/libcport/upgrade.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/python3 -# -# upgrade.py - module for upgrading the ports -# Copyright (C) 2021, 2022 Michail Krasnov -# -# 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 . - -import libcport.core as cc -import libcport.install as ci -import libcport.remove as cr -import libcport.datatypes as cd - -import toml - - -def check_exist(port: cd.port) -> bool: - """Checks if the port exists in the database""" - db = cc.CDatabase(port) - in_db: bool = db.check() - db.close() - - return in_db - - -def compare_version(port: cd.port) -> bool: - """Checks for a match between the port versions - specified in the database and those specified - in the ports system""" - - db = cc.CDatabase(port) - - info_db: list = db.get_one() - info_toml: dict = toml.load(f"{port.path}/port.toml") - - ver_in_db: str = info_db[1] - ver_in_toml: str = info_toml['package']['version'] - - return ver_in_db == ver_in_toml - - -def upgrade_soft(port: cd.port) -> int: - db = cc.CDatabase(port) - if not check_exist(port): - cc.Msg.warn(f"Port '{port.name}' isn't in database!") - return 1 - - if compare_version(port): - cc.Msg.err(f"Nothing to do.") - return 1 - - db.remove() - inst = ci.CInstall(port) - - inst.download() - - if not inst.cksum(): - return 1 - - inst.unpack() - r = inst.install() - - if r == 0: - db.add() - - db.close() - - return r - - -def upgrade_hard(port: cd.port) -> int: - db = cc.CDatabase(port) - if not check_exist(port): - cc.Msg.warn(f"Port '{port.name}' isn't in database!") - return 1 - - if compare_version(port): - cc.Msg.err(f"Nothing to do.") - return 1 - - remove = cr.CRemove(port) - remove.remove() - - db.remove() - - inst = ci.CInstall(port) - - inst.download() - - if not inst.cksum(): - return 1 - - inst.unpack() - r = inst.install() - - if r == 0: - db.add() - - db.close() - - return r