Merge commit 'f819e9a9c5f1041770726a0a7595a01fe417711c'

This commit is contained in:
2023-05-23 17:59:29 +02:00
16 changed files with 516 additions and 370 deletions

View File

@@ -38,6 +38,12 @@ pub struct CreateCommand {
#[structopt(parse(from_os_str))]
pub path: Option<PathBuf>,
/// Path to a pacman.conf file which will be used to pacstrap packages into the image.
///
/// This pacman.conf will also be copied into the resulting Arch Linux image.
#[structopt(short = "c", long = "pacman-conf", value_name = "pacman_conf")]
pub pacman_conf: Option<PathBuf>,
/// Additional packages to install
#[structopt(short = "p", long = "extra-packages", value_name = "package")]
pub extra_packages: Vec<String>,
@@ -46,6 +52,10 @@ pub struct CreateCommand {
#[structopt(long = "aur-packages", value_name = "aurpackage")]
pub aur_packages: Vec<String>,
/// Boot partition size in megabytes
#[structopt(long = "boot-size")]
pub boot_size: Option<u32>,
/// Enter interactive chroot before unmounting the drive
#[structopt(short = "i", long = "interactive")]
pub interactive: bool,
@@ -78,7 +88,7 @@ pub struct CreateCommand {
#[structopt(long = "allow-non-removable")]
pub allow_non_removable: bool,
#[structopt(long = "aur-helper", possible_values=&["yay"], default_value="yay")]
#[structopt(long = "aur-helper", possible_values=&["paru", "yay"], default_value="yay")]
pub aur_helper: AurHelper,
}

View File

@@ -3,6 +3,7 @@ use std::str::FromStr;
pub struct AurHelper {
pub name: String,
pub package_name: String,
pub install_command: Vec<String>,
}
@@ -11,8 +12,28 @@ impl FromStr for AurHelper {
fn from_str(s: &str) -> anyhow::Result<Self> {
match s {
"yay" => Ok(AurHelper {
"paru" => Ok(Self {
name: String::from("paru"),
package_name: String::from("paru-bin"),
install_command: vec![
String::from("paru"),
String::from("-S"),
String::from("--skipreview"),
String::from("--noupgrademenu"),
String::from("--useask"),
String::from("--removemake"),
String::from("--norebuild"),
String::from("--nocleanafter"),
String::from("--noredownload"),
String::from("--mflags"),
String::from(""),
String::from("--noconfirm"),
String::from("--batchinstall"),
],
}),
"yay" => Ok(Self {
name: String::from("yay"),
package_name: String::from("yay-bin"),
install_command: vec![
String::from("yay"),
String::from("-S"),
@@ -23,7 +44,6 @@ impl FromStr for AurHelper {
String::from("--useask"),
String::from("--removemake"),
String::from("--norebuild"),
String::from("--noconfirm"),
String::from("--answeredit"),
String::from("None"),
String::from("--answerclean"),

View File

@@ -162,12 +162,14 @@ fn create(command: args::CreateCommand) -> anyhow::Result<()> {
info!("Partitioning the block device");
debug!("{:?}", disk_path);
let boot_size = command.boot_size.unwrap_or(300);
sgdisk
.execute()
.args(&[
"-Z",
"-o",
"--new=1::+250M",
&format!("--new=1::+{}M", boot_size),
"--new=2::+1M",
"--largest-new=3",
"--typecode=1:EF00",
@@ -186,7 +188,7 @@ fn create(command: args::CreateCommand) -> anyhow::Result<()> {
let root_partition_base = storage_device.get_partition(constants::ROOT_PARTITION_INDEX)?;
let encrypted_root = if let Some(cryptsetup) = &cryptsetup {
info!("Encrypting the root filesystem");
EncryptedDevice::prepare(&cryptsetup, &root_partition_base)?;
EncryptedDevice::prepare(cryptsetup, &root_partition_base)?;
Some(EncryptedDevice::open(
cryptsetup,
&root_partition_base,
@@ -225,13 +227,24 @@ fn create(command: args::CreateCommand) -> anyhow::Result<()> {
packages.extend(presets.packages);
if !presets.aur_packages.is_empty() {
packages.extend(constants::AUR_DEPENDENCIES.iter().map(|s| String::from(*s)));
}
let aur_pacakges = {
let mut p = vec![String::from("shim-signed")];
p.extend(presets.aur_packages);
p.extend(command.aur_packages);
p
};
packages.extend(constants::AUR_DEPENDENCIES.iter().map(|s| String::from(*s)));
let pacman_conf_path = command
.pacman_conf
.unwrap_or_else(|| "/etc/pacman.conf".into());
info!("Bootstrapping system");
pacstrap
.execute()
.arg("-C")
.arg(&pacman_conf_path)
.arg("-c")
.arg(mount_point.path())
.args(packages)
@@ -239,6 +252,10 @@ fn create(command: args::CreateCommand) -> anyhow::Result<()> {
.run()
.context("Pacstrap error")?;
// Copy pacman.conf to the image.
fs::copy(pacman_conf_path, mount_point.path().join("etc/pacman.conf"))
.context("Failed copying pacman.conf")?;
let fstab = fix_fstab(
&genfstab
.execute()
@@ -257,84 +274,85 @@ fn create(command: args::CreateCommand) -> anyhow::Result<()> {
.run()
.context("Failed to delete the root password")?;
if !presets.aur_packages.is_empty() {
arch_chroot
.execute()
.arg(mount_point.path())
.args(&["useradd", "-m", "aur"])
.run()
.context("Failed to create temporary user to install AUR packages")?;
info!("Setting locale");
fs::OpenOptions::new()
.append(true)
.write(true)
.open(mount_point.path().join("etc/locale.gen"))
.and_then(|mut locale_gen| locale_gen.write_all(b"en_US.UTF-8 UTF-8\n"))
.context("Failed to create locale.gen")?;
fs::write(
mount_point.path().join("etc/locale.conf"),
"LANG=en_US.UTF-8",
)
.context("Failed to write to locale.conf")?;
arch_chroot
.execute()
.arg(mount_point.path())
.arg("locale-gen")
.run()
.context("locale-gen failed")?;
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()
.context("Failed to modify sudoers file for AUR packages")?;
info!("Installing AUR packages");
arch_chroot
.execute()
.arg(mount_point.path())
.args(&["sudo", "-u", "aur"])
.arg("git")
.arg("clone")
.arg(format!(
"https://aur.archlinux.org/{}.git",
arch_chroot
.execute()
.arg(mount_point.path())
.args(&["useradd", "-m", "aur"])
.run()
.context("Failed to create temporary user to install AUR packages")?;
let aur_sudoers = mount_point.path().join("etc/sudoers.d/aur");
fs::write(&aur_sudoers, "aur ALL=(ALL) NOPASSWD: ALL")
.context("Failed to modify sudoers file for AUR packages")?;
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.package_name
))
.arg(format!("/home/aur/{}", &command.aur_helper.name))
.run()
.context("Failed to clone AUR helper package")?;
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
))
.arg(format!("/home/aur/{}", &command.aur_helper.name))
.run()
.context("Failed to clone AUR helper package")?;
),
])
.run()
.context("Failed to build AUR helper")?;
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()
.context("Failed to build AUR helper")?;
arch_chroot
.execute()
.arg(mount_point.path())
.args(&["sudo", "-u", "aur"])
.args(&command.aur_helper.install_command)
.args(aur_pacakges)
.run()
.context("Failed to install AUR packages")?;
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()
.context("Failed to install AUR packages")?;
// Clean up aur user:
arch_chroot
.execute()
.arg(mount_point.path())
.args(&["userdel", "-r", "aur"])
.run()
.context("Failed to delete temporary aur user")?;
// Clean up aur user:
arch_chroot
.execute()
.arg(mount_point.path())
.args(&["userdel", "-r", "aur"])
.run()
.context("Failed to delete temporary aur user")?;
fs::remove_file(&aur_sudoers).context("Cannot delete the AUR sudoers temporary file")?;
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()
.context("Failed to undo sudoers changes")?;
}
if !presets.scripts.is_empty() {
info!("Running custom scripts");
}
@@ -387,7 +405,7 @@ fn create(command: args::CreateCommand) -> anyhow::Result<()> {
),
)
.run()
.context("Failed running preset script")?;
.with_context(|| format!("Failed running preset script:\n{}", script.script_text))?;
}
info!("Performing post installation tasks");
@@ -406,25 +424,6 @@ fn create(command: args::CreateCommand) -> anyhow::Result<()> {
)
.context("Failed to write to journald.conf")?;
info!("Setting locale");
fs::OpenOptions::new()
.append(true)
.write(true)
.open(mount_point.path().join("etc/locale.gen"))
.and_then(|mut locale_gen| locale_gen.write_all(b"en_US.UTF-8 UTF-8\n"))
.context("Failed to create locale.gen")?;
fs::write(
mount_point.path().join("etc/locale.conf"),
"LANG=en_US.UTF-8",
)
.context("Failed to write to locale.conf")?;
arch_chroot
.execute()
.arg(mount_point.path())
.arg("locale-gen")
.run()
.context("locale-gen failed")?;
info!("Generating initramfs");
fs::write(
mount_point.path().join("etc/mkinitcpio.conf"),
@@ -472,6 +471,23 @@ fn create(command: args::CreateCommand) -> anyhow::Result<()> {
.arg(format!("grub-install --target=i386-pc --boot-directory /boot {} && grub-install --target=x86_64-efi --efi-directory /boot --boot-directory /boot --removable && grub-mkconfig -o /boot/grub/grub.cfg", disk_path.display()))
.run().context("Failed to install grub")?;
let bootloader = mount_point.path().join("boot/EFI/BOOT/BOOTX64.efi");
fs::rename(
&bootloader,
mount_point.path().join("boot/EFI/BOOT/grubx64.efi"),
)
.context("Cannot move out grub")?;
fs::copy(
mount_point.path().join("usr/share/shim-signed/mmx64.efi"),
mount_point.path().join("boot/EFI/BOOT/mmx64.efi"),
)
.context("Failed copying mmx64")?;
fs::copy(
mount_point.path().join("usr/share/shim-signed/shimx64.efi"),
bootloader,
)
.context("Failed copying shim")?;
debug!(
"GRUB configuration: {}",
fs::read_to_string(mount_point.path().join("boot/grub/grub.cfg"))

View File

@@ -33,7 +33,7 @@ fn visit_dirs(dir: &Path, filevec: &mut Vec<PathBuf>) -> Result<(), io::Error> {
impl Preset {
fn load(path: &Path) -> anyhow::Result<Self> {
let data = fs::read_to_string(path).with_context(|| format!("{}", path.display()))?;
Ok(toml::from_str(&data).with_context(|| format!("{}", path.display()))?)
toml::from_str(&data).with_context(|| format!("{}", path.display()))
}
fn process(
@@ -41,7 +41,7 @@ impl Preset {
packages: &mut HashSet<String>,
scripts: &mut Vec<Script>,
environment_variables: &mut HashSet<String>,
path: &PathBuf,
path: &Path,
aur_packages: &mut HashSet<String>,
) -> anyhow::Result<()> {
if let Some(preset_packages) = &self.packages {
@@ -111,7 +111,7 @@ impl PresetsCollection {
// Build vector of paths to files, then sort by path name
// Recursively load directories of preset files
let mut dir_paths: Vec<PathBuf> = Vec::new();
visit_dirs(&preset, &mut dir_paths)
visit_dirs(preset, &mut dir_paths)
.with_context(|| format!("{}", preset.display()))?;
// Order not guaranteed so we sort
@@ -128,11 +128,11 @@ impl PresetsCollection {
)?;
}
} else {
Preset::load(&preset)?.process(
Preset::load(preset)?.process(
&mut packages,
&mut scripts,
&mut environment_variables,
&preset,
preset,
&mut aur_packages,
)?;
}
@@ -151,8 +151,8 @@ impl PresetsCollection {
Ok(Self {
packages,
scripts,
aur_packages,
scripts,
})
}
}

View File

@@ -30,7 +30,7 @@ impl LoopDevice {
);
info!("Mounted {} to {}", file.display(), path.display());
Ok(LoopDevice { path, losetup })
Ok(Self { path, losetup })
}
pub fn path(&self) -> &Path {

View File

@@ -68,7 +68,7 @@ pub fn get_storage_devices(allow_non_removable: bool) -> anyhow::Result<Vec<Devi
.context("Could not parse block size to unsigned integer (u128)")?
* 512,
),
})
});
}
Ok(result)

View File

@@ -21,7 +21,7 @@ impl<'a> StorageDevice<'a> {
.context("Error querying information about the block device")?;
let device_name = path
.file_name()
.and_then(|s| s.to_str())
.and_then(std::ffi::OsStr::to_str)
.map(String::from)
.ok_or_else(|| anyhow!("Invalid device name: {}", path.display()))?;

View File

@@ -2,6 +2,7 @@ mod chroot;
mod mount;
mod qemu;
use anyhow::Context;
pub use chroot::chroot;
pub use mount::mount;
pub use qemu::qemu;
@@ -17,7 +18,9 @@ pub struct Tool {
impl Tool {
pub fn find(name: &'static str) -> anyhow::Result<Self> {
Ok(Self { exec: which(name)? })
Ok(Self {
exec: which(name).context(format!("Cannot find {}", name))?,
})
}
pub fn execute(&self) -> Command {

View File

@@ -20,7 +20,7 @@ pub fn mount<'a>(
info!("Mounting filesystems to {}", mount_path.display());
mount_stack
.mount(&root_filesystem, mount_path.into(), None)
.mount(root_filesystem, mount_path.into(), None)
.with_context(|| format!("Error mounting filesystem to {}", mount_path.display()))?;
let boot_point = mount_path.join("boot");
@@ -29,7 +29,7 @@ pub fn mount<'a>(
}
mount_stack
.mount(&boot_filesystem, boot_point, None)
.mount(boot_filesystem, boot_point, None)
.context("Error mounting the boot point")?;
Ok(mount_stack)