deb: download keyring in cache directory for mmdebstrap
Some checks failed
CI / build (push) Failing after 13m33s
CI / snap (push) Has been skipped

also avoid sudo if we are root
This commit is contained in:
2026-02-19 11:30:33 +01:00
parent 87b48bf9c8
commit b508dd3d82
2 changed files with 65 additions and 36 deletions

View File

@@ -7,7 +7,7 @@ use crate::context;
use crate::distro_info; use crate::distro_info;
use serde::Deserialize; use serde::Deserialize;
use std::error::Error; use std::error::Error;
use std::path::Path; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
/// Launchpad API response structure for PPA information /// Launchpad API response structure for PPA information
@@ -16,21 +16,36 @@ struct LaunchpadPpaResponse {
signing_key_fingerprint: String, signing_key_fingerprint: String,
} }
/// Download a keyring into apt trusted.gpg.d directory, trusting that keyring /// Download a keyring to the application cache directory and return the path
pub async fn download_trust_keyring( ///
/// This function downloads the keyring to a user-writable cache directory
/// instead of the system apt keyring directory, allowing non-root usage.
/// The returned path can be passed to mmdebstrap via --keyring.
///
/// # Arguments
/// * `ctx` - Optional context to use
/// * `series` - The distribution series (e.g., "noble", "sid")
///
/// # Returns
/// The path to the downloaded keyring file
pub async fn download_cache_keyring(
ctx: Option<Arc<context::Context>>, ctx: Option<Arc<context::Context>>,
series: &str, series: &str,
) -> Result<(), Box<dyn Error>> { ) -> Result<PathBuf, Box<dyn Error>> {
let ctx = ctx.unwrap_or_else(context::current); let ctx = ctx.unwrap_or_else(context::current);
// Obtain keyring URL from distro_info // Obtain keyring URL from distro_info
let keyring_url = distro_info::get_keyring_url(series).await?; let keyring_url = distro_info::get_keyring_url(series).await?;
log::debug!("Downloading keyring from: {}", keyring_url); log::debug!("Downloading keyring from: {}", keyring_url);
// Create trusted.gpg.d directory if it doesn't exist // Get the application cache directory
let trusted_gpg_d = "/etc/apt/trusted.gpg.d"; let proj_dirs = directories::ProjectDirs::from("com", "pkh", "pkh")
if !ctx.exists(Path::new(trusted_gpg_d))? { .ok_or("Could not determine project directories")?;
ctx.command("mkdir").arg("-p").arg(trusted_gpg_d).status()?; let cache_dir = proj_dirs.cache_dir();
// Create cache directory if it doesn't exist
if !ctx.exists(cache_dir)? {
ctx.command("mkdir").arg("-p").arg(cache_dir).status()?;
} }
// Extract the original filename from the keyring URL // Extract the original filename from the keyring URL
@@ -39,9 +54,9 @@ pub async fn download_trust_keyring(
.next_back() .next_back()
.unwrap_or("pkh-{}.gpg") .unwrap_or("pkh-{}.gpg")
.replace("{}", series); .replace("{}", series);
let keyring_path = format!("{}/{}", trusted_gpg_d, filename); let keyring_path = cache_dir.join(&filename);
// Download the keyring directly to the final location using curl // Download the keyring using curl
let mut curl_cmd = ctx.command("curl"); let mut curl_cmd = ctx.command("curl");
curl_cmd curl_cmd
.arg("-s") .arg("-s")
@@ -57,11 +72,11 @@ pub async fn download_trust_keyring(
} }
log::info!( log::info!(
"Successfully downloaded and installed keyring for {} to {}", "Successfully downloaded keyring for {} to {}",
series, series,
keyring_path keyring_path.display()
); );
Ok(()) Ok(keyring_path)
} }
/// Download and import a PPA key using Launchpad API /// Download and import a PPA key using Launchpad API

View File

@@ -143,22 +143,17 @@ impl EphemeralContextGuard {
.arg(lockfile_path.to_string_lossy().to_string()) .arg(lockfile_path.to_string_lossy().to_string())
.status()?; .status()?;
// Make sure we have the right apt keyrings to mmdebstrap the chroot // Download the keyring to the cache directory
// Check for root privileges before downloading keyring let keyring_path =
if crate::utils::root::is_root()? { crate::apt::keyring::download_cache_keyring(Some(ctx.clone()), series).await?;
crate::apt::keyring::download_trust_keyring(Some(ctx.clone()), series).await?;
} else {
log::info!(
"Lacking root privileges. Please ensure that the keyrings for the target distribution are present on your system."
);
}
// Use mmdebstrap to download the tarball to the cache directory // Use mmdebstrap to download the tarball to the cache directory
let mut cmd = ctx.command("mmdebstrap"); let mut cmd = ctx.command("mmdebstrap");
cmd.arg("--variant=buildd") cmd.arg("--variant=buildd")
.arg("--mode=unshare") .arg("--mode=unshare")
.arg("--include=mount,curl,ca-certificates") .arg("--include=mount,curl,ca-certificates")
.arg("--format=tar"); .arg("--format=tar")
.arg(format!("--keyring={}", keyring_path.display()));
// Add architecture if specified // Add architecture if specified
if let Some(a) = arch { if let Some(a) = arch {
@@ -238,10 +233,15 @@ impl EphemeralContextGuard {
.arg(dev_zero_path.to_string_lossy().to_string()) .arg(dev_zero_path.to_string_lossy().to_string())
.status(); .status();
// Create new device nodes using fakeroot and mknod // Check if we're running as root
let status_null = ctx let is_root = crate::utils::root::is_root()?;
.command("sudo")
.arg("mknod") // Create new device nodes using mknod (with sudo if not root)
let mut cmd_null = ctx.command(if is_root { "mknod" } else { "sudo" });
if !is_root {
cmd_null.arg("mknod");
}
let status_null = cmd_null
.arg("-m") .arg("-m")
.arg("666") .arg("666")
.arg(dev_null_path.to_string_lossy().to_string()) .arg(dev_null_path.to_string_lossy().to_string())
@@ -250,9 +250,11 @@ impl EphemeralContextGuard {
.arg("3") .arg("3")
.status()?; .status()?;
let status_zero = ctx let mut cmd_zero = ctx.command(if is_root { "mknod" } else { "sudo" });
.command("sudo") if !is_root {
.arg("mknod") cmd_zero.arg("mknod");
}
let status_zero = cmd_zero
.arg("-m") .arg("-m")
.arg("666") .arg("666")
.arg(dev_zero_path.to_string_lossy().to_string()) .arg(dev_zero_path.to_string_lossy().to_string())
@@ -288,12 +290,24 @@ impl Drop for EphemeralContextGuard {
"Build succeeded, removing chroot directory: {}", "Build succeeded, removing chroot directory: {}",
self.chroot_path.display() self.chroot_path.display()
); );
let result = context::current()
// Check if we're running as root to avoid unnecessary sudo
let is_root = crate::utils::root::is_root().unwrap_or(false);
let result = if is_root {
context::current()
.command("rm")
.arg("-rf")
.arg(&self.chroot_path)
.status()
} else {
context::current()
.command("sudo") .command("sudo")
.arg("rm") .arg("rm")
.arg("-rf") .arg("-rf")
.arg(&self.chroot_path) .arg(&self.chroot_path)
.status(); .status()
};
match result { match result {
Ok(status) => { Ok(status) => {