feat: Add bootc support (#448)
Adds support for using `bootc` as the preferred method for booting from a locally created image. This new method gets rid of the need to create a tarball and move it to the correct place and instead it will make use of `podman scp` which copies the image to the root `containers-storage` and then has `rpm-ostree` and `bootc` boot from that store. Closes #418 Closes #200
This commit is contained in:
parent
2c525854c9
commit
3a0be4099a
65 changed files with 2991 additions and 1857 deletions
|
|
@ -7,20 +7,18 @@ use blue_build_process_management::{
|
|||
BuildTagPushOpts, CheckKeyPairOpts, CompressionType, GenerateImageNameOpts,
|
||||
GenerateTagsOpts, SignVerifyOpts,
|
||||
},
|
||||
types::Platform,
|
||||
types::{ImageRef, Platform},
|
||||
},
|
||||
logging::{color_str, gen_random_ansi_color},
|
||||
};
|
||||
use blue_build_recipe::Recipe;
|
||||
use blue_build_utils::{
|
||||
constants::{
|
||||
ARCHIVE_SUFFIX, BB_REGISTRY_NAMESPACE, BB_SKIP_VALIDATION, CONFIG_PATH, CONTAINER_FILE,
|
||||
RECIPE_FILE, RECIPE_PATH,
|
||||
ARCHIVE_SUFFIX, BB_REGISTRY_NAMESPACE, BB_SKIP_VALIDATION, CONFIG_PATH, RECIPE_FILE,
|
||||
RECIPE_PATH,
|
||||
},
|
||||
cowstr,
|
||||
credentials::{Credentials, CredentialsArgs},
|
||||
string,
|
||||
traits::CowCollecter,
|
||||
};
|
||||
use bon::Builder;
|
||||
use clap::Args;
|
||||
|
|
@ -163,7 +161,7 @@ impl BlueBuildCommand for BuildCommand {
|
|||
|
||||
if self.push {
|
||||
blue_build_utils::check_command_exists("cosign")?;
|
||||
Driver::check_signing_files(&CheckKeyPairOpts::builder().dir(Path::new(".")).build())?;
|
||||
Driver::check_signing_files(CheckKeyPairOpts::builder().dir(Path::new(".")).build())?;
|
||||
Driver::login()?;
|
||||
Driver::signing_login()?;
|
||||
}
|
||||
|
|
@ -191,11 +189,11 @@ impl BlueBuildCommand for BuildCommand {
|
|||
|
||||
recipe_paths.par_iter().try_for_each(|recipe| {
|
||||
GenerateCommand::builder()
|
||||
.output(tempdir.path().join(if recipe_paths.len() > 1 {
|
||||
blue_build_utils::generate_containerfile_path(recipe)?
|
||||
} else {
|
||||
PathBuf::from(CONTAINER_FILE)
|
||||
}))
|
||||
.output(
|
||||
tempdir
|
||||
.path()
|
||||
.join(blue_build_utils::generate_containerfile_path(recipe)?),
|
||||
)
|
||||
.skip_validation(self.skip_validation)
|
||||
.maybe_platform(self.platform)
|
||||
.recipe(recipe)
|
||||
|
|
@ -217,12 +215,10 @@ impl BuildCommand {
|
|||
let images = recipe_paths
|
||||
.par_iter()
|
||||
.try_fold(Vec::new, |mut images, recipe_path| -> Result<Vec<String>> {
|
||||
let containerfile = temp_dir.join(if recipe_paths.len() > 1 {
|
||||
blue_build_utils::generate_containerfile_path(recipe_path)?
|
||||
} else {
|
||||
PathBuf::from(CONTAINER_FILE)
|
||||
});
|
||||
images.extend(self.build(recipe_path, &containerfile)?);
|
||||
images.extend(self.build(
|
||||
recipe_path,
|
||||
&temp_dir.join(blue_build_utils::generate_containerfile_path(recipe_path)?),
|
||||
)?);
|
||||
Ok(images)
|
||||
})
|
||||
.try_reduce(Vec::new, |mut init, image_names| {
|
||||
|
|
@ -245,9 +241,9 @@ impl BuildCommand {
|
|||
fn build(&self, recipe_path: &Path, containerfile: &Path) -> Result<Vec<String>> {
|
||||
let recipe = Recipe::parse(recipe_path)?;
|
||||
let tags = Driver::generate_tags(
|
||||
&GenerateTagsOpts::builder()
|
||||
GenerateTagsOpts::builder()
|
||||
.oci_ref(&recipe.base_image_ref()?)
|
||||
.maybe_alt_tags(recipe.alt_tags.as_ref().map(CowCollecter::collect_cow_vec))
|
||||
.maybe_alt_tags(recipe.alt_tags.as_deref())
|
||||
.maybe_platform(self.platform)
|
||||
.build(),
|
||||
)?;
|
||||
|
|
@ -276,45 +272,44 @@ impl BuildCommand {
|
|||
&image_name,
|
||||
cache_image.as_ref(),
|
||||
)?
|
||||
} else if let Some(archive_dir) = self.archive.as_ref() {
|
||||
Driver::build_tag_push(
|
||||
BuildTagPushOpts::builder()
|
||||
.containerfile(containerfile)
|
||||
.maybe_platform(self.platform)
|
||||
.image(&ImageRef::from(PathBuf::from(format!(
|
||||
"{}/{}.{ARCHIVE_SUFFIX}",
|
||||
archive_dir.to_string_lossy().trim_end_matches('/'),
|
||||
recipe.name.to_lowercase().replace('/', "_"),
|
||||
))))
|
||||
.squash(self.squash)
|
||||
.maybe_cache_from(cache_image.as_ref())
|
||||
.maybe_cache_to(cache_image.as_ref())
|
||||
.secrets(&recipe.get_secrets())
|
||||
.build(),
|
||||
)?
|
||||
} else {
|
||||
Driver::build_tag_push(&self.archive.as_ref().map_or_else(
|
||||
|| {
|
||||
BuildTagPushOpts::builder()
|
||||
.image(&image)
|
||||
.containerfile(containerfile)
|
||||
.maybe_platform(self.platform)
|
||||
.tags(tags.collect_cow_vec())
|
||||
.push(self.push)
|
||||
.retry_push(self.retry_push)
|
||||
.retry_count(self.retry_count)
|
||||
.compression(self.compression_format)
|
||||
.squash(self.squash)
|
||||
.maybe_cache_from(cache_image.as_ref())
|
||||
.maybe_cache_to(cache_image.as_ref())
|
||||
.secrets(recipe.get_secrets())
|
||||
.build()
|
||||
},
|
||||
|archive_dir| {
|
||||
BuildTagPushOpts::builder()
|
||||
.containerfile(containerfile)
|
||||
.maybe_platform(self.platform)
|
||||
.image(PathBuf::from(format!(
|
||||
"{}/{}.{ARCHIVE_SUFFIX}",
|
||||
archive_dir.to_string_lossy().trim_end_matches('/'),
|
||||
recipe.name.to_lowercase().replace('/', "_"),
|
||||
)))
|
||||
.squash(self.squash)
|
||||
.maybe_cache_from(cache_image.as_ref())
|
||||
.maybe_cache_to(cache_image.as_ref())
|
||||
.secrets(recipe.get_secrets())
|
||||
.build()
|
||||
},
|
||||
))?
|
||||
Driver::build_tag_push(
|
||||
BuildTagPushOpts::builder()
|
||||
.image(&ImageRef::from(&image))
|
||||
.containerfile(containerfile)
|
||||
.maybe_platform(self.platform)
|
||||
.tags(&tags)
|
||||
.push(self.push)
|
||||
.retry_push(self.retry_push)
|
||||
.retry_count(self.retry_count)
|
||||
.compression(self.compression_format)
|
||||
.squash(self.squash)
|
||||
.maybe_cache_from(cache_image.as_ref())
|
||||
.maybe_cache_to(cache_image.as_ref())
|
||||
.secrets(&recipe.get_secrets())
|
||||
.build(),
|
||||
)?
|
||||
};
|
||||
|
||||
if self.push && !self.no_sign {
|
||||
Driver::sign_and_verify(
|
||||
&SignVerifyOpts::builder()
|
||||
SignVerifyOpts::builder()
|
||||
.image(&image)
|
||||
.retry_push(self.retry_push)
|
||||
.retry_count(self.retry_count)
|
||||
|
|
@ -342,13 +337,13 @@ impl BuildCommand {
|
|||
.parse()
|
||||
.into_diagnostic()?;
|
||||
Driver::rechunk(
|
||||
&RechunkOpts::builder()
|
||||
RechunkOpts::builder()
|
||||
.image(image_name)
|
||||
.containerfile(containerfile)
|
||||
.maybe_platform(self.platform)
|
||||
.tags(tags.collect_cow_vec())
|
||||
.tags(tags)
|
||||
.push(self.push)
|
||||
.version(format!(
|
||||
.version(&format!(
|
||||
"{version}.<date>",
|
||||
version = Driver::get_os_version()
|
||||
.oci_ref(&recipe.base_image_ref()?)
|
||||
|
|
@ -359,23 +354,23 @@ impl BuildCommand {
|
|||
.retry_count(self.retry_count)
|
||||
.compression(self.compression_format)
|
||||
.base_digest(
|
||||
Driver::get_metadata(
|
||||
&GetMetadataOpts::builder()
|
||||
&Driver::get_metadata(
|
||||
GetMetadataOpts::builder()
|
||||
.image(&base_image)
|
||||
.maybe_platform(self.platform)
|
||||
.build(),
|
||||
)?
|
||||
.digest,
|
||||
)
|
||||
.repo(Driver::get_repo_url()?)
|
||||
.name(&*recipe.name)
|
||||
.description(&*recipe.description)
|
||||
.base_image(format!("{}:{}", &recipe.base_image, &recipe.image_version))
|
||||
.repo(&Driver::get_repo_url()?)
|
||||
.name(&recipe.name)
|
||||
.description(&recipe.description)
|
||||
.base_image(&format!("{}:{}", &recipe.base_image, &recipe.image_version))
|
||||
.maybe_tempdir(self.tempdir.as_deref())
|
||||
.clear_plan(self.rechunk_clear_plan)
|
||||
.maybe_cache_from(cache_image)
|
||||
.maybe_cache_to(cache_image)
|
||||
.secrets(recipe.get_secrets())
|
||||
.secrets(&recipe.get_secrets())
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
|
@ -384,8 +379,8 @@ impl BuildCommand {
|
|||
let image_name = Driver::generate_image_name(
|
||||
GenerateImageNameOpts::builder()
|
||||
.name(recipe.name.trim())
|
||||
.maybe_registry(self.credentials.registry.as_ref().map(|r| cowstr!(r)))
|
||||
.maybe_registry_namespace(self.registry_namespace.as_ref().map(|r| cowstr!(r)))
|
||||
.maybe_registry(self.credentials.registry.as_deref())
|
||||
.maybe_registry_namespace(self.registry_namespace.as_deref())
|
||||
.build(),
|
||||
)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -142,6 +142,19 @@ impl GenerateCommand {
|
|||
let base_image: Reference = format!("{}:{}", &recipe.base_image, &recipe.image_version)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
let base_digest = &Driver::get_metadata(
|
||||
GetMetadataOpts::builder()
|
||||
.image(&base_image)
|
||||
.maybe_platform(self.platform)
|
||||
.build(),
|
||||
)?
|
||||
.digest;
|
||||
let build_scripts_image = &determine_scripts_tag(self.platform)?;
|
||||
let repo = &Driver::get_repo_url()?;
|
||||
let build_features = &[
|
||||
#[cfg(feature = "bootc")]
|
||||
"bootc".into(),
|
||||
];
|
||||
|
||||
let template = ContainerFileTemplate::builder()
|
||||
.os_version(
|
||||
|
|
@ -153,19 +166,12 @@ impl GenerateCommand {
|
|||
.build_id(Driver::get_build_id())
|
||||
.recipe(&recipe)
|
||||
.recipe_path(recipe_path.as_path())
|
||||
.registry(registry)
|
||||
.repo(Driver::get_repo_url()?)
|
||||
.build_scripts_image(determine_scripts_tag(self.platform)?.to_string())
|
||||
.base_digest(
|
||||
Driver::get_metadata(
|
||||
&GetMetadataOpts::builder()
|
||||
.image(&base_image)
|
||||
.maybe_platform(self.platform)
|
||||
.build(),
|
||||
)?
|
||||
.digest,
|
||||
)
|
||||
.registry(®istry)
|
||||
.repo(repo)
|
||||
.build_scripts_image(build_scripts_image)
|
||||
.base_digest(base_digest)
|
||||
.maybe_nushell_version(recipe.nushell_version.as_ref())
|
||||
.build_features(build_features)
|
||||
.build();
|
||||
|
||||
let output_str = template.render().into_diagnostic()?;
|
||||
|
|
@ -197,7 +203,7 @@ fn determine_scripts_tag(platform: Option<Platform>) -> Result<Reference> {
|
|||
.parse()
|
||||
.into_diagnostic()
|
||||
.and_then(|image| {
|
||||
Driver::get_metadata(&opts.clone().image(&image).build())
|
||||
Driver::get_metadata(opts.clone().image(&image).build())
|
||||
.inspect_err(|e| trace!("{e:?}"))
|
||||
.map(|_| image)
|
||||
})
|
||||
|
|
@ -205,7 +211,7 @@ fn determine_scripts_tag(platform: Option<Platform>) -> Result<Reference> {
|
|||
let image: Reference = format!("{BUILD_SCRIPTS_IMAGE_REF}:{}", shadow::BRANCH)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
Driver::get_metadata(&opts.clone().image(&image).build())
|
||||
Driver::get_metadata(opts.clone().image(&image).build())
|
||||
.inspect_err(|e| trace!("{e:?}"))
|
||||
.map(|_| image)
|
||||
})
|
||||
|
|
@ -213,7 +219,7 @@ fn determine_scripts_tag(platform: Option<Platform>) -> Result<Reference> {
|
|||
let image: Reference = format!("{BUILD_SCRIPTS_IMAGE_REF}:v{}", crate_version!())
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
Driver::get_metadata(&opts.image(&image).build())
|
||||
Driver::get_metadata(opts.image(&image).build())
|
||||
.inspect_err(|e| trace!("{e:?}"))
|
||||
.map(|_| image)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use blue_build_recipe::Recipe;
|
|||
use blue_build_utils::{
|
||||
constants::{ARCHIVE_SUFFIX, BB_SKIP_VALIDATION},
|
||||
string_vec,
|
||||
traits::CowCollecter,
|
||||
};
|
||||
use bon::Builder;
|
||||
use clap::{Args, Subcommand, ValueEnum};
|
||||
|
|
@ -189,8 +188,10 @@ impl GenerateIsoCommand {
|
|||
format!("SECURE_BOOT_KEY_URL={}", self.secure_boot_url),
|
||||
format!("ENROLLMENT_PASSWORD={}", self.enrollment_password),
|
||||
];
|
||||
let image_out_dir = &image_out_dir.display().to_string();
|
||||
let output_dir = &output_dir.display().to_string();
|
||||
let mut vols = run_volumes![
|
||||
output_dir.display().to_string() => "/build-container-installer/build",
|
||||
output_dir => "/build-container-installer/build",
|
||||
"dnf-cache" => "/cache/dnf/",
|
||||
];
|
||||
|
||||
|
|
@ -239,8 +240,8 @@ impl GenerateIsoCommand {
|
|||
.call()?,
|
||||
),
|
||||
]);
|
||||
vols.extend(run_volumes![
|
||||
image_out_dir.display().to_string() => "/img_src/",
|
||||
vols.extend(&run_volumes![
|
||||
image_out_dir => "/img_src/",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -250,11 +251,11 @@ impl GenerateIsoCommand {
|
|||
.image("ghcr.io/jasonn3/build-container-installer")
|
||||
.privileged(true)
|
||||
.remove(true)
|
||||
.args(args.collect_cow_vec())
|
||||
.volumes(vols)
|
||||
.args(&args)
|
||||
.volumes(&vols)
|
||||
.build();
|
||||
|
||||
let status = Driver::run(&opts)?;
|
||||
let status = Driver::run(opts)?;
|
||||
|
||||
if !status.success() {
|
||||
bail!("Failed to create ISO");
|
||||
|
|
|
|||
|
|
@ -518,8 +518,8 @@ impl InitCommand {
|
|||
.with_context(|| format!("Failed to delete old public file {COSIGN_PUB_PATH}"))?;
|
||||
|
||||
Driver::generate_key_pair(
|
||||
&GenerateKeyPairOpts::builder()
|
||||
.maybe_dir(self.dir.as_ref())
|
||||
GenerateKeyPairOpts::builder()
|
||||
.maybe_dir(self.dir.as_deref())
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ impl BlueBuildCommand for PruneCommand {
|
|||
}
|
||||
|
||||
Driver::prune(
|
||||
&PruneOpts::builder()
|
||||
PruneOpts::builder()
|
||||
.all(self.all)
|
||||
.volumes(self.volumes)
|
||||
.build(),
|
||||
|
|
|
|||
|
|
@ -1,29 +1,19 @@
|
|||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use blue_build_process_management::{
|
||||
drivers::{Driver, DriverArgs},
|
||||
logging::CommandLogging,
|
||||
use blue_build_process_management::drivers::{
|
||||
BootDriver, BuildDriver, CiDriver, Driver, DriverArgs, PodmanDriver, RunDriver,
|
||||
opts::{BuildOpts, GenerateImageNameOpts, RemoveImageOpts, SwitchOpts},
|
||||
types::ImageRef,
|
||||
};
|
||||
use blue_build_recipe::Recipe;
|
||||
use blue_build_utils::{
|
||||
constants::{
|
||||
ARCHIVE_SUFFIX, BB_SKIP_VALIDATION, LOCAL_BUILD, OCI_ARCHIVE, OSTREE_UNVERIFIED_IMAGE,
|
||||
SUDO_ASKPASS,
|
||||
},
|
||||
has_env_var, running_as_root,
|
||||
};
|
||||
use blue_build_utils::constants::BB_SKIP_VALIDATION;
|
||||
use bon::Builder;
|
||||
use clap::Args;
|
||||
use comlexr::cmd;
|
||||
use indicatif::ProgressBar;
|
||||
use log::{debug, trace};
|
||||
use log::trace;
|
||||
use miette::{IntoDiagnostic, Result, bail};
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::{commands::build::BuildCommand, rpm_ostree_status::RpmOstreeStatus};
|
||||
use crate::commands::generate::GenerateCommand;
|
||||
|
||||
use super::BlueBuildCommand;
|
||||
|
||||
|
|
@ -60,238 +50,59 @@ impl BlueBuildCommand for SwitchCommand {
|
|||
|
||||
Driver::init(self.drivers);
|
||||
|
||||
let status = RpmOstreeStatus::try_new()?;
|
||||
trace!("{status:?}");
|
||||
let status = Driver::status()?;
|
||||
|
||||
if status.transaction_in_progress() {
|
||||
bail!("There is a transaction in progress. Please cancel it using `rpm-ostree cancel`");
|
||||
}
|
||||
|
||||
let recipe = Recipe::parse(&self.recipe)?;
|
||||
let image_name = Driver::generate_image_name(
|
||||
GenerateImageNameOpts::builder()
|
||||
.name(recipe.name.trim())
|
||||
.build(),
|
||||
)?;
|
||||
let tempdir = if let Some(ref dir) = self.tempdir {
|
||||
TempDir::new_in(dir).into_diagnostic()?
|
||||
} else {
|
||||
TempDir::new().into_diagnostic()?
|
||||
};
|
||||
trace!("{tempdir:?}");
|
||||
let containerfile = tempdir
|
||||
.path()
|
||||
.join(blue_build_utils::generate_containerfile_path(&self.recipe)?);
|
||||
|
||||
BuildCommand::builder()
|
||||
.recipe([self.recipe.clone()])
|
||||
.archive(tempdir.path())
|
||||
.maybe_tempdir(self.tempdir.clone())
|
||||
.skip_validation(self.skip_validation)
|
||||
GenerateCommand::builder()
|
||||
.output(&containerfile)
|
||||
.recipe(&self.recipe)
|
||||
.build()
|
||||
.try_run()?;
|
||||
PodmanDriver::build(
|
||||
BuildOpts::builder()
|
||||
.image(&ImageRef::from(&image_name))
|
||||
.containerfile(&containerfile)
|
||||
.secrets(&recipe.get_secrets())
|
||||
.build(),
|
||||
)?;
|
||||
PodmanDriver::copy_image_to_root_store(&image_name)?;
|
||||
PodmanDriver::remove_image(RemoveImageOpts::builder().image(&image_name).build())?;
|
||||
|
||||
let recipe = Recipe::parse(&self.recipe)?;
|
||||
let image_file_name = format!(
|
||||
"{}.{ARCHIVE_SUFFIX}",
|
||||
recipe.name.to_lowercase().replace('/', "_")
|
||||
);
|
||||
let temp_file_path = tempdir.path().join(&image_file_name);
|
||||
let archive_path = Path::new(LOCAL_BUILD).join(&image_file_name);
|
||||
|
||||
Self::clean_local_build_dir()?;
|
||||
Self::move_archive(&temp_file_path, &archive_path)?;
|
||||
|
||||
// We drop the tempdir ahead of time so that the directory
|
||||
// can be cleaned out.
|
||||
drop(tempdir);
|
||||
|
||||
self.switch(&archive_path, &status)
|
||||
}
|
||||
}
|
||||
|
||||
impl SwitchCommand {
|
||||
fn switch(&self, archive_path: &Path, status: &RpmOstreeStatus<'_>) -> Result<()> {
|
||||
trace!(
|
||||
"SwitchCommand::switch({}, {status:#?})",
|
||||
archive_path.display()
|
||||
);
|
||||
|
||||
let status = if status.is_booted_on_archive(archive_path)
|
||||
|| status.is_staged_on_archive(archive_path)
|
||||
if status
|
||||
.booted_image()
|
||||
.is_some_and(|booted| booted == image_name)
|
||||
{
|
||||
let command = cmd!("rpm-ostree", "upgrade", if self.reboot => "--reboot");
|
||||
|
||||
trace!("{command:?}");
|
||||
command
|
||||
Driver::upgrade(
|
||||
SwitchOpts::builder()
|
||||
.image(&image_name)
|
||||
.reboot(self.reboot)
|
||||
.build(),
|
||||
)
|
||||
} else {
|
||||
let image_ref = format!(
|
||||
"{OSTREE_UNVERIFIED_IMAGE}:{OCI_ARCHIVE}:{path}",
|
||||
path = archive_path.display()
|
||||
);
|
||||
|
||||
let command = cmd!(
|
||||
"rpm-ostree",
|
||||
"rebase",
|
||||
&image_ref,
|
||||
if self.reboot => "--reboot",
|
||||
);
|
||||
|
||||
trace!("{command:?}");
|
||||
command
|
||||
Driver::switch(
|
||||
SwitchOpts::builder()
|
||||
.image(&image_name)
|
||||
.reboot(self.reboot)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
.build_status(
|
||||
format!("{}", archive_path.display()),
|
||||
"Switching to new image",
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
|
||||
if !status.success() {
|
||||
bail!("Failed to switch to new image!");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_archive(from: &Path, to: &Path) -> Result<()> {
|
||||
trace!(
|
||||
"SwitchCommand::move_archive({}, {})",
|
||||
from.display(),
|
||||
to.display()
|
||||
);
|
||||
|
||||
let progress = ProgressBar::new_spinner();
|
||||
progress.enable_steady_tick(Duration::from_millis(100));
|
||||
progress.set_message(format!("Moving image archive to {}...", to.display()));
|
||||
|
||||
let status = {
|
||||
let c = cmd!(
|
||||
if running_as_root() {
|
||||
"mv"
|
||||
} else {
|
||||
"sudo"
|
||||
},
|
||||
if !running_as_root() && has_env_var(SUDO_ASKPASS) => [
|
||||
"-A",
|
||||
"-p",
|
||||
format!("Password needed to move {from:?} to {to:?}"),
|
||||
],
|
||||
if !running_as_root() => "mv",
|
||||
from,
|
||||
to,
|
||||
);
|
||||
trace!("{c:?}");
|
||||
c
|
||||
}
|
||||
.status()
|
||||
.into_diagnostic()?;
|
||||
|
||||
progress.finish_and_clear();
|
||||
|
||||
if !status.success() {
|
||||
bail!(
|
||||
"Failed to move archive from {from} to {to}",
|
||||
from = from.display(),
|
||||
to = to.display()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clean_local_build_dir() -> Result<()> {
|
||||
trace!("SwitchCommand::clean_local_build_dir()");
|
||||
|
||||
let local_build_path = Path::new(LOCAL_BUILD);
|
||||
|
||||
if local_build_path.exists() {
|
||||
debug!("Cleaning out build dir {LOCAL_BUILD}");
|
||||
|
||||
let mut command = {
|
||||
let c = cmd!(
|
||||
if running_as_root() {
|
||||
"ls"
|
||||
} else {
|
||||
"sudo"
|
||||
},
|
||||
if !running_as_root() && has_env_var(SUDO_ASKPASS) => [
|
||||
"-A",
|
||||
"-p",
|
||||
format!("Password required to list files in {LOCAL_BUILD}"),
|
||||
],
|
||||
if !running_as_root() => "ls",
|
||||
LOCAL_BUILD
|
||||
);
|
||||
trace!("{c:?}");
|
||||
c
|
||||
};
|
||||
let output =
|
||||
String::from_utf8(command.output().into_diagnostic()?.stdout).into_diagnostic()?;
|
||||
|
||||
trace!("{output}");
|
||||
|
||||
let files = output
|
||||
.lines()
|
||||
.filter(|line| line.ends_with(ARCHIVE_SUFFIX))
|
||||
.map(|file| local_build_path.join(file).display().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !files.is_empty() {
|
||||
let progress = ProgressBar::new_spinner();
|
||||
progress.enable_steady_tick(Duration::from_millis(100));
|
||||
progress.set_message("Removing old image archive files...");
|
||||
|
||||
let status = {
|
||||
let c = cmd!(
|
||||
if running_as_root() {
|
||||
"rm"
|
||||
} else {
|
||||
"sudo"
|
||||
},
|
||||
if !running_as_root() && has_env_var(SUDO_ASKPASS) => [
|
||||
"-A",
|
||||
"-p",
|
||||
format!("Password required to remove files: {files:?}"),
|
||||
],
|
||||
if !running_as_root() => "rm",
|
||||
"-f",
|
||||
for files,
|
||||
);
|
||||
trace!("{c:?}");
|
||||
c
|
||||
}
|
||||
.status()
|
||||
.into_diagnostic()?;
|
||||
|
||||
progress.finish_and_clear();
|
||||
|
||||
if !status.success() {
|
||||
bail!("Failed to clean out archives in {LOCAL_BUILD}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
"Creating build output dir at {}",
|
||||
local_build_path.display()
|
||||
);
|
||||
|
||||
let status = {
|
||||
let c = cmd!(
|
||||
if running_as_root() {
|
||||
"mkdir"
|
||||
} else {
|
||||
"sudo"
|
||||
},
|
||||
if !running_as_root() && has_env_var(SUDO_ASKPASS) => [
|
||||
"-A",
|
||||
"-p",
|
||||
format!("Password needed to create directory {local_build_path:?}"),
|
||||
],
|
||||
if !running_as_root() => "mkdir",
|
||||
"-p",
|
||||
local_build_path,
|
||||
);
|
||||
trace!("{c:?}");
|
||||
c
|
||||
}
|
||||
.status()
|
||||
.into_diagnostic()?;
|
||||
|
||||
if !status.success() {
|
||||
bail!("Failed to create directory {LOCAL_BUILD}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue