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:
Gerald Pinder 2025-08-09 14:05:59 -04:00 committed by GitHub
parent 2c525854c9
commit 3a0be4099a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
65 changed files with 2991 additions and 1857 deletions

View file

@ -88,6 +88,7 @@ pub const OSTREE_IMAGE_SIGNED: &str = "ostree-image-signed";
pub const OSTREE_UNVERIFIED_IMAGE: &str = "ostree-unverified-image";
pub const SKOPEO_IMAGE: &str = "quay.io/skopeo/stable:latest";
pub const TEMPLATE_REPO_URL: &str = "https://github.com/blue-build/template.git";
pub const USER: &str = "USER";
pub const UNKNOWN_SHELL: &str = "<unknown shell>";
pub const UNKNOWN_VERSION: &str = "<unknown version>";
pub const UNKNOWN_TERMINAL: &str = "<unknown terminal>";
@ -110,3 +111,4 @@ pub const STAGE_SCHEMA: &str = concat!(JSON_SCHEMA, "/stage-v1.json");
// Messages
pub const BUG_REPORT_WARNING_MESSAGE: &str =
"Please copy the above report and open an issue manually.";
pub const SUDO_PROMPT: &str = "Bluebuild requires your password for sudo operation";

View file

@ -39,3 +39,155 @@ macro_rules! cowstr_vec {
}
};
}
#[macro_export]
macro_rules! impl_de_fromstr {
($($typ:ty),* $(,)?) => {
$(
impl TryFrom<&str> for $typ {
type Error = miette::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
value.parse()
}
}
impl TryFrom<&String> for $typ {
type Error = miette::Error;
fn try_from(value: &String) -> Result<Self, Self::Error> {
Self::try_from(value.as_str())
}
}
impl TryFrom<String> for $typ {
type Error = miette::Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_from(value.as_str())
}
}
impl<'de> serde::de::Deserialize<'de> for $typ {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Self::try_from(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
}
}
)*
};
}
#[macro_export]
macro_rules! sudo_cmd {
(
prompt = $prompt:expr,
sudo_check = $sudo_check:expr,
$command:expr,
$($rest:tt)*
) => {
{
let _use_sudo = ($sudo_check) && !$crate::running_as_root();
::comlexr::cmd!(
if _use_sudo {
"sudo"
} else {
$command
},
if _use_sudo && $crate::has_env_var($crate::constants::SUDO_ASKPASS) => [
"-A",
"-p",
$prompt,
],
if _use_sudo => [
"--preserve-env",
$command,
],
$($rest)*
)
}
};
(
sudo_check = $sudo_check:expr,
$command:expr,
$($rest:tt)*
) => {
{
let _use_sudo = ($sudo_check) && !$crate::running_as_root();
::comlexr::cmd!(
if _use_sudo {
"sudo"
} else {
$command
},
if _use_sudo && $crate::has_env_var($crate::constants::SUDO_ASKPASS) => [
"-A",
"-p",
$crate::constants::SUDO_PROMPT,
],
if _use_sudo => [
"--preserve-env",
$command,
],
$($rest)*
)
}
};
(
prompt = $prompt:expr,
$command:expr,
$($rest:tt)*
) => {
{
let _use_sudo = !$crate::running_as_root();
::comlexr::cmd!(
if _use_sudo {
"sudo"
} else {
$command
},
if _use_sudo && $crate::has_env_var($crate::constants::SUDO_ASKPASS) => [
"-A",
"-p",
$prompt,
],
if _use_sudo => [
"--preserve-env",
$command,
],
$($rest)*
)
}
};
(
$command:expr,
$($rest:tt)*
) => {
{
let _use_sudo = !$crate::running_as_root();
::comlexr::cmd!(
if _use_sudo {
"sudo"
} else {
$command
},
if _use_sudo && $crate::has_env_var($crate::constants::SUDO_ASKPASS) => [
"-A",
"-p",
$crate::constants::SUDO_PROMPT,
],
if _use_sudo => [
"--preserve-env",
$command,
],
$($rest)*
)
}
};
}

View file

@ -1,5 +1,4 @@
use std::{
collections::HashSet,
fs,
hash::{DefaultHasher, Hash, Hasher},
ops::Not,
@ -121,7 +120,7 @@ impl SecretMounts for Vec<Secret> {
}
}
impl<H: std::hash::BuildHasher> private::Private for HashSet<&Secret, H> {}
impl private::Private for &[&Secret] {}
#[allow(private_bounds)]
pub trait SecretArgs: private::Private {
@ -138,7 +137,7 @@ pub trait SecretArgs: private::Private {
fn ssh(&self) -> bool;
}
impl<H: std::hash::BuildHasher> SecretArgs for HashSet<&Secret, H> {
impl SecretArgs for &[&Secret] {
fn args(&self, temp_dir: &TempDir) -> Result<Vec<String>> {
Ok(self
.iter()
@ -173,7 +172,7 @@ impl<H: std::hash::BuildHasher> SecretArgs for HashSet<&Secret, H> {
}
fn ssh(&self) -> bool {
self.contains(&Secret::Ssh)
self.contains(&&Secret::Ssh)
}
}