deb: change ui/ux of pkh deb
This commit is contained in:
+39
-5
@@ -1,4 +1,5 @@
|
||||
use crate::context::{self, Context, ContextConfig};
|
||||
use crate::ui::deb::{DebUi, Phase};
|
||||
use directories::ProjectDirs;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
@@ -28,6 +29,7 @@ impl EphemeralContextGuard {
|
||||
series: &str,
|
||||
arch: Option<&str>,
|
||||
base_ctx: Arc<Context>,
|
||||
ui: Option<Arc<DebUi>>,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let current_context_name = context::manager().current_name();
|
||||
|
||||
@@ -43,7 +45,8 @@ impl EphemeralContextGuard {
|
||||
);
|
||||
|
||||
// Download and extract the chroot tarball
|
||||
Self::download_and_extract_chroot(series, arch, &chroot_path, base_ctx.clone()).await?;
|
||||
Self::download_and_extract_chroot(series, arch, &chroot_path, base_ctx.clone(), &ui)
|
||||
.await?;
|
||||
|
||||
// Switch to an ephemeral context to build the package in the chroot
|
||||
context::manager().set_current_ephemeral(Context::new(ContextConfig::Unshare {
|
||||
@@ -64,6 +67,7 @@ impl EphemeralContextGuard {
|
||||
arch: Option<&str>,
|
||||
chroot_path: &PathBuf,
|
||||
ctx: Arc<context::Context>,
|
||||
ui: &Option<Arc<DebUi>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Clone ctx for use in create_device_nodes after download_chroot_tarball consumes it
|
||||
let ctx_for_devices = ctx.clone();
|
||||
@@ -119,7 +123,10 @@ impl EphemeralContextGuard {
|
||||
series,
|
||||
arch
|
||||
);
|
||||
Self::download_chroot_tarball(series, arch, &tarball_path, ctx).await?;
|
||||
if let Some(u) = ui {
|
||||
u.phase(Phase::PreparingChroot);
|
||||
}
|
||||
Self::download_chroot_tarball(series, arch, &tarball_path, ctx, ui).await?;
|
||||
} else {
|
||||
log::debug!(
|
||||
"Using cached chroot tarball for {} (arch: {:?})",
|
||||
@@ -130,10 +137,16 @@ impl EphemeralContextGuard {
|
||||
|
||||
// Extract tarball to chroot directory
|
||||
log::debug!("Extracting chroot tarball to {}...", chroot_path.display());
|
||||
Self::extract_tarball(&tarball_path, chroot_path)?;
|
||||
if let Some(u) = ui {
|
||||
u.phase(Phase::ExtractingChroot);
|
||||
}
|
||||
Self::extract_tarball(&tarball_path, chroot_path, ui.as_deref())?;
|
||||
|
||||
// Create device nodes in the chroot
|
||||
log::debug!("Creating device nodes in chroot...");
|
||||
if let Some(u) = ui {
|
||||
u.phase(Phase::FinalizingChroot);
|
||||
}
|
||||
Self::create_device_nodes(chroot_path, ctx_for_devices.clone())?;
|
||||
|
||||
// Bind mount /proc from host into chroot (before entering unshare namespace)
|
||||
@@ -149,6 +162,7 @@ impl EphemeralContextGuard {
|
||||
arch: Option<&str>,
|
||||
tarball_path: &Path,
|
||||
ctx: Arc<context::Context>,
|
||||
ui: &Option<Arc<DebUi>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Create a lock file to make sure that noone tries to use the file while it's not fully downloaded
|
||||
let lockfile_path = tarball_path.with_extension("lock");
|
||||
@@ -182,6 +196,10 @@ impl EphemeralContextGuard {
|
||||
cmd.arg(series)
|
||||
.arg(tarball_path.to_string_lossy().to_string());
|
||||
|
||||
if let Some(u) = ui {
|
||||
cmd.capture(u.sink());
|
||||
}
|
||||
|
||||
let status = cmd.status()?;
|
||||
|
||||
if !status.success() {
|
||||
@@ -216,6 +234,7 @@ impl EphemeralContextGuard {
|
||||
fn extract_tarball(
|
||||
tarball_path: &PathBuf,
|
||||
chroot_path: &PathBuf,
|
||||
ui: Option<&DebUi>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Create the chroot directory
|
||||
fs::create_dir_all(chroot_path)?;
|
||||
@@ -225,8 +244,23 @@ impl EphemeralContextGuard {
|
||||
let xz_decoder = XzDecoder::new(tarball_file);
|
||||
let mut archive = Archive::new(xz_decoder);
|
||||
|
||||
// Extract all files to the chroot directory
|
||||
archive.unpack(chroot_path)?;
|
||||
// Extract entries one by one so progress can be reported (a full
|
||||
// second decompression pass just to count entries upfront would be
|
||||
// too expensive for multi-hundred-MB chroot tarballs)
|
||||
let mut count = 0usize;
|
||||
for entry in archive.entries()? {
|
||||
let mut entry = entry?;
|
||||
entry.unpack_in(chroot_path)?;
|
||||
count += 1;
|
||||
if count.is_multiple_of(100)
|
||||
&& let Some(u) = ui
|
||||
{
|
||||
u.progress_message(&format!("Extracting chroot… ({count} files)"));
|
||||
}
|
||||
}
|
||||
if let Some(u) = ui {
|
||||
u.progress_message(&format!("Extracting chroot… ({count} files)"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+117
-55
@@ -1,7 +1,9 @@
|
||||
/// Local binary package building
|
||||
/// Directly calling 'debian/rules' in current context
|
||||
use crate::context::Context;
|
||||
use crate::context::{Context, ContextCommand, LineSink};
|
||||
use crate::deb::find_dsc_file;
|
||||
use crate::ui::deb::{DebUi, Phase};
|
||||
use crate::ui::logfmt::QuiltClassifier;
|
||||
use log::warn;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
@@ -11,6 +13,17 @@ use std::sync::Arc;
|
||||
use crate::apt;
|
||||
use crate::deb::cross;
|
||||
|
||||
/// Attach the capture sink to a command when the live UI is active
|
||||
fn cap<'a>(
|
||||
cmd: &'a mut ContextCommand<'a>,
|
||||
sink: &Option<Arc<dyn LineSink>>,
|
||||
) -> &'a mut ContextCommand<'a> {
|
||||
if let Some(s) = sink {
|
||||
cmd.capture(s.clone());
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn build(
|
||||
package: &str,
|
||||
@@ -23,7 +36,10 @@ pub async fn build(
|
||||
ppa: Option<&[&str]>,
|
||||
inject_packages: Option<&[&str]>,
|
||||
ctx: Arc<Context>,
|
||||
ui: Option<Arc<DebUi>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let sink: Option<Arc<dyn LineSink>> = ui.as_ref().map(|u| u.sink());
|
||||
|
||||
// Environment
|
||||
let mut env = HashMap::<String, String>::new();
|
||||
env.insert("LANG".to_string(), "C".to_string());
|
||||
@@ -155,19 +171,22 @@ pub async fn build(
|
||||
|
||||
// Update package lists
|
||||
log::debug!("Updating package lists for local build...");
|
||||
let status = ctx
|
||||
.command("apt-get")
|
||||
.envs(env.clone())
|
||||
.arg("update")
|
||||
.status()
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to run 'apt-get update' inside the build context: {}. \
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::UpdatingPackageLists);
|
||||
}
|
||||
let status = cap(
|
||||
ctx.command("apt-get").envs(env.clone()).arg("update"),
|
||||
&sink,
|
||||
)
|
||||
.status()
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to run 'apt-get update' inside the build context: {}. \
|
||||
If this is a local build, make sure apt-get is available and \
|
||||
try executing with sudo.",
|
||||
e
|
||||
)
|
||||
})?;
|
||||
e
|
||||
)
|
||||
})?;
|
||||
if !status.success() {
|
||||
return Err("apt-get update failed inside the build context. \
|
||||
If this is a local build, try executing with sudo, \
|
||||
@@ -193,7 +212,10 @@ pub async fn build(
|
||||
cmd.arg(format!("libc6:{arch}"));
|
||||
cmd.arg(format!("libc6-dev:{arch}"));
|
||||
}
|
||||
let status = cmd.status()?;
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::InstallingEssentials);
|
||||
}
|
||||
let status = cap(&mut cmd, &sink).status()?;
|
||||
if !status.success() {
|
||||
return Err("Could not install essential packages for the build".into());
|
||||
}
|
||||
@@ -206,15 +228,18 @@ pub async fn build(
|
||||
.ok_or("Invalid package directory path")?;
|
||||
|
||||
// Apply quilt patches if the package provides a patch series
|
||||
apply_quilt_patches(package_dir_str, &env, ctx.clone())?;
|
||||
apply_quilt_patches(package_dir_str, &env, ctx.clone(), &ui, &sink)?;
|
||||
|
||||
// Install injected packages if specified
|
||||
if let Some(packages) = inject_packages {
|
||||
install_injected_packages(packages, &env, ctx.clone())?;
|
||||
install_injected_packages(packages, &env, ctx.clone(), &ui, &sink)?;
|
||||
}
|
||||
|
||||
// Install arch-specific build dependencies
|
||||
log::debug!("Installing arch-specific build dependencies...");
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::InstallingBuildDeps);
|
||||
}
|
||||
let mut cmd = ctx.command("apt-get");
|
||||
cmd.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
@@ -224,51 +249,69 @@ pub async fn build(
|
||||
cmd.arg(format!("--host-architecture={arch}"));
|
||||
}
|
||||
cmd.arg("--arch-only");
|
||||
let status = cmd.arg("./").status()?;
|
||||
let status = cap(&mut cmd, &sink).arg("./").status()?;
|
||||
|
||||
// If build-dep fails, we try to explain the failure using dose-debcheck
|
||||
if !status.success() {
|
||||
if let Some(u) = &ui {
|
||||
u.suspend();
|
||||
}
|
||||
dose3_explain_dependencies(package, version, arch, build_root, cross, ctx.clone())?;
|
||||
return Err("Could not install build-dependencies for the build".into());
|
||||
}
|
||||
|
||||
// Install arch-independant build dependencies
|
||||
log::debug!("Installing arch-independant build dependencies...");
|
||||
let status = ctx
|
||||
.command("apt-get")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("-y")
|
||||
.arg("build-dep")
|
||||
.arg("./")
|
||||
.status()?;
|
||||
let status = cap(
|
||||
ctx.command("apt-get")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("-y")
|
||||
.arg("build-dep")
|
||||
.arg("./"),
|
||||
&sink,
|
||||
)
|
||||
.status()?;
|
||||
|
||||
// If build-dep fails, we try to explain the failure using dose-debcheck
|
||||
if !status.success() {
|
||||
if let Some(u) = &ui {
|
||||
u.suspend();
|
||||
}
|
||||
dose3_explain_dependencies(package, version, arch, build_root, cross, ctx.clone())?;
|
||||
return Err("Could not install build-dependencies for the build".into());
|
||||
}
|
||||
|
||||
// Run the build step
|
||||
log::debug!("Building (debian/rules build) package...");
|
||||
let status = ctx
|
||||
.command("debian/rules")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("build")
|
||||
.status()?;
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::Building);
|
||||
}
|
||||
let status = cap(
|
||||
ctx.command("debian/rules")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("build"),
|
||||
&sink,
|
||||
)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err("Error while building the package".into());
|
||||
}
|
||||
|
||||
// Run the 'binary' step to produce deb
|
||||
let status = ctx
|
||||
.command("fakeroot")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("debian/rules")
|
||||
.arg("binary")
|
||||
.status()?;
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::ProducingBinaries);
|
||||
}
|
||||
let status = cap(
|
||||
ctx.command("fakeroot")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("debian/rules")
|
||||
.arg("binary"),
|
||||
&sink,
|
||||
)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err(
|
||||
"Error while building the binary artifacts (.deb) from the built package".into(),
|
||||
@@ -284,6 +327,8 @@ fn apply_quilt_patches(
|
||||
package_dir: &str,
|
||||
env: &HashMap<String, String>,
|
||||
ctx: Arc<Context>,
|
||||
ui: &Option<Arc<DebUi>>,
|
||||
sink: &Option<Arc<dyn LineSink>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let series_path = Path::new(package_dir).join("debian/patches/series");
|
||||
if !ctx.exists(&series_path)? {
|
||||
@@ -296,9 +341,11 @@ fn apply_quilt_patches(
|
||||
|
||||
// Skip patch application if the series file contains no patches
|
||||
let series_content = ctx.read_file(&series_path)?;
|
||||
let has_patches = series_content
|
||||
let total_patches = series_content
|
||||
.lines()
|
||||
.any(|line| !line.trim().is_empty() && !line.trim().starts_with('#'));
|
||||
.filter(|line| !line.trim().is_empty() && !line.trim().starts_with('#'))
|
||||
.count();
|
||||
let has_patches = total_patches > 0;
|
||||
if !has_patches {
|
||||
log::debug!(
|
||||
"'{}' contains no patches, skipping quilt patch application",
|
||||
@@ -309,28 +356,37 @@ fn apply_quilt_patches(
|
||||
|
||||
// Make sure quilt is available in the build context
|
||||
log::debug!("Installing quilt for patch application...");
|
||||
let status = ctx
|
||||
.command("apt-get")
|
||||
.envs(env.clone())
|
||||
.arg("-y")
|
||||
.arg("install")
|
||||
.arg("quilt")
|
||||
.status()?;
|
||||
let status = cap(
|
||||
ctx.command("apt-get")
|
||||
.envs(env.clone())
|
||||
.arg("-y")
|
||||
.arg("install")
|
||||
.arg("quilt"),
|
||||
sink,
|
||||
)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err("Could not install 'quilt', required to apply patches".into());
|
||||
}
|
||||
|
||||
// Apply all patches listed in the series
|
||||
log::info!("Applying quilt patches from debian/patches/series...");
|
||||
if let Some(u) = ui {
|
||||
u.phase_with(
|
||||
Phase::ApplyingPatches,
|
||||
Box::new(QuiltClassifier::new(total_patches)),
|
||||
);
|
||||
}
|
||||
let mut patch_env = env.clone();
|
||||
patch_env.insert("QUILT_PATCHES".to_string(), "debian/patches".to_string());
|
||||
let status = ctx
|
||||
.command("quilt")
|
||||
.current_dir(package_dir)
|
||||
.envs(patch_env)
|
||||
.arg("push")
|
||||
.arg("-a")
|
||||
.status()?;
|
||||
let status = cap(
|
||||
ctx.command("quilt")
|
||||
.current_dir(package_dir)
|
||||
.envs(patch_env)
|
||||
.arg("push")
|
||||
.arg("-a"),
|
||||
sink,
|
||||
)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err("Failed to apply quilt patches ('quilt push -a')".into());
|
||||
}
|
||||
@@ -360,9 +416,15 @@ fn install_injected_packages(
|
||||
packages: &[&str],
|
||||
env: &HashMap<String, String>,
|
||||
ctx: Arc<Context>,
|
||||
ui: &Option<Arc<DebUi>>,
|
||||
sink: &Option<Arc<dyn LineSink>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
log::info!("Installing injected packages: {:?}", packages);
|
||||
|
||||
if let Some(u) = ui {
|
||||
u.phase(Phase::InjectingPackages);
|
||||
}
|
||||
|
||||
// Separate .deb files from package names
|
||||
let mut deb_files: Vec<String> = Vec::new();
|
||||
let mut package_names: Vec<&str> = Vec::new();
|
||||
@@ -400,7 +462,7 @@ fn install_injected_packages(
|
||||
if !package_names.is_empty() {
|
||||
cmd.args(&package_names);
|
||||
}
|
||||
let status = cmd.status()?;
|
||||
let status = cap(&mut cmd, sink).status()?;
|
||||
if !status.success() {
|
||||
return Err(format!("Could not install injected packages: {:?}", deb_files).into());
|
||||
}
|
||||
|
||||
+125
-40
@@ -3,6 +3,7 @@ mod ephemeral;
|
||||
mod local;
|
||||
|
||||
use crate::context::{self, Context};
|
||||
use crate::ui::deb::{DebUi, Phase};
|
||||
use std::error::Error;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
@@ -15,6 +16,11 @@ pub enum BuildMode {
|
||||
}
|
||||
|
||||
/// Build package in 'cwd' to a .deb
|
||||
///
|
||||
/// Returns the list of produced .deb files retrieved locally. When `ui` is
|
||||
/// set, a live view (status bar + rolling log pane) is displayed and all
|
||||
/// subprocess output is captured through it; on failure the widget is cleared
|
||||
/// and a summary of captured errors is printed.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn build_binary_package(
|
||||
arch: Option<&str>,
|
||||
@@ -26,7 +32,43 @@ pub async fn build_binary_package(
|
||||
ppa: Option<&[&str]>,
|
||||
inject_packages: Option<&[&str]>,
|
||||
ctx: Option<Arc<Context>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
ui: Option<Arc<DebUi>>,
|
||||
) -> Result<Vec<PathBuf>, Box<dyn Error>> {
|
||||
let result = build_binary_package_impl(
|
||||
arch,
|
||||
series,
|
||||
pocket,
|
||||
cwd,
|
||||
cross,
|
||||
mode,
|
||||
ppa,
|
||||
inject_packages,
|
||||
ctx,
|
||||
&ui,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let (Some(u), Err(_)) = (&ui, &result) {
|
||||
u.finish_failure();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Implementation of [`build_binary_package`], without failure handling
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn build_binary_package_impl(
|
||||
arch: Option<&str>,
|
||||
series: Option<&str>,
|
||||
pocket: Option<&str>,
|
||||
cwd: Option<&Path>,
|
||||
cross: bool,
|
||||
mode: Option<BuildMode>,
|
||||
ppa: Option<&[&str]>,
|
||||
inject_packages: Option<&[&str]>,
|
||||
ctx: Option<Arc<Context>>,
|
||||
ui: &Option<Arc<DebUi>>,
|
||||
) -> Result<Vec<PathBuf>, Box<dyn Error>> {
|
||||
let cwd = cwd.unwrap_or_else(|| Path::new("."));
|
||||
|
||||
// Parse changelog to get package name, version and series
|
||||
@@ -61,12 +103,22 @@ pub async fn build_binary_package(
|
||||
// Use provided context or get current
|
||||
let base_ctx = ctx.unwrap_or_else(context::current);
|
||||
|
||||
// Identify the target in the live UI once the changelog is parsed, so
|
||||
// even the chroot download output is attributed and tee'd
|
||||
if let Some(u) = ui {
|
||||
u.set_target(&package, &version, series, arch);
|
||||
}
|
||||
|
||||
// Create an ephemeral unshare context for all Local builds. It is kept in
|
||||
// this scope so it outlives the guarded section below and is only dropped
|
||||
// once the live view has been cleared.
|
||||
let mut guard = if mode == BuildMode::Local {
|
||||
Some(
|
||||
ephemeral::EphemeralContextGuard::new_with_context(
|
||||
series,
|
||||
chroot_arch,
|
||||
base_ctx.clone(),
|
||||
ui.clone(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -74,59 +126,91 @@ pub async fn build_binary_package(
|
||||
None
|
||||
};
|
||||
|
||||
// Get the build context - either the ephemeral context or the base context
|
||||
let build_ctx = if mode == BuildMode::Local {
|
||||
context::current()
|
||||
} else {
|
||||
base_ctx.clone()
|
||||
};
|
||||
let result = async {
|
||||
// Get the build context - either the ephemeral context or the base context
|
||||
let build_ctx = if mode == BuildMode::Local {
|
||||
context::current()
|
||||
} else {
|
||||
base_ctx.clone()
|
||||
};
|
||||
|
||||
// Prepare build directory
|
||||
let build_root = build_ctx.create_temp_dir()?;
|
||||
// Prepare build directory
|
||||
let build_root = build_ctx.create_temp_dir()?;
|
||||
|
||||
// Ensure availability of all needed files for the build
|
||||
let parent_dir = cwd.parent().ok_or("Cannot find parent directory")?;
|
||||
build_ctx.ensure_available(parent_dir, &build_root)?;
|
||||
let parent_dir_name = parent_dir
|
||||
.file_name()
|
||||
.ok_or("Cannot find parent directory name")?;
|
||||
let build_root = format!("{}/{}", build_root, parent_dir_name.to_str().unwrap());
|
||||
// Ensure availability of all needed files for the build
|
||||
let parent_dir = cwd.parent().ok_or("Cannot find parent directory")?;
|
||||
build_ctx.ensure_available(parent_dir, &build_root)?;
|
||||
let parent_dir_name = parent_dir
|
||||
.file_name()
|
||||
.ok_or("Cannot find parent directory name")?;
|
||||
let build_root = format!("{}/{}", build_root, parent_dir_name.to_str().unwrap());
|
||||
|
||||
// Run the build using target build mode
|
||||
match mode {
|
||||
BuildMode::Local => {
|
||||
local::build(
|
||||
&package,
|
||||
&version,
|
||||
arch,
|
||||
series,
|
||||
pocket,
|
||||
&build_root,
|
||||
cross,
|
||||
ppa,
|
||||
inject_packages,
|
||||
build_ctx.clone(),
|
||||
)
|
||||
.await?
|
||||
// Run the build using target build mode
|
||||
match mode {
|
||||
BuildMode::Local => {
|
||||
local::build(
|
||||
&package,
|
||||
&version,
|
||||
arch,
|
||||
series,
|
||||
pocket,
|
||||
&build_root,
|
||||
cross,
|
||||
ppa,
|
||||
inject_packages,
|
||||
build_ctx.clone(),
|
||||
ui.clone(),
|
||||
)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve produced .deb files
|
||||
let remote_files = build_ctx.list_files(Path::new(&build_root))?;
|
||||
for remote_file in remote_files {
|
||||
if remote_file.extension().is_some_and(|ext| ext == "deb") {
|
||||
// Retrieve produced .deb files
|
||||
if let Some(u) = ui {
|
||||
u.phase(Phase::RetrievingArtifacts);
|
||||
}
|
||||
let remote_files = build_ctx.list_files(Path::new(&build_root))?;
|
||||
let deb_files: Vec<PathBuf> = remote_files
|
||||
.into_iter()
|
||||
.filter(|f| f.extension().is_some_and(|ext| ext == "deb"))
|
||||
.collect();
|
||||
let total_debs = deb_files.len();
|
||||
|
||||
let mut artifacts = Vec::with_capacity(total_debs);
|
||||
for (idx, remote_file) in deb_files.iter().enumerate() {
|
||||
let file_name = remote_file.file_name().ok_or("Invalid remote filename")?;
|
||||
let local_dest = parent_dir.join(file_name);
|
||||
build_ctx.retrieve_path(&remote_file, &local_dest)?;
|
||||
build_ctx.retrieve_path(remote_file, &local_dest)?;
|
||||
artifacts.push(local_dest);
|
||||
|
||||
if let Some(u) = ui {
|
||||
u.count_progress("Retrieving artifacts", idx + 1, total_debs);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(u) = ui {
|
||||
u.finish_success(&artifacts, u.elapsed());
|
||||
}
|
||||
|
||||
Ok(artifacts)
|
||||
}
|
||||
.await;
|
||||
|
||||
// Clear the live view before returning: the ephemeral guard is dropped at
|
||||
// the end of this function and its cleanup commands (umount, rm -rf of
|
||||
// the chroot) inherit the terminal, so they must not fight the widget.
|
||||
if let Some(u) = ui {
|
||||
u.suspend();
|
||||
}
|
||||
|
||||
// Mark build as successful to trigger chroot cleanup
|
||||
if let Some(ref mut g) = guard {
|
||||
if result.is_ok()
|
||||
&& let Some(ref mut g) = guard
|
||||
{
|
||||
g.mark_build_successful();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
result
|
||||
}
|
||||
|
||||
/// Find the current package directory by trying both patterns:
|
||||
@@ -321,6 +405,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
Some(ctx),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("Cannot build binary package (deb)");
|
||||
|
||||
Reference in New Issue
Block a user