Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,39 @@ Some example configuration files for Ultramarine are in `templates/`.
Example configuration file:

```toml
no_langpage = false

[distro]
name = "Ultramarine Linux"
icon = "fedora-logo-icon" # optional
bios_support = true # false by default

[install]
allowed_installtypes = ["chromebookinstall", "wholedisk", "dualboot", "custom"]
copy_mode = "repart" # or "bootc"
bootc_imgref = "optional string"
bootc_target_imgref = "optional string"

[[bento]] # 3 of them
title = "bento1" # translation id
desc = "bento1-desc" # translation id
link = "https://wiki.ultramarine-linux.org/en/welcome/"
icon = "explore-symbolic"

[[bento]]
title = "bento2"
desc = "bento2-desc"
link = "https://wiki.ultramarine-linux.org/en/community/community/"
icon = "chat-symbolic"

[[bento]]
title = "bento3"
desc = "bento3-desc"
link = "https://wiki.ultramarine-linux.org/en/contributing/contributorguide/"
icon = "applications-development-symbolic"
```

## Playbook

You can execute a headless installation by using the `readymade-playbook` crate.
See `test/test.sh` for more information.
2 changes: 1 addition & 1 deletion clippy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ disallowed-macros = ["cmd_lib::run_cmd", "cmd_lib::run_fun"]
# check-private-items = true
enforce-iter-loop-reborrow = true
excessive-nesting-threshold = 5
doc-valid-idents = ["..", "ChromeOS", "QoL"]
doc-valid-idents = ["..", "ChromeOS", "QoL", "SELinux"]
2 changes: 1 addition & 1 deletion crates/libreadymade/backend/provisioners/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ pub mod disk;
pub mod filesystem;

pub use disk::DiskProvisioner;
pub use filesystem::FileSystemProvisioner;
pub use filesystem::FileSystemProvisioner;
47 changes: 22 additions & 25 deletions crates/libreadymade/playbook.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Readymade playbook: configuration for your new installation.
//!
//! The Readymade playbook type describes the operation for Readymade to perform.
//! It is designed to be serializable and deserializable, so that it can be easily stored and transmitted,
//! but should be generated by an external program such as a GUI app or template system, rather than being manually written by users,
Expand Down Expand Up @@ -43,7 +45,7 @@ pub struct Playbook {
}

// TODO: handle luks lol
fn mounts_to_container(tempdir: &tempfile::TempDir, mounts: &Mounts) -> Result<Container> {
fn mounts_to_container(tempdir: &tempfile::TempDir, mounts: &Mounts) -> tiffin::Container {
let mut container = Container::new(tempdir.path().to_owned());

for mount in &mounts.0 {
Expand All @@ -52,11 +54,7 @@ fn mounts_to_container(tempdir: &tempfile::TempDir, mounts: &Mounts) -> Result<C
MountTarget {
target: mount.mountpoint.clone(),
flags: MountFlags::empty(),
data: if mount.options != "defaults" {
Some(mount.options.clone())
} else {
None
},
data: (mount.options != "defaults").then(|| mount.options.clone()),
fstype: None,
},
mount.partition.clone(),
Expand All @@ -77,7 +75,7 @@ fn mounts_to_container(tempdir: &tempfile::TempDir, mounts: &Mounts) -> Result<C

container.host_bind_mount();

Ok(container)
container
}

#[derive(Clone, Debug, Serialize, Deserialize)]
Expand All @@ -91,10 +89,11 @@ pub enum PlaybookProgress {
}

thread_local! {
pub(crate) static PROGRESS_SENDER: RefCell<Option<mpsc::Sender<PlaybookProgress>>> = RefCell::new(None);
pub(crate) static PROGRESS_SENDER: RefCell<Option<mpsc::Sender<PlaybookProgress>>> = const { RefCell::new(None) };
}

impl Playbook {
#[must_use]
pub fn channel() -> (
mpsc::Sender<PlaybookProgress>,
mpsc::Receiver<PlaybookProgress>,
Expand All @@ -105,22 +104,20 @@ impl Playbook {
/// Execute the playbook
pub fn play(&self, progress: mpsc::Sender<PlaybookProgress>) -> Result<()> {
PROGRESS_SENDER.with(|tx| *tx.borrow_mut() = Some(progress));
// todo: maybe use a guard here
let result = (|| {
let mounts = self.disk_provisioner.run(self)?;
if let Some(filesystem_provisioner) = &self.filesystem_provisioner {
filesystem_provisioner.run(self, &mounts)?;
}

self.setup_system(&mounts)?;

if let Some(filesystem_provisioner) = &self.filesystem_provisioner {
filesystem_provisioner.cleanup(self, &mounts)?;
}
Ok(())
})();
PROGRESS_SENDER.with(|tx| *tx.borrow_mut() = None);
result
scopeguard::defer! {
PROGRESS_SENDER.with(|tx| *tx.borrow_mut() = None);
}
let mounts = self.disk_provisioner.run(self)?;
if let Some(filesystem_provisioner) = &self.filesystem_provisioner {
filesystem_provisioner.run(self, &mounts)?;
}

self.setup_system(&mounts)?;

if let Some(filesystem_provisioner) = &self.filesystem_provisioner {
filesystem_provisioner.cleanup(self, &mounts)?;
}
Ok(())
}

#[tracing::instrument]
Expand All @@ -135,7 +132,7 @@ impl Playbook {
// the fstab generator to be correct, IF we're using encryption
//
// todo: Unfuck this
let mut container = mounts_to_container(&tempdir, mounts)?;
let mut container = mounts_to_container(&tempdir, mounts);
// let fstab = mounts.generate_fstab()?;
// tiffin will run `nix::unistd::chdir("/")` when entering the container, so we can use `sysroot as above`
container.run(|| self.inner_sys_setup(mounts))??;
Expand Down
2 changes: 1 addition & 1 deletion crates/libreadymade/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub use color_eyre::{Result, Section};
pub use itertools::Itertools;
pub use serde::{Deserialize, Serialize};

pub use crate::backend::mounts::{Mount, Mounts, EncryptionOption, CryptData};
pub use crate::backend::mounts::{CryptData, EncryptionOption, Mount, Mounts};
pub use std::path::Component;
pub use std::path::Path;
pub use std::path::PathBuf;
Expand Down
7 changes: 0 additions & 7 deletions src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ pub struct Install {
pub copy_mode: CopyMode,
pub bootc_imgref: Option<String>,
pub bootc_target_imgref: Option<String>,
#[serde(default)]
pub bootc_enforce_sigpolicy: bool,
pub bootc_kargs: Option<Vec<String>>,
pub bootc_args: Option<Vec<String>>,
}

#[derive(Deserialize, Serialize, Default, Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -197,9 +193,6 @@ mod tests {
copy_mode: CopyMode::Bootc,
bootc_imgref: None,
bootc_target_imgref: None,
bootc_enforce_sigpolicy: false,
bootc_kargs: None,
bootc_args: None,
},
postinstall: vec![
libreadymade::backend::postinstall::grub2::GRUB2.into(),
Expand Down
34 changes: 3 additions & 31 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use crate::{
use gtk::glib::translate::FromGlibPtrNone;
use i18n_embed::LanguageLoader as _;
use pages::installation::InstallationPageMsg;
use libreadymade::playbook::Playbook;
use relm4::SharedState;
use tracing_subscriber::{EnvFilter, fmt, prelude::*};

Expand Down Expand Up @@ -190,9 +189,9 @@ impl SimpleComponent for AppModel {
match message {
AppMsg::StartInstallation => {
let playbook = {
let custom_mounts =
(APPLICATION_STATE.read().installation_type == Some(cfg::InstallationType::Custom))
.then(|| self.install_custom_page.model().mounts());
let custom_mounts = (APPLICATION_STATE.read().installation_type
== Some(cfg::InstallationType::Custom))
.then(|| self.install_custom_page.model().mounts());
APPLICATION_STATE
.read()
.to_playbook(&CONFIG.read(), custom_mounts)
Expand Down Expand Up @@ -264,33 +263,6 @@ fn main() -> Result<()> {
let langs_th = std::thread::spawn(|| LazyLock::force(&AVAILABLE_LANGS));
let _guard = setup_hooks();

if let Some((i, _)) = std::env::args().find_position(|arg| arg == "--non-interactive") {
tracing::info!("Running in non-interactive mode");
let playbook: Playbook = serde_json::from_reader(std::io::stdin())?;
let progress = if let Some(channel_id) = std::env::args().nth(i.wrapping_add(1)) {
let sender = ipc_channel::ipc::IpcSender::connect(channel_id)?;
let (tx, rx) = Playbook::channel();
std::thread::spawn(move || {
for msg in rx {
if let Err(err) = sender.send(msg) {
tracing::error!("Failed to send progress to parent process: {err:?}");
break;
}
}
});
tx
} else {
let (tx, _rx) = Playbook::channel();
tx
};

*LL.write() = handle_l10n();
langs_th.join().expect("cannot join available_langs_th");
return playbook
.play(progress)
.inspect_err(|e| _ = sentry_eyre::capture_report(e));
}

*CONFIG.write() = cfg::get_cfg()?;
*APPLICATION_STATE.write() = ApplicationState::from(&*CONFIG.read());

Expand Down
40 changes: 32 additions & 8 deletions src/pages/installation.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::prelude::*;
use crate::{APPLICATION_STATE, NavigationAction, state};
use crate::{APPLICATION_STATE, NavigationAction};
use color_eyre::Result;
use l10n::BENTO_LOADER as L;
use libreadymade::playbook::{Playbook, PlaybookProgress};
Expand Down Expand Up @@ -289,16 +289,17 @@ impl Component for InstallationPage {
let sender2 = sender.clone();
let (s, r) = relm4::channel();
sender.oneshot_command(async move {
r.forward(sender2.input_sender().clone(), InstallationPageMsg::Progress)
.await;
r.forward(
sender2.input_sender().clone(),
InstallationPageMsg::Progress,
)
.await;
InstallationPageCommandMsg::None
});

sender.spawn_oneshot_command(move || {
tracing::debug!(?playbook, "Starting installation...");
InstallationPageCommandMsg::FinishInstallation(
state::install_using_subprocess(&playbook, move |msg| s.emit(msg)),
)
InstallationPageCommandMsg::FinishInstallation(install(&playbook, s))
});
}
InstallationPageMsg::Navigate(action) => sender
Expand All @@ -309,8 +310,12 @@ impl Component for InstallationPage {
self.progress_bar.set_text(Some(&status));
}
PlaybookProgress::PostModule(module, index, total) => {
self.progress_bar
.set_text(Some(&format!("Post-install {} / {}: {}", index + 1, total, module)));
self.progress_bar.set_text(Some(&format!(
"Post-install {} / {}: {}",
index + 1,
total,
module
)));
}
},
InstallationPageMsg::Update => {}
Expand Down Expand Up @@ -347,3 +352,22 @@ impl Component for InstallationPage {
}
}
}

pub fn install(playbook: &Playbook, sender: relm4::Sender<PlaybookProgress>) -> Result<()> {
let (tx, rx) = Playbook::channel();

let th = std::thread::spawn(move || {
for msg in rx {
sender.emit(msg);
}
});

let res = playbook
.play(tx)
.inspect_err(|e| _ = sentry_eyre::capture_report(e));

th.join()
.expect("cannot join PlaybookProgress propagation thread");

res
}
Loading
Loading