diff --git a/presets/aur_example.toml b/presets/aur_example.toml new file mode 100644 index 0000000..86021b3 --- /dev/null +++ b/presets/aur_example.toml @@ -0,0 +1,2 @@ +packages = ["clang"] +aur_packages = ["bat-cat-git"] diff --git a/src/args.rs b/src/args.rs index 056e4dc..346f4c4 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,3 +1,4 @@ +use super::aur::AurHelper; use byte_unit::Byte; use std::path::PathBuf; use structopt::StructOpt; @@ -41,6 +42,10 @@ pub struct CreateCommand { #[structopt(short = "p", long = "extra-packages", value_name = "package")] pub extra_packages: Vec, + /// Additional packages to install + #[structopt(long = "aur-packages", value_name = "aurpackage")] + pub aur_packages: Vec, + /// Enter interactive chroot before unmounting the drive #[structopt(short = "i", long = "interactive")] pub interactive: bool, @@ -72,6 +77,9 @@ pub struct CreateCommand { /// show non-removable devices #[structopt(long = "allow-non-removable")] pub allow_non_removable: bool, + + #[structopt(long = "aur-helper", possible_values=&["yay"], default_value="yay")] + pub aur_helper: AurHelper, } #[derive(StructOpt)] diff --git a/src/aur.rs b/src/aur.rs new file mode 100644 index 0000000..5973551 --- /dev/null +++ b/src/aur.rs @@ -0,0 +1,38 @@ +use crate::error::ErrorKind; +use std::str::FromStr; + +pub struct AurHelper { + pub name: String, + pub install_command: Vec, +} + +impl FromStr for AurHelper { + type Err = ErrorKind; + + fn from_str(s: &str) -> Result { + match s { + "yay" => Ok(AurHelper { + name: String::from("yay"), + install_command: vec![ + String::from("yay"), + String::from("-S"), + String::from("--nocleanmenu"), + String::from("--nodiffmenu"), + String::from("--noeditmenu"), + String::from("--noupgrademenu"), + String::from("--useask"), + String::from("--removemake"), + String::from("--norebuild"), + String::from("--noconfirm"), + String::from("--answeredit"), + String::from("None"), + String::from("--answerclean"), + String::from("None"), + String::from("--mflags"), + String::from("--noconfirm"), + ], + }), + _ => Err(ErrorKind::AurHelper {}), + } + } +} diff --git a/src/constants.rs b/src/constants.rs index 1302a3d..1fd8659 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -17,3 +17,5 @@ pub const BASE_PACKAGES: [&str; 8] = [ "networkmanager", "broadcom-wl", ]; + +pub const AUR_DEPENDENCIES: [&str; 3] = ["base-devel", "git", "sudo"]; diff --git a/src/error.rs b/src/error.rs index eca29a1..cda84de 100644 --- a/src/error.rs +++ b/src/error.rs @@ -86,6 +86,9 @@ pub enum ErrorKind { #[fail(display = "Error executing preset script")] PresetScript, + #[fail(display = "Error parsing AUR helper string")] + AurHelper, + #[fail(display = "Error creating the image")] Image, diff --git a/src/main.rs b/src/main.rs index abebcae..31bd8ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod args; +mod aur; mod constants; mod error; mod initcpio; @@ -240,6 +241,10 @@ fn create(command: args::CreateCommand) -> Result<(), Error> { packages.extend(presets.packages); + if presets.aur_packages.len() > 0 { + packages.extend(constants::AUR_DEPENDENCIES.iter().map(|s| String::from(*s))); + } + info!("Bootstrapping system"); pacstrap .execute() @@ -258,7 +263,77 @@ fn create(command: args::CreateCommand) -> Result<(), Error> { ); debug!("fstab:\n{}", fstab); fs::write(mount_point.path().join("etc/fstab"), fstab).context(ErrorKind::Fstab)?; + if presets.aur_packages.len() > 0 { + arch_chroot + .execute() + .arg(mount_point.path()) + .args(&["useradd", "-m", "aur"]) + .run(ErrorKind::PostInstallation)?; + arch_chroot + .execute() + .arg(mount_point.path()) + .args(&[ + "sed", + "-i", + "s/# %wheel ALL=(ALL) NOPASSWD: ALL/aur ALL=(ALL) NOPASSWD: ALL/g", + ]) + .arg("/etc/sudoers") + .run(ErrorKind::PostInstallation)?; + + arch_chroot + .execute() + .arg(mount_point.path()) + .args(&["sudo", "-u", "aur"]) + .arg("git") + .arg("clone") + .arg(format!( + "https://aur.archlinux.org/{}.git", + &command.aur_helper.name + )) + .arg(format!("/home/aur/{}", &command.aur_helper.name)) + .run(ErrorKind::PostInstallation)?; + + arch_chroot + .execute() + .arg(mount_point.path()) + .args(&[ + "bash", + "-c", + &format!( + "cd /home/aur/{} && sudo -u aur makepkg -s -i --noconfirm", + &command.aur_helper.name + ), + ]) + .run(ErrorKind::PostInstallation)?; + + arch_chroot + .execute() + .arg(mount_point.path()) + .args(&["sudo", "-u", "aur"]) + .args(&command.aur_helper.install_command) + .args(presets.aur_packages) + .args(&command.aur_packages) + .run(ErrorKind::PostInstallation)?; + + // Clean up aur user: + arch_chroot + .execute() + .arg(mount_point.path()) + .args(&["userdel", "-r", "aur"]) + .run(ErrorKind::PostInstallation)?; + + arch_chroot + .execute() + .arg(mount_point.path()) + .args(&[ + "sed", + "-i", + "s/aur ALL=(ALL) NOPASSWD: ALL/# %wheel ALL=(ALL) NOPASSWD: ALL/g", + ]) + .arg("/etc/sudoers") + .run(ErrorKind::PostInstallation)?; + } if !presets.scripts.is_empty() { info!("Running custom scripts"); } diff --git a/src/presets.rs b/src/presets.rs index 54db31d..99531a6 100644 --- a/src/presets.rs +++ b/src/presets.rs @@ -13,6 +13,7 @@ struct Preset { script: Option, environment_variables: Option>, shared_directories: Option>, + aur_packages: Option>, } fn visit_dirs(dir: &Path, filevec: &mut Vec) -> Result<(), io::Error> { @@ -46,11 +47,16 @@ impl Preset { scripts: &mut Vec