deb: default to ephemeral context with local builds
Some checks failed
CI / build (push) Failing after 1m28s
Some checks failed
CI / build (push) Failing after 1m28s
This commit is contained in:
143
src/deb/cross.rs
143
src/deb/cross.rs
@@ -1,149 +1,6 @@
|
|||||||
use crate::context;
|
use crate::context;
|
||||||
use crate::context::{Context, ContextConfig};
|
|
||||||
use directories::ProjectDirs;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use tar::Archive;
|
|
||||||
use xz2::read::XzDecoder;
|
|
||||||
|
|
||||||
pub struct EphemeralContextGuard {
|
|
||||||
previous_context: String,
|
|
||||||
chroot_path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EphemeralContextGuard {
|
|
||||||
pub fn new(series: &str) -> Result<Self, Box<dyn Error>> {
|
|
||||||
let current_context_name = context::manager().current_name();
|
|
||||||
|
|
||||||
// Create a temporary directory for the chroot
|
|
||||||
let chroot_path_str = context::current().create_temp_dir()?;
|
|
||||||
let chroot_path = PathBuf::from(chroot_path_str);
|
|
||||||
|
|
||||||
log::debug!(
|
|
||||||
"Creating new chroot for {} at {}...",
|
|
||||||
series,
|
|
||||||
chroot_path.display()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Download and extract the chroot tarball
|
|
||||||
Self::download_and_extract_chroot(series, &chroot_path)?;
|
|
||||||
|
|
||||||
// Switch to an ephemeral context to build the package in the chroot
|
|
||||||
context::manager().set_current_ephemeral(Context::new(ContextConfig::Unshare {
|
|
||||||
path: chroot_path.to_string_lossy().to_string(),
|
|
||||||
parent: Some(current_context_name.clone()),
|
|
||||||
}));
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
previous_context: current_context_name,
|
|
||||||
chroot_path,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn download_and_extract_chroot(
|
|
||||||
series: &str,
|
|
||||||
chroot_path: &PathBuf,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
// Get project directories for caching
|
|
||||||
let proj_dirs = ProjectDirs::from("com", "pkh", "pkh")
|
|
||||||
.ok_or("Could not determine project directories")?;
|
|
||||||
let cache_dir = proj_dirs.cache_dir();
|
|
||||||
fs::create_dir_all(cache_dir)?;
|
|
||||||
|
|
||||||
// Create tarball filename based on series
|
|
||||||
let tarball_filename = format!("{}-buildd.tar.xz", series);
|
|
||||||
let tarball_path = cache_dir.join(&tarball_filename);
|
|
||||||
|
|
||||||
// Download tarball if it doesn't exist
|
|
||||||
if !tarball_path.exists() {
|
|
||||||
log::debug!("Downloading chroot tarball for {}...", series);
|
|
||||||
Self::download_chroot_tarball(series, &tarball_path)?;
|
|
||||||
} else {
|
|
||||||
log::debug!("Using cached chroot tarball for {}", series);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract tarball to chroot directory
|
|
||||||
log::debug!("Extracting chroot tarball to {}...", chroot_path.display());
|
|
||||||
Self::extract_tarball(&tarball_path, chroot_path)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn download_chroot_tarball(series: &str, tarball_path: &Path) -> Result<(), Box<dyn Error>> {
|
|
||||||
// Use mmdebstrap to download the tarball to the cache directory
|
|
||||||
let status = context::current()
|
|
||||||
.command("mmdebstrap")
|
|
||||||
.arg("--variant=buildd")
|
|
||||||
.arg("--mode=unshare")
|
|
||||||
.arg("--format=tar")
|
|
||||||
.arg(series)
|
|
||||||
.arg(tarball_path.to_string_lossy().to_string())
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
return Err(format!("Failed to download chroot tarball for series {}", series).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_tarball(
|
|
||||||
tarball_path: &PathBuf,
|
|
||||||
chroot_path: &PathBuf,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
// Create the chroot directory
|
|
||||||
fs::create_dir_all(chroot_path)?;
|
|
||||||
|
|
||||||
// Open the tarball file
|
|
||||||
let tarball_file = std::fs::File::open(tarball_path)?;
|
|
||||||
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)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for EphemeralContextGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
log::debug!("Cleaning up ephemeral context...");
|
|
||||||
// Reset to normal context
|
|
||||||
if let Err(e) = context::manager().set_current(&self.previous_context) {
|
|
||||||
log::error!("Failed to restore context {}: {}", self.previous_context, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove chroot directory
|
|
||||||
// We use the restored context to execute the cleanup command
|
|
||||||
let result = context::current()
|
|
||||||
.command("sudo")
|
|
||||||
.arg("rm")
|
|
||||||
.arg("-rf")
|
|
||||||
.arg(&self.chroot_path)
|
|
||||||
.status();
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(status) => {
|
|
||||||
if !status.success() {
|
|
||||||
log::error!(
|
|
||||||
"Failed to remove chroot directory {}",
|
|
||||||
self.chroot_path.display()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!(
|
|
||||||
"Failed to execute cleanup command for {}: {}",
|
|
||||||
self.chroot_path.display(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set environment variables for cross-compilation
|
/// Set environment variables for cross-compilation
|
||||||
pub fn setup_environment(
|
pub fn setup_environment(
|
||||||
|
|||||||
148
src/deb/ephemeral.rs
Normal file
148
src/deb/ephemeral.rs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
use crate::context;
|
||||||
|
use crate::context::{Context, ContextConfig};
|
||||||
|
use directories::ProjectDirs;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use tar::Archive;
|
||||||
|
use xz2::read::XzDecoder;
|
||||||
|
|
||||||
|
/// An ephemeral unshare context guard that creates and manages a temporary chroot environment
|
||||||
|
/// for building packages with unshare permissions.
|
||||||
|
pub struct EphemeralContextGuard {
|
||||||
|
previous_context: String,
|
||||||
|
chroot_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EphemeralContextGuard {
|
||||||
|
/// Create a new ephemeral unshare context for the specified series
|
||||||
|
pub fn new(series: &str) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let current_context_name = context::manager().current_name();
|
||||||
|
|
||||||
|
// Create a temporary directory for the chroot
|
||||||
|
let chroot_path_str = context::current().create_temp_dir()?;
|
||||||
|
let chroot_path = PathBuf::from(chroot_path_str);
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"Creating new chroot for {} at {}...",
|
||||||
|
series,
|
||||||
|
chroot_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Download and extract the chroot tarball
|
||||||
|
Self::download_and_extract_chroot(series, &chroot_path)?;
|
||||||
|
|
||||||
|
// Switch to an ephemeral context to build the package in the chroot
|
||||||
|
context::manager().set_current_ephemeral(Context::new(ContextConfig::Unshare {
|
||||||
|
path: chroot_path.to_string_lossy().to_string(),
|
||||||
|
parent: Some(current_context_name.clone()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
previous_context: current_context_name,
|
||||||
|
chroot_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_and_extract_chroot(
|
||||||
|
series: &str,
|
||||||
|
chroot_path: &PathBuf,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
// Get project directories for caching
|
||||||
|
let proj_dirs = ProjectDirs::from("com", "pkh", "pkh")
|
||||||
|
.ok_or("Could not determine project directories")?;
|
||||||
|
let cache_dir = proj_dirs.cache_dir();
|
||||||
|
fs::create_dir_all(cache_dir)?;
|
||||||
|
|
||||||
|
// Create tarball filename based on series
|
||||||
|
let tarball_filename = format!("{}-buildd.tar.xz", series);
|
||||||
|
let tarball_path = cache_dir.join(&tarball_filename);
|
||||||
|
|
||||||
|
// Download tarball if it doesn't exist
|
||||||
|
if !tarball_path.exists() {
|
||||||
|
log::debug!("Downloading chroot tarball for {}...", series);
|
||||||
|
Self::download_chroot_tarball(series, &tarball_path)?;
|
||||||
|
} else {
|
||||||
|
log::debug!("Using cached chroot tarball for {}", series);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract tarball to chroot directory
|
||||||
|
log::debug!("Extracting chroot tarball to {}...", chroot_path.display());
|
||||||
|
Self::extract_tarball(&tarball_path, chroot_path)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_chroot_tarball(series: &str, tarball_path: &Path) -> Result<(), Box<dyn Error>> {
|
||||||
|
// Use mmdebstrap to download the tarball to the cache directory
|
||||||
|
let status = context::current()
|
||||||
|
.command("mmdebstrap")
|
||||||
|
.arg("--variant=buildd")
|
||||||
|
.arg("--mode=unshare")
|
||||||
|
.arg("--format=tar")
|
||||||
|
.arg(series)
|
||||||
|
.arg(tarball_path.to_string_lossy().to_string())
|
||||||
|
.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
return Err(format!("Failed to download chroot tarball for series {}", series).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_tarball(
|
||||||
|
tarball_path: &PathBuf,
|
||||||
|
chroot_path: &PathBuf,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
// Create the chroot directory
|
||||||
|
fs::create_dir_all(chroot_path)?;
|
||||||
|
|
||||||
|
// Open the tarball file
|
||||||
|
let tarball_file = std::fs::File::open(tarball_path)?;
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EphemeralContextGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
log::debug!("Cleaning up ephemeral context...");
|
||||||
|
// Reset to normal context
|
||||||
|
if let Err(e) = context::manager().set_current(&self.previous_context) {
|
||||||
|
log::error!("Failed to restore context {}: {}", self.previous_context, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove chroot directory
|
||||||
|
// We use the restored context to execute the cleanup command
|
||||||
|
let result = context::current()
|
||||||
|
.command("sudo")
|
||||||
|
.arg("rm")
|
||||||
|
.arg("-rf")
|
||||||
|
.arg(&self.chroot_path)
|
||||||
|
.status();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(status) => {
|
||||||
|
if !status.success() {
|
||||||
|
log::error!(
|
||||||
|
"Failed to remove chroot directory {}",
|
||||||
|
self.chroot_path.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to execute cleanup command for {}: {}",
|
||||||
|
self.chroot_path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
mod cross;
|
mod cross;
|
||||||
|
mod ephemeral;
|
||||||
mod local;
|
mod local;
|
||||||
mod sbuild;
|
mod sbuild;
|
||||||
|
|
||||||
@@ -42,21 +43,13 @@ pub fn build_binary_package(
|
|||||||
let mode = if let Some(m) = mode {
|
let mode = if let Some(m) = mode {
|
||||||
m
|
m
|
||||||
} else {
|
} else {
|
||||||
// For cross-compilation, we use local with an ephemeral context
|
// By default, we use local build
|
||||||
// created by the cross-compilation handler (see below)
|
BuildMode::Local
|
||||||
if cross {
|
|
||||||
BuildMode::Local
|
|
||||||
} else {
|
|
||||||
// By default, we use sbuild
|
|
||||||
BuildMode::Sbuild
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Specific case: native cross-compilation, we don't allow that
|
// Create an ephemeral unshare context for all Local builds
|
||||||
// instead this wraps to an automatic unshare chroot
|
let _guard = if mode == BuildMode::Local {
|
||||||
// using an ephemeral context
|
Some(ephemeral::EphemeralContextGuard::new(series)?)
|
||||||
let _guard = if cross && mode == BuildMode::Local {
|
|
||||||
Some(cross::EphemeralContextGuard::new(series)?)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user