commit 3ebaf7e732480de25a251780d343d4c53dac5533 Author: Eduard Toloza Date: Sat Apr 20 16:14:23 2019 -0500 First commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06cdafa --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +PKGBUILD* diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..00af2bd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,166 @@ +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blackarch-devtools" +version = "0.1.0" +dependencies = [ + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_syscall" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "termcolor" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wincolor" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "yaml-rust" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" +"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..919c8b1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "blackarch-devtools" +version = "0.1.0" +authors = ["Eduard Toloza "] +edition = "2018" + +[dependencies] +termcolor = "1.0.4" +clap = {version = "2.33.0", features = ["yaml"]} diff --git a/bin/blackarch-devtools b/bin/blackarch-devtools new file mode 100755 index 0000000..17cd509 Binary files /dev/null and b/bin/blackarch-devtools differ diff --git a/src/cli.yml b/src/cli.yml new file mode 100644 index 0000000..4ab1817 --- /dev/null +++ b/src/cli.yml @@ -0,0 +1,53 @@ +name: blackarch-devtools +version: "0.1.0" +author: Eduard Tolosa +about: Development tools for BlackArch Linux +args: + - setup: + short: s + long: setup + help: Setup the clean chroot environment. + takes_value: false + - update: + short: u + long: update + help: Update BlackArch Linux chroot environment. + takes_value: false + - build: + short: b + long: build + help: Build package from PKGBUILD in clean chroot environment. + takes_value: false + - test: + short: t + long: test + help: Install and test package in clean chroot environment. + takes_value: false + requires: + - package + - executable + - package: + short: p + long: package + help: Build package to install in the clean chroot environment. + takes_value: true + - executable: + short: e + long: executable + help: Name of the binary file provided by the package that you're installing in the chroot environment. + takes_value: true + - install-missing: + short: I + long: install-missing + help: Build package files that aren't available in repos. You can specify it multiple times. + multiple: true + takes_value: true + - clean: + short: c + long: clean + help: Clean chroot environment before building. + takes_value: false + - verbose: + short: v + multiple: true + help: Sets the level of verbosity diff --git a/src/functions.rs b/src/functions.rs new file mode 100644 index 0000000..396ec42 --- /dev/null +++ b/src/functions.rs @@ -0,0 +1,364 @@ +// Crate termcolor +use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; + +// Standard imports +use std::io::Write; +use std::path::Path; +use std::process; +use std::process::Command; +use std::{env, fs}; + +pub fn get_vars(get_var: &str) -> String { + let home_dir: String = env::var("HOME").expect("Failed to read HOME system variable."); + let chroot_dir: String = [&home_dir, "/blackarch_chroot"].concat(); + let devtools_mkarchroot = String::from("/usr/bin/mkarchroot"); + let devtools_nspawn = String::from("/usr/bin/arch-nspawn"); + let blackarch_instance: String = String::from("blackarch"); + let chroot_root: String = [&chroot_dir, "/root/"].concat(); + let chroot_blackarch: String = [&chroot_dir, "/blackarch/"].concat(); + let devtools_makechrootpkg: String = String::from("/usr/bin/makechrootpkg"); + let pacman: String = String::from("/usr/bin/pacman"); + if get_var == "home_dir" { + home_dir + } else if get_var == "chroot_dir" { + chroot_dir + } else if get_var == "mkarchroot" { + devtools_mkarchroot + } else if get_var == "nspawn" { + devtools_nspawn + } else if get_var == "chroot_root" { + chroot_root + } else if get_var == "chroot_blackarch" { + chroot_blackarch + } else if get_var == "blackarch_instance" { + blackarch_instance + } else if get_var == "makechrootpkg" { + devtools_makechrootpkg + } else if get_var == "pacman" { + pacman + } else { + String::from("Error returning the value.") + } +} + +pub fn coloring(color: &str) -> termcolor::StandardStream { + let mut stdout = StandardStream::stdout(ColorChoice::Always); + if color == "green" { + stdout.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Color::Green))); + stdout + } else if color == "yellow" { + stdout.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Color::Yellow))); + stdout + } else if color == "red" { + stdout.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Color::Red))); + stdout + } else { + stdout.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Color::White))); + stdout + } +} + +pub fn setup_chroot() { + let chroot_dir = get_vars("chroot_dir"); + if Path::new(&chroot_dir).exists() { + if Path::new(&chroot_dir).is_dir() { + writeln!(coloring("red"), "The directory {} already exists in the system, remove it or try with a different path.", &chroot_dir); + process::exit(1); + } else if Path::new(&chroot_dir).is_file() { + writeln!( + coloring("red"), + "The file {} already exists in the system, remove it or try a different path.", + &chroot_dir + ); + process::exit(1); + } + } else { + writeln!( + coloring("yellow"), + "Creating chroot directory with name: {}", + &chroot_dir + ); + fs::create_dir(&chroot_dir) + .expect("An error as ocurred while creating the chroot directoy."); + writeln!(coloring("yellow"), "Setting up chroot environment..."); + let devtools_mkarchroot = get_vars("mkarchroot"); + let devtools_nspawn = get_vars("nspawn"); + let chroot_root = get_vars("chroot_root"); + if Path::new(&devtools_mkarchroot).exists() { + let up_chroot = Command::new(&devtools_mkarchroot) + .args(&[&chroot_root, "base", "base-devel"]) + .status() + .expect("Failed to setup chroot environment."); + if up_chroot.success() { + writeln!( + coloring("yellow"), + "Configuring BlackArch Linux repo in the chroot environment..." + ); + let get_strap = Command::new(&devtools_nspawn) + .args(&[&chroot_root, "curl", "-O", "https://blackarch.org/strap.sh"]) + .status() + .expect("Failed to retrieve strap.sh from blackarch.org"); + if get_strap.success() { + let strap_perms = Command::new(&devtools_nspawn) + .args(&[&chroot_root, "chmod", "+x", "strap.sh"]) + .status() + .expect("Failed to change permisions for strap.sh"); + if strap_perms.success() { + let strap_exec = Command::new(&devtools_nspawn) + .args(&[&chroot_root, "sh", "strap.sh"]) + .status() + .expect("Failed to exec strap.sh"); + if strap_exec.success() { + Command::new(&devtools_nspawn) + .args(&[&chroot_root, "rm", "strap.sh"]) + .status() + .expect("Error deleting strap.sh from chroot environment."); + writeln!(coloring("green"), "Chroot environment setup complete!"); + } else { + writeln!( + coloring("red"), + "Can't install strap.sh into {}!", + &chroot_root + ); + } + } + } + } + } else { + writeln!( + coloring("red"), + "Executable file {} not found, install the devtools package from repos.", + &devtools_mkarchroot + ); + } + } +} + +pub fn update_chroot_packages() { + writeln!(coloring("green"), "Updating the chroot environment..."); + let devtools_nspawn = get_vars("nspawn"); + let chroot_root = get_vars("chroot_root"); + let update_packages = Command::new(&devtools_nspawn) + .args(&[&chroot_root, "/bin/sh", "-c", "pacman --noconfirm -Syuu"]) + .status() + .expect("Failed updating chroot environment"); + if update_packages.success() { + writeln!(coloring("green"), "Chroot environment updated correctly!"); + } else { + writeln!( + coloring("red"), + "An error as ocurred while updating the chroot environment." + ); + } +} + +pub fn build_package() { + update_chroot_packages(); + let devtools_makechrootpkg = get_vars("makechrootpkg"); + let chroot_dir = get_vars("chroot_dir"); + let chroot_blackarch = get_vars("chroot_blackarch"); + let build_package = Command::new(&devtools_makechrootpkg) + .args(&["-l", &chroot_blackarch, "-r", &chroot_dir]) + .status() + .expect("Failed to build the package."); + if build_package.success() { + writeln!(coloring("green"), "Package built sucessfully!"); + } else { + writeln!(coloring("red"), "Failed to build the package."); + } +} + +pub fn build_package_with_missing_deps(missing: &[&str]) { + update_chroot_packages(); + let chroot_blackarch = get_vars("chroot_blackarch"); + let devtools_nspawn = get_vars("nspawn"); + for missing in missing.iter() { + let copy_path: String = [&chroot_blackarch, "root/", &missing].concat(); + println!("{}", &missing); + println!("{}", ©_path); + Command::new("sudo") + .args(&["cp", &missing, ©_path]) + .spawn() + .expect("Failed to copy missing packages."); + } + writeln!( + coloring("yellow"), + "Installing missing packages: {:?}", + missing + ); + let install_missing = Command::new(&devtools_nspawn) + .args(&[ + &chroot_blackarch, + "/bin/sh", + "-c", + "pacman -U --noconfirm root/*", + ]) + .status() + .expect("Failed to install missing packages."); + if install_missing.success() { + build_package(); + Command::new(&devtools_nspawn).args(&[&chroot_blackarch, "/bin/sh", "-c", "rm -rf root/*"]); + } +} + +pub fn sync_chroot() { + let chroot_blackarch = get_vars("chroot_blackarch"); + let chroot_root = get_vars("chroot_root"); + let chroot_dir = get_vars("chroot_dir"); + writeln!( + coloring("green"), + "Syncing chroot copy {} with {} ...", + &chroot_blackarch, + &chroot_root + ); + if Path::new(&chroot_dir).exists() { + if Path::new(&chroot_blackarch).exists() { + let fs_type = Command::new("stat") + .args(&["-f", "-c", "%T", &chroot_blackarch]) + .output() + .expect("Failed to read filesystem type"); + let fs_id_hex = Command::new("stat") + .args(&["-c", "%i", &chroot_blackarch]) + .output() + .expect("Failed to read filesystem ID"); + if String::from_utf8_lossy(&fs_type.stdout) == "btrfs" + && String::from_utf8_lossy(&fs_id_hex.stdout) == "256" + { + Command::new("sudo") + .args(&["btrfs", "subvolume", "delete", &chroot_blackarch]) + .status() + .expect("Failed to delete chroot copy."); + Command::new("sudo") + .args(&[ + "btrfs", + "subvolume", + "snapshot", + &chroot_root, + &chroot_blackarch, + ]) + .spawn() + .expect("Failed to create chroot copy."); + } else { + let delete_existing_chroot = Command::new("sudo") + .args(&[ + "rm", + "--recursive", + "--force", + "--one-file-system", + &chroot_blackarch, + ]) + .status() + .expect("Failed to delete chroot copy."); + if delete_existing_chroot.success() { + let create_chroot_copy = Command::new("sudo") + .args(&["mkdir", &chroot_blackarch]) + .status() + .expect("Failed to create working copy of chroot environment."); + if create_chroot_copy.success() { + let make_chroot_copy = Command::new("sudo") + .args(&[ + "rsync", + "-a", + "--delete", + "-q", + "-W", + "-x", + &chroot_root, + &chroot_blackarch, + ]) + .status() + .expect("Failed to create copy of root chroot environment."); + if make_chroot_copy.success() { + writeln!(coloring("green"), "Chroot environment is ready!"); + } else { + writeln!( + coloring("red"), + "Failed to create copy of root chroot environment." + ); + } + } else { + writeln!( + coloring("red"), + "Error while trying to create the directory for the chroot copy." + ); + } + } else { + writeln!( + coloring("red"), + "Failed while trying to delete the chroot copy." + ); + } + } + } else { + let create_chroot_copy = Command::new("sudo") + .args(&["mkdir", &chroot_blackarch]) + .status() + .expect("Failed to create working copy of chroot environment."); + if create_chroot_copy.success() { + let make_chroot_copy = Command::new("sudo") + .args(&[ + "rsync", + "-a", + "--delete", + "-q", + "-W", + "-x", + &chroot_root, + &chroot_blackarch, + ]) + .status() + .expect("Failed to create chroot copy."); + if make_chroot_copy.success() { + writeln!(coloring("green"), "Chroot environment is ready!"); + } else { + writeln!( + coloring("red"), + "Failed to create copy of root chroot environment." + ); + } + } + } + } else { + writeln!( + coloring("red"), + "Chroot environment doesn't exists. Please use the -s option first." + ); + } +} + +pub fn test_package(package: &str, executable: &str) { + let pacman = get_vars("pacman"); + let chroot_blackarch = get_vars("chroot_blackarch"); + let install_package = Command::new("sudo") + .args(&[&pacman, "--root", &chroot_blackarch, "-U", &package]) + .status() + .expect("Failed to install the package in the chroot environment."); + if install_package.success() { + writeln!( + coloring("green"), + "Package {} installed correctly! Testing it now...", + &package + ); + let execute_package = Command::new("sudo") + .args(&["chroot", &chroot_blackarch, &executable]) + .status() + .expect( + "Failed to execute the {} binary in the chroot environment, check the binary name.", + ); + if execute_package.success() { + writeln!( + coloring("green"), + "Binary {} sucessfully executed!", + executable + ); + } else { + writeln!(coloring("red"), "An error as ocurred while trying to execute the binary {}, are you sure that it's the binary name?", &executable); + } + } else { + writeln!( + coloring("red"), + "Package {} wasn't installed in the chroot environment, please check the package name.", + &package + ); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..10b0a01 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,54 @@ +// Crate clap +#[macro_use] +extern crate clap; +use clap::App; +use clap::AppSettings; + +// Functions module +mod functions; + +fn main() { + let yaml = load_yaml!("cli.yml"); + let matches = App::from_yaml(yaml) + .setting(AppSettings::ArgRequiredElseHelp) + .setting(AppSettings::StrictUtf8) + .get_matches(); + if matches.is_present("setup") { + functions::setup_chroot(); + } else if matches.is_present("build") { + if matches.is_present("clean") { + functions::sync_chroot(); + functions::build_package(); + } else { + functions::build_package(); + } + } else if matches.is_present("update") { + functions::update_chroot_packages(); + } else if matches.is_present("test") { + let package = matches + .value_of("package") + .expect("Failed to convert in a valid String") + .to_string(); + let executable = matches + .value_of("executable") + .expect("Failed to convert in a valid String") + .to_string(); + if matches.is_present("clean") { + functions::sync_chroot(); + functions::test_package(&package, &executable); + } else { + functions::test_package(&package, &executable); + } + } else if matches.is_present("install-missing") { + if matches.is_present("clean") { + functions::sync_chroot(); + let missing_deps: Vec<&str> = matches.values_of("install-missing").unwrap().collect(); + functions::build_package_with_missing_deps(&missing_deps.as_slice()); + } else { + let missing_deps: Vec<&str> = matches.values_of("install-missing").unwrap().collect(); + functions::build_package_with_missing_deps(&missing_deps.as_slice()); + } + } else if matches.is_present("clean") { + functions::sync_chroot(); + } +}