Add Steam Deck Support (#34)
* Init Steam Deck support * Improve systemd units * Handle stopping nix-daemon.service before stopping mount * Better handle being in a sysext * Add some install directions * Add a KDE autostart script * Tidy up uninstall * Use stop/disable instead of disable --now * Fixup a double-disable * Repair some defaults * Tidy up services * Make ci test steam deck planner * Delete bonus line * Use newer image * Get steam-deck working hopefully * Create steamos-readonly mock * Make stub of steamos-readonly * Use sudo for chmod * Attempt CI fix * Don't add deck group * A more clever method * We have a new method and the CI can be cleaned a bit * Brazenly disable sandbox on the deck ci * Extra-conf takes vec * Dump lsblk mount * An even more clever method * More debugging symbols * More debugging symbols * Even more debugging * probe for issues * Get specific with permissions and ownership (for ci) * We are now way overboard on debugging symbols * Specify permissions on created home stub * Allow specifying persistence * Cleanup debugging bits * Fixup persistence path * Work out some better linking in units * units don't need executable * Tidy * Delint * Remove a note from readme * Github actions seems to have build the wrong checkout? * Doctest repair * Don't create directory twice * Restore missing doc comments Co-authored-by: Cole Helbling <cole.e.helbling@outlook.com>
This commit is contained in:
parent
0e2f27713f
commit
ae37842a0d
51
.github/workflows/ci.yml
vendored
51
.github/workflows/ci.yml
vendored
|
@ -70,15 +70,52 @@ jobs:
|
||||||
name: harmonic-x86_64-linux
|
name: harmonic-x86_64-linux
|
||||||
- name: Set executable
|
- name: Set executable
|
||||||
run: chmod +x ./harmonic
|
run: chmod +x ./harmonic
|
||||||
|
|
||||||
- name: Initial install
|
- name: Initial install
|
||||||
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
||||||
- name: Test run
|
- name: Initial uninstall (without a `nix run` first)
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
|
||||||
|
- name: Repeated install
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
||||||
|
- name: Test `nix`
|
||||||
run: |
|
run: |
|
||||||
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||||
nix run nixpkgs#fortune
|
nix run nixpkgs#fortune
|
||||||
- name: Initial uninstall
|
- name: Repeated uninstall
|
||||||
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
|
||||||
|
run-steam-deck:
|
||||||
|
name: Run Steam Deck (mock)
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
needs: build-x86_64-linux
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: harmonic-x86_64-linux
|
||||||
|
- name: Set executable
|
||||||
|
run: chmod +x ./harmonic
|
||||||
|
- name: Make the CI look like a steam deck
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/bin
|
||||||
|
echo -e "#! /bin/sh\nexit 0" | sudo tee -a /bin/steamos-readonly
|
||||||
|
sudo chmod +x /bin/steamos-readonly
|
||||||
|
sudo useradd -m deck
|
||||||
|
|
||||||
|
- name: Initial install
|
||||||
|
run: sudo PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install steam-deck --persistence `pwd`/ci-test-nix --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
||||||
|
- name: Initial uninstall (without a `nix run` first)
|
||||||
|
run: sudo PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
|
||||||
|
- name: Repeated install
|
||||||
|
run: sudo PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install steam-deck --persistence `pwd`/ci-test-nix --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
||||||
|
- name: Test `nix`
|
||||||
|
run: |
|
||||||
|
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||||
|
nix run nixpkgs#fortune
|
||||||
|
- name: Repeated uninstall
|
||||||
|
run: sudo PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
|
||||||
build-x86_64-darwin:
|
build-x86_64-darwin:
|
||||||
name: Build x86_64 Darwin
|
name: Build x86_64 Darwin
|
||||||
runs-on: macos-12
|
runs-on: macos-12
|
||||||
|
@ -108,17 +145,15 @@ jobs:
|
||||||
name: harmonic-x86_64-darwin
|
name: harmonic-x86_64-darwin
|
||||||
- name: Set executable
|
- name: Set executable
|
||||||
run: chmod +x ./harmonic
|
run: chmod +x ./harmonic
|
||||||
|
|
||||||
- name: Initial install
|
- name: Initial install
|
||||||
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
||||||
- name: Test run
|
- name: Initial uninstall (without a `nix run` first)
|
||||||
run: |
|
|
||||||
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
|
||||||
nix run nixpkgs#fortune
|
|
||||||
- name: Initial uninstall
|
|
||||||
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
|
||||||
- name: Repeated install
|
- name: Repeated install
|
||||||
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
|
||||||
- name: Repeated test run
|
- name: Test `nix`
|
||||||
run: |
|
run: |
|
||||||
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||||
nix run nixpkgs#fortune
|
nix run nixpkgs#fortune
|
||||||
|
|
|
@ -16,6 +16,7 @@ Planned support:
|
||||||
+ Note: User deletion is currently unimplemented, you need to use a user with a secure token and `dscl . -delete /Users/_nixbuild*` where `*` is each user number.
|
+ Note: User deletion is currently unimplemented, you need to use a user with a secure token and `dscl . -delete /Users/_nixbuild*` where `*` is each user number.
|
||||||
* [x] Multi-user aarch64 MacOS
|
* [x] Multi-user aarch64 MacOS
|
||||||
+ Note: User deletion is currently unimplemented, you need to use a user with a secure token and `dscl . -delete /Users/_nixbuild*` where `*` is each user number.
|
+ Note: User deletion is currently unimplemented, you need to use a user with a secure token and `dscl . -delete /Users/_nixbuild*` where `*` is each user number.
|
||||||
|
* [x] Valve Steam Deck
|
||||||
* [ ] Single-user x86_64 Linux with systemd init
|
* [ ] Single-user x86_64 Linux with systemd init
|
||||||
* [ ] Single-user aarch64 Linux with systemd init
|
* [ ] Single-user aarch64 Linux with systemd init
|
||||||
* [ ] Others...
|
* [ ] Others...
|
||||||
|
|
|
@ -17,7 +17,7 @@ impl PlaceNixConfiguration {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
nix_build_group_name: String,
|
nix_build_group_name: String,
|
||||||
extra_conf: Option<String>,
|
extra_conf: Vec<String>,
|
||||||
force: bool,
|
force: bool,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let buf = format!(
|
let buf = format!(
|
||||||
|
@ -30,7 +30,7 @@ impl PlaceNixConfiguration {
|
||||||
\n\
|
\n\
|
||||||
auto-optimise-store = true\n\
|
auto-optimise-store = true\n\
|
||||||
",
|
",
|
||||||
extra_conf = extra_conf.unwrap_or_else(|| "".into()),
|
extra_conf = extra_conf.join("\n"),
|
||||||
);
|
);
|
||||||
let create_directory =
|
let create_directory =
|
||||||
CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force).await?;
|
CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force).await?;
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
|
|
||||||
const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
|
const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
|
||||||
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
|
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
|
||||||
const TMPFILES_SRC: &str = "/nix/var/nix/profiles/default//lib/tmpfiles.d/nix-daemon.conf";
|
const TMPFILES_SRC: &str = "/nix/var/nix/profiles/default/lib/tmpfiles.d/nix-daemon.conf";
|
||||||
const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf";
|
const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf";
|
||||||
const DARWIN_NIX_DAEMON_DEST: &str = "/Library/LaunchDaemons/org.nixos.nix-daemon.plist";
|
const DARWIN_NIX_DAEMON_DEST: &str = "/Library/LaunchDaemons/org.nixos.nix-daemon.plist";
|
||||||
|
|
||||||
|
@ -153,8 +153,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
.arg("enable")
|
.arg("enable")
|
||||||
.arg("--now")
|
.arg("--now")
|
||||||
.arg("nix-daemon.socket")
|
.arg("nix-daemon.socket"),
|
||||||
.stdin(std::process::Stdio::null()),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||||
|
@ -284,6 +283,8 @@ pub enum ConfigureNixDaemonServiceError {
|
||||||
std::path::PathBuf,
|
std::path::PathBuf,
|
||||||
#[source] std::io::Error,
|
#[source] std::io::Error,
|
||||||
),
|
),
|
||||||
|
#[error("Set mode `{0}` on `{1}`")]
|
||||||
|
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
||||||
#[error("Command failed to execute")]
|
#[error("Command failed to execute")]
|
||||||
Command(#[source] std::io::Error),
|
Command(#[source] std::io::Error),
|
||||||
#[error("Remove file `{0}`")]
|
#[error("Remove file `{0}`")]
|
||||||
|
|
|
@ -1,175 +0,0 @@
|
||||||
use crate::action::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
|
|
||||||
use crate::action::StatefulAction;
|
|
||||||
use crate::{
|
|
||||||
action::{Action, ActionDescription},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
const PATHS: &[&str] = &[
|
|
||||||
"usr",
|
|
||||||
"usr/lib",
|
|
||||||
"usr/lib/extension-release.d",
|
|
||||||
"usr/lib/systemd",
|
|
||||||
"usr/lib/systemd/system",
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
|
||||||
pub struct CreateSystemdSysext {
|
|
||||||
destination: PathBuf,
|
|
||||||
create_directories: Vec<StatefulAction<CreateDirectory>>,
|
|
||||||
create_extension_release: StatefulAction<CreateFile>,
|
|
||||||
create_bind_mount_unit: StatefulAction<CreateFile>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateSystemdSysext {
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub async fn plan(
|
|
||||||
destination: impl AsRef<Path>,
|
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let destination = destination.as_ref();
|
|
||||||
|
|
||||||
let mut create_directories =
|
|
||||||
vec![CreateDirectory::plan(destination, None, None, 0o0755, true).await?];
|
|
||||||
for path in PATHS {
|
|
||||||
create_directories.push(
|
|
||||||
CreateDirectory::plan(destination.join(path), None, None, 0o0755, false).await?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let version_id = tokio::fs::read_to_string("/etc/os-release")
|
|
||||||
.await
|
|
||||||
.map(|buf| {
|
|
||||||
buf.lines()
|
|
||||||
.find_map(|line| match line.starts_with("VERSION_ID") {
|
|
||||||
true => line.rsplit("=").next().map(|inner| inner.to_owned()),
|
|
||||||
false => None,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|e| CreateSystemdSysextError::ReadingOsRelease(e).boxed())?
|
|
||||||
.ok_or_else(|| CreateSystemdSysextError::NoVersionId.boxed())?;
|
|
||||||
let extension_release_buf =
|
|
||||||
format!("SYSEXT_LEVEL=1.0\nID=steamos\nVERSION_ID={version_id}");
|
|
||||||
let create_extension_release = CreateFile::plan(
|
|
||||||
destination.join("usr/lib/extension-release.d/extension-release.nix"),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
0o0755,
|
|
||||||
extension_release_buf,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let create_bind_mount_buf = format!(
|
|
||||||
"
|
|
||||||
[Mount]\n\
|
|
||||||
What={}\n\
|
|
||||||
Where=/nix\n\
|
|
||||||
Type=none\n\
|
|
||||||
Options=bind\n\
|
|
||||||
",
|
|
||||||
destination.display(),
|
|
||||||
);
|
|
||||||
let create_bind_mount_unit = CreateFile::plan(
|
|
||||||
destination.join("usr/lib/systemd/system/nix.mount"),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
0o0755,
|
|
||||||
create_bind_mount_buf,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
destination: destination.to_path_buf(),
|
|
||||||
create_directories,
|
|
||||||
create_extension_release,
|
|
||||||
create_bind_mount_unit,
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
#[typetag::serde(name = "create_systemd_sysext")]
|
|
||||||
impl Action for CreateSystemdSysext {
|
|
||||||
fn tracing_synopsis(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"Create a systemd sysext at `{}`",
|
|
||||||
self.destination.display()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_description(&self) -> Vec<ActionDescription> {
|
|
||||||
vec![ActionDescription::new(
|
|
||||||
self.tracing_synopsis(),
|
|
||||||
vec![format!(
|
|
||||||
"Create a writable, persistent systemd system extension.",
|
|
||||||
)],
|
|
||||||
)]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(destination,))]
|
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let Self {
|
|
||||||
destination: _,
|
|
||||||
create_directories,
|
|
||||||
create_extension_release,
|
|
||||||
create_bind_mount_unit,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
for create_directory in create_directories {
|
|
||||||
create_directory.try_execute().await?;
|
|
||||||
}
|
|
||||||
create_extension_release.try_execute().await?;
|
|
||||||
create_bind_mount_unit.try_execute().await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn revert_description(&self) -> Vec<ActionDescription> {
|
|
||||||
vec![ActionDescription::new(
|
|
||||||
format!(
|
|
||||||
"Remove the sysext located at `{}`",
|
|
||||||
self.destination.display()
|
|
||||||
),
|
|
||||||
vec![],
|
|
||||||
)]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(destination,))]
|
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let Self {
|
|
||||||
destination: _,
|
|
||||||
create_directories,
|
|
||||||
create_extension_release,
|
|
||||||
create_bind_mount_unit,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
create_bind_mount_unit.try_revert().await?;
|
|
||||||
|
|
||||||
create_extension_release.try_revert().await?;
|
|
||||||
|
|
||||||
for create_directory in create_directories.iter_mut().rev() {
|
|
||||||
create_directory.try_revert().await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateSystemdSysextError {
|
|
||||||
#[error(transparent)]
|
|
||||||
CreateDirectory(#[from] CreateDirectoryError),
|
|
||||||
#[error(transparent)]
|
|
||||||
CreateFile(#[from] CreateFileError),
|
|
||||||
#[error("Reading /etc/os-release")]
|
|
||||||
ReadingOsRelease(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
std::io::Error,
|
|
||||||
),
|
|
||||||
#[error("No `VERSION_ID` line in /etc/os-release")]
|
|
||||||
NoVersionId,
|
|
||||||
}
|
|
|
@ -1,9 +1,5 @@
|
||||||
//! [`Action`](crate::action::Action)s for Linux based systems
|
|
||||||
|
|
||||||
mod configure_nix_daemon_service;
|
mod configure_nix_daemon_service;
|
||||||
mod create_systemd_sysext;
|
|
||||||
mod start_systemd_unit;
|
mod start_systemd_unit;
|
||||||
|
|
||||||
pub use configure_nix_daemon_service::{ConfigureNixDaemonService, ConfigureNixDaemonServiceError};
|
pub use configure_nix_daemon_service::{ConfigureNixDaemonService, ConfigureNixDaemonServiceError};
|
||||||
pub use create_systemd_sysext::{CreateSystemdSysext, CreateSystemdSysextError};
|
|
||||||
pub use start_systemd_unit::{StartSystemdUnit, StartSystemdUnitError};
|
pub use start_systemd_unit::{StartSystemdUnit, StartSystemdUnitError};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::action::StatefulAction;
|
use crate::action::{ActionState, StatefulAction};
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -19,9 +19,14 @@ pub struct StartSystemdUnit {
|
||||||
impl StartSystemdUnit {
|
impl StartSystemdUnit {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
unit: String,
|
unit: impl AsRef<str>,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
Ok(Self { unit }.into())
|
Ok(StatefulAction {
|
||||||
|
action: Self {
|
||||||
|
unit: unit.as_ref().to_string(),
|
||||||
|
},
|
||||||
|
state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
use crate::execute_command;
|
|
||||||
use crate::{
|
|
||||||
action::{Action, ActionDescription, ActionState},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use tokio::process::Command;
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
|
||||||
pub struct SystemdSysextMerge {
|
|
||||||
device: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SystemdSysextMerge {
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub async fn plan(device: PathBuf) -> Result<Self, SystemdSysextMergeError> {
|
|
||||||
Ok(Self { device })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
#[typetag::serde(name = "systemd_sysext_merge")]
|
|
||||||
impl Action for SystemdSysextMerge {
|
|
||||||
fn tracing_synopsis(&self) -> String {
|
|
||||||
format!("Run `systemd-sysext merge `{}`", device.display())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn describe_execute(&self) -> Vec<ActionDescription> {
|
|
||||||
vec![ActionDescription::new(self.tracing_synopsis(), vec![])]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(
|
|
||||||
device = %self.device.display(),
|
|
||||||
))]
|
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let Self {
|
|
||||||
device,
|
|
||||||
action_state,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
execute_command(
|
|
||||||
Command::new("systemd-sysext")
|
|
||||||
.process_group(0)
|
|
||||||
.arg("merge")
|
|
||||||
.arg(device)
|
|
||||||
.stdin(std::process::Stdio::null()),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| SystemdSysextMergeError::Command(e).boxed())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn describe_revert(&self) -> Vec<ActionDescription> {
|
|
||||||
vec![ActionDescription::new(
|
|
||||||
"Stop the systemd Nix service and socket".to_string(),
|
|
||||||
vec![
|
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(
|
|
||||||
device = %self.device.display(),
|
|
||||||
))]
|
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let Self { device } = self;
|
|
||||||
|
|
||||||
// TODO(@Hoverbear): Handle proxy vars
|
|
||||||
execute_command(
|
|
||||||
Command::new("systemd-sysext")
|
|
||||||
.process_group(0)
|
|
||||||
.arg("unmerge")
|
|
||||||
.arg(device)
|
|
||||||
.stdin(std::process::Stdio::null()),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| SystemdSysextMergeError::Command(e).boxed())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum SystemdSysextMergeError {
|
|
||||||
#[error("Failed to execute command")]
|
|
||||||
Command(#[source] std::io::Error),
|
|
||||||
}
|
|
|
@ -49,7 +49,7 @@ use std::{error::Error, collections::HashMap};
|
||||||
use harmonic::{
|
use harmonic::{
|
||||||
InstallPlan,
|
InstallPlan,
|
||||||
settings::{CommonSettings, InstallSettingsError},
|
settings::{CommonSettings, InstallSettingsError},
|
||||||
planner::{Planner, PlannerError, specific::SteamDeck},
|
planner::{Planner, PlannerError, linux::SteamDeck},
|
||||||
action::{Action, StatefulAction, ActionDescription},
|
action::{Action, StatefulAction, ActionDescription},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ Sometimes choosing a specific plan is desired:
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use harmonic::{InstallPlan, planner::{Planner, specific::SteamDeck}};
|
use harmonic::{InstallPlan, planner::{Planner, linux::SteamDeck}};
|
||||||
|
|
||||||
# async fn chosen_planner_install() -> color_eyre::Result<()> {
|
# async fn chosen_planner_install() -> color_eyre::Result<()> {
|
||||||
let planner = SteamDeck::default().await?;
|
let planner = SteamDeck::default().await?;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Planners for Linux based systems
|
//! Planners for Linux based systems
|
||||||
|
|
||||||
mod multi;
|
mod multi;
|
||||||
|
mod steam_deck;
|
||||||
|
|
||||||
pub use multi::LinuxMulti;
|
pub use multi::LinuxMulti;
|
||||||
|
pub use steam_deck::SteamDeck;
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::{
|
||||||
action::{
|
action::{
|
||||||
base::CreateDirectory,
|
base::CreateDirectory,
|
||||||
common::{ConfigureNix, ProvisionNix},
|
common::{ConfigureNix, ProvisionNix},
|
||||||
|
linux::StartSystemdUnit,
|
||||||
StatefulAction,
|
StatefulAction,
|
||||||
},
|
},
|
||||||
planner::{Planner, PlannerError},
|
planner::{Planner, PlannerError},
|
||||||
|
@ -58,6 +59,10 @@ impl Planner for LinuxMulti {
|
||||||
.await
|
.await
|
||||||
.map_err(PlannerError::Action)?
|
.map_err(PlannerError::Action)?
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
StartSystemdUnit::plan("nix-daemon.socket".to_string())
|
||||||
|
.await
|
||||||
|
.map_err(|v| PlannerError::Action(v.into()))?
|
||||||
|
.boxed(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
246
src/planner/linux/steam_deck.rs
Normal file
246
src/planner/linux/steam_deck.rs
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
/** Testing the Steam Deck Install (Summary of https://blogs.igalia.com/berto/2022/07/05/running-the-steam-decks-os-in-a-virtual-machine-using-qemu/)
|
||||||
|
|
||||||
|
One time step:
|
||||||
|
|
||||||
|
1. Grab the SteamOS: Steam Deck Image from https://store.steampowered.com/steamos/download/?ver=steamdeck&snr=
|
||||||
|
2. Extract it (this can take a bit)
|
||||||
|
```sh
|
||||||
|
bunzip2 steamdeck-recovery-4.img.bz2
|
||||||
|
```
|
||||||
|
2. Create a disk image
|
||||||
|
```sh
|
||||||
|
qemu-img create -f qcow2 steamos.qcow2 64G
|
||||||
|
```
|
||||||
|
3. Start a VM to run the install onto the created disk
|
||||||
|
|
||||||
|
*Note:*
|
||||||
|
```sh
|
||||||
|
RECOVERY_IMAGE=steamdeck-recovery-4.img
|
||||||
|
nix build "nixpkgs#legacyPackages.x86_64-linux.OVMF.fd" --out-link ovmf
|
||||||
|
qemu-system-x86_64 -enable-kvm -smp cores=4 -m 8G \
|
||||||
|
-device usb-ehci -device usb-tablet \
|
||||||
|
-device intel-hda -device hda-duplex \
|
||||||
|
-device VGA,xres=1280,yres=800 \
|
||||||
|
-drive if=pflash,format=raw,readonly=on,file=ovmf-fd/FV/OVMF.fd \
|
||||||
|
-drive if=virtio,file=$RECOVERY_IMAGE,driver=raw \
|
||||||
|
-device nvme,drive=drive0,serial=badbeef \
|
||||||
|
-drive if=none,id=drive0,file=steamos.qcow2
|
||||||
|
```
|
||||||
|
4. Pick "Reimage Steam Deck". **Important:** when it is done do not reboot the steam deck, hit "Cancel"
|
||||||
|
5. Run `sudo steamos-chroot --disk /dev/nvme0n1 --partset A` and inside run this
|
||||||
|
```sh
|
||||||
|
steamos-readonly disable
|
||||||
|
echo -e '[Autologin]\nSession=plasma.desktop' > /etc/sddm.conf.d/zz-steamos-autologin.conf
|
||||||
|
passwd deck
|
||||||
|
sudo systemctl enable sshd
|
||||||
|
steamos-readonly enable
|
||||||
|
exit
|
||||||
|
```
|
||||||
|
6. Run `sudo steamos-chroot --disk /dev/nvme0n1 --partset B` and inside run the same above commands
|
||||||
|
7. Safely turn off the VM!
|
||||||
|
|
||||||
|
Repeated step:
|
||||||
|
1. Create a snapshot of the base install to work on
|
||||||
|
```sh
|
||||||
|
cp steamos.qcow2 steamos-hack.qcow2
|
||||||
|
2. Run the VM
|
||||||
|
```sh
|
||||||
|
nix build "nixpkgs#legacyPackages.x86_64-linux.OVMF.fd" --out-link ovmf
|
||||||
|
qemu-system-x86_64 -enable-kvm -smp cores=4 -m 8G \
|
||||||
|
-device usb-ehci -device usb-tablet \
|
||||||
|
-device intel-hda -device hda-duplex \
|
||||||
|
-device VGA,xres=1280,yres=800 \
|
||||||
|
-drive if=pflash,format=raw,readonly=on,file=ovmf-fd/FV/OVMF_CODE.fd \
|
||||||
|
-drive if=pflash,format=raw,readonly=on,file=ovmf-fd/FV/OVMF_VARS.fd \
|
||||||
|
-drive if=virtio,file=steamos-hack.qcow2 \
|
||||||
|
-device virtio-net-pci,netdev=net0 \
|
||||||
|
-netdev user,id=net0,hostfwd=tcp::2222-:22
|
||||||
|
```
|
||||||
|
3. **Do your testing!** You can `ssh deck@localhost -p 2222` in and use `rsync -e 'ssh -p 2222' result/bin/harmonic deck@localhost:harmonic` to send a harmonic build.
|
||||||
|
4. Delete `steamos-hack.qcow2`
|
||||||
|
*/
|
||||||
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
action::{
|
||||||
|
base::{CreateDirectory, CreateFile},
|
||||||
|
common::{ConfigureNix, ProvisionNix},
|
||||||
|
linux::StartSystemdUnit,
|
||||||
|
Action, StatefulAction,
|
||||||
|
},
|
||||||
|
planner::{Planner, PlannerError},
|
||||||
|
settings::{CommonSettings, InstallSettingsError},
|
||||||
|
BuiltinPlanner,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, clap::Parser, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct SteamDeck {
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
env = "HARMONIC_STEAM_DECK_PERSISTENCE",
|
||||||
|
default_value = "/home/nix"
|
||||||
|
)]
|
||||||
|
persistence: PathBuf,
|
||||||
|
#[clap(flatten)]
|
||||||
|
pub settings: CommonSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
#[typetag::serde(name = "steam-deck")]
|
||||||
|
impl Planner for SteamDeck {
|
||||||
|
async fn default() -> Result<Self, PlannerError> {
|
||||||
|
Ok(Self {
|
||||||
|
persistence: PathBuf::from("/home/nix"),
|
||||||
|
settings: CommonSettings::default()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn plan(&self) -> Result<Vec<StatefulAction<Box<dyn Action>>>, PlannerError> {
|
||||||
|
let persistence = &self.persistence;
|
||||||
|
|
||||||
|
let nix_directory_buf = format!(
|
||||||
|
"
|
||||||
|
[Unit]\n\
|
||||||
|
Description=Create a `/nix` directory to be used for bind mounting\n\
|
||||||
|
PropagatesStopTo=nix-daemon.service\n\
|
||||||
|
PropagatesStopTo=nix.mount\n\
|
||||||
|
DefaultDependencies=no\n\
|
||||||
|
\n\
|
||||||
|
[Service]\n\
|
||||||
|
Type=oneshot\n\
|
||||||
|
ExecCondition=sh -c \"if [ -d /nix ]; then exit 1; else exit 0; fi\"
|
||||||
|
ExecStart=steamos-readonly disable\n\
|
||||||
|
ExecStart=mkdir -vp /nix\n\
|
||||||
|
ExecStart=chmod -v 0755 /nix\n\
|
||||||
|
ExecStart=chown -v root /nix\n\
|
||||||
|
ExecStart=chgrp -v root /nix\n\
|
||||||
|
ExecStart=steamos-readonly enable\n\
|
||||||
|
ExecStop=steamos-readonly disable\n\
|
||||||
|
ExecStop=rmdir /nix\n\
|
||||||
|
ExecStop=steamos-readonly enable\n\
|
||||||
|
RemainAfterExit=true\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
let nix_directory_unit = CreateFile::plan(
|
||||||
|
"/etc/systemd/system/nix-directory.service",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
0o0644,
|
||||||
|
nix_directory_buf,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?;
|
||||||
|
|
||||||
|
let create_bind_mount_buf = format!(
|
||||||
|
"\
|
||||||
|
[Unit]\n\
|
||||||
|
Description=Mount `{persistence}` on `/nix`\n\
|
||||||
|
PropagatesStopTo=nix-daemon.service\n\
|
||||||
|
PropagatesStopTo=nix-directory.service\n\
|
||||||
|
After=nix-directory.service\n\
|
||||||
|
Requires=nix-directory.service\n\
|
||||||
|
ConditionPathIsDirectory=/nix\n\
|
||||||
|
DefaultDependencies=no\n\
|
||||||
|
\n\
|
||||||
|
[Mount]\n\
|
||||||
|
What={persistence}\n\
|
||||||
|
Where=/nix\n\
|
||||||
|
Type=none\n\
|
||||||
|
DirectoryMode=0755\n\
|
||||||
|
Options=bind\n\
|
||||||
|
",
|
||||||
|
persistence = persistence.display(),
|
||||||
|
);
|
||||||
|
let create_bind_mount_unit = CreateFile::plan(
|
||||||
|
"/etc/systemd/system/nix.mount",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
0o0644,
|
||||||
|
create_bind_mount_buf,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?;
|
||||||
|
|
||||||
|
let ensure_symlinked_units_resolve_buf = format!(
|
||||||
|
"\
|
||||||
|
[Unit]\n\
|
||||||
|
Description=Ensure Nix related units which are symlinked resolve\n\
|
||||||
|
After=nix.mount\n\
|
||||||
|
Requires=nix-directory.service\n\
|
||||||
|
Requires=nix.mount\n\
|
||||||
|
PropagatesStopTo=nix-directory.service\n\
|
||||||
|
PropagatesStopTo=nix.mount\n\
|
||||||
|
DefaultDependencies=no\n\
|
||||||
|
\n\
|
||||||
|
[Service]\n\
|
||||||
|
Type=oneshot\n\
|
||||||
|
RemainAfterExit=yes\n\
|
||||||
|
ExecStart=/usr/bin/systemctl daemon-reload\n\
|
||||||
|
ExecStart=/usr/bin/systemctl restart --no-block sockets.target timers.target multi-user.target\n\
|
||||||
|
\n\
|
||||||
|
[Install]\n\
|
||||||
|
WantedBy=sysinit.target\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
let ensure_symlinked_units_resolve_unit = CreateFile::plan(
|
||||||
|
"/etc/systemd/system/ensure-symlinked-units-resolve.service",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
0o0644,
|
||||||
|
ensure_symlinked_units_resolve_buf,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?;
|
||||||
|
|
||||||
|
Ok(vec![
|
||||||
|
CreateDirectory::plan(&persistence, None, None, 0o0755, true)
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?
|
||||||
|
.boxed(),
|
||||||
|
nix_directory_unit.boxed(),
|
||||||
|
create_bind_mount_unit.boxed(),
|
||||||
|
ensure_symlinked_units_resolve_unit.boxed(),
|
||||||
|
StartSystemdUnit::plan("ensure-symlinked-units-resolve.service".to_string())
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?
|
||||||
|
.boxed(),
|
||||||
|
ProvisionNix::plan(&self.settings.clone())
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?
|
||||||
|
.boxed(),
|
||||||
|
ConfigureNix::plan(&self.settings)
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?
|
||||||
|
.boxed(),
|
||||||
|
StartSystemdUnit::plan("nix-daemon.socket".to_string())
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?
|
||||||
|
.boxed(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Result<HashMap<String, serde_json::Value>, InstallSettingsError> {
|
||||||
|
let Self {
|
||||||
|
settings,
|
||||||
|
persistence,
|
||||||
|
} = self;
|
||||||
|
let mut map = HashMap::default();
|
||||||
|
|
||||||
|
map.extend(settings.settings()?.into_iter());
|
||||||
|
map.insert(
|
||||||
|
"persistence".to_string(),
|
||||||
|
serde_json::to_value(persistence)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<BuiltinPlanner> for SteamDeck {
|
||||||
|
fn into(self) -> BuiltinPlanner {
|
||||||
|
BuiltinPlanner::SteamDeck(self)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
/*! [`BuiltinPlanner`]s and traits to create new types which can be used to plan out an [`InstallPlan`]
|
/*! [`BuiltinPlanner`]s and traits to create new types which can be used to plan out an [`InstallPlan`]
|
||||||
|
|
||||||
It's a [`Planner`]s job to construct (if possible) a valid [`InstallPlan`] for the host. Some planners,
|
It's a [`Planner`]s job to construct (if possible) a valid [`InstallPlan`] for the host. Some planners,
|
||||||
like [`LinuxMulti`](linux::LinuxMulti), are operating system specific. Others, like [`SteamDeck`](specific::SteamDeck), are device specific.
|
like [`LinuxMulti`](linux::LinuxMulti), are operating system specific. Others, like [`SteamDeck`](linux::SteamDeck), are device specific.
|
||||||
|
|
||||||
[`Planner`]s contain their planner specific settings, typically alongside a [`CommonSettings`][crate::settings::CommonSettings].
|
[`Planner`]s contain their planner specific settings, typically alongside a [`CommonSettings`][crate::settings::CommonSettings].
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ use std::{error::Error, collections::HashMap};
|
||||||
use harmonic::{
|
use harmonic::{
|
||||||
InstallPlan,
|
InstallPlan,
|
||||||
settings::{CommonSettings, InstallSettingsError},
|
settings::{CommonSettings, InstallSettingsError},
|
||||||
planner::{Planner, PlannerError, specific::SteamDeck},
|
planner::{Planner, PlannerError, linux::SteamDeck},
|
||||||
action::{Action, StatefulAction, linux::StartSystemdUnit},
|
action::{Action, StatefulAction, linux::StartSystemdUnit},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ impl Planner for MyPlanner {
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
StartSystemdUnit::plan("nix-daemon.socket".into())
|
StartSystemdUnit::plan("nix-daemon.socket")
|
||||||
.await
|
.await
|
||||||
.map_err(PlannerError::Action)?.boxed(),
|
.map_err(PlannerError::Action)?.boxed(),
|
||||||
])
|
])
|
||||||
|
@ -76,7 +76,6 @@ match plan.install(None).await {
|
||||||
*/
|
*/
|
||||||
pub mod darwin;
|
pub mod darwin;
|
||||||
pub mod linux;
|
pub mod linux;
|
||||||
pub mod specific;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
@ -115,8 +114,8 @@ pub enum BuiltinPlanner {
|
||||||
LinuxMulti(linux::LinuxMulti),
|
LinuxMulti(linux::LinuxMulti),
|
||||||
/// A standard MacOS (Darwin) multi-user install
|
/// A standard MacOS (Darwin) multi-user install
|
||||||
DarwinMulti(darwin::DarwinMulti),
|
DarwinMulti(darwin::DarwinMulti),
|
||||||
/// An install suitable for the Valve Steam Deck console
|
/// A specialized install suitable for the Valve Steam Deck console
|
||||||
SteamDeck(specific::SteamDeck),
|
SteamDeck(linux::SteamDeck),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuiltinPlanner {
|
impl BuiltinPlanner {
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
mod steam_deck;
|
|
||||||
|
|
||||||
pub use steam_deck::SteamDeck;
|
|
|
@ -1,68 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
action::{
|
|
||||||
base::CreateDirectory,
|
|
||||||
common::ProvisionNix,
|
|
||||||
linux::{CreateSystemdSysext, StartSystemdUnit},
|
|
||||||
StatefulAction,
|
|
||||||
},
|
|
||||||
planner::{Planner, PlannerError},
|
|
||||||
settings::CommonSettings,
|
|
||||||
settings::InstallSettingsError,
|
|
||||||
Action, BuiltinPlanner,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A planner suitable for Valve Steam Deck consoles
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
||||||
#[cfg_attr(feature = "cli", derive(clap::Parser))]
|
|
||||||
pub struct SteamDeck {
|
|
||||||
#[cfg_attr(feature = "cli", clap(flatten))]
|
|
||||||
pub settings: CommonSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
#[typetag::serde(name = "steam-deck")]
|
|
||||||
impl Planner for SteamDeck {
|
|
||||||
async fn default() -> Result<Self, PlannerError> {
|
|
||||||
Ok(Self {
|
|
||||||
settings: CommonSettings::default()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn plan(&self) -> Result<Vec<StatefulAction<Box<dyn Action>>>, PlannerError> {
|
|
||||||
Ok(vec![
|
|
||||||
CreateSystemdSysext::plan("/var/lib/extensions/nix")
|
|
||||||
.await
|
|
||||||
.map_err(PlannerError::Action)?
|
|
||||||
.boxed(),
|
|
||||||
CreateDirectory::plan("/nix", None, None, 0o0755, true)
|
|
||||||
.await
|
|
||||||
.map_err(PlannerError::Action)?
|
|
||||||
.boxed(),
|
|
||||||
ProvisionNix::plan(&self.settings.clone())
|
|
||||||
.await
|
|
||||||
.map_err(PlannerError::Action)?
|
|
||||||
.boxed(),
|
|
||||||
StartSystemdUnit::plan("nix-daemon.socket".into())
|
|
||||||
.await
|
|
||||||
.map_err(PlannerError::Action)?
|
|
||||||
.boxed(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn settings(&self) -> Result<HashMap<String, serde_json::Value>, InstallSettingsError> {
|
|
||||||
let Self { settings } = self;
|
|
||||||
let mut map = HashMap::default();
|
|
||||||
|
|
||||||
map.extend(settings.settings()?.into_iter());
|
|
||||||
|
|
||||||
Ok(map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<BuiltinPlanner> for SteamDeck {
|
|
||||||
fn into(self) -> BuiltinPlanner {
|
|
||||||
BuiltinPlanner::SteamDeck(self)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -127,8 +127,8 @@ pub struct CommonSettings {
|
||||||
pub(crate) nix_package_url: Url,
|
pub(crate) nix_package_url: Url,
|
||||||
|
|
||||||
/// Extra configuration lines for `/etc/nix.conf`
|
/// Extra configuration lines for `/etc/nix.conf`
|
||||||
#[cfg_attr(feature = "cli", clap(long, env = "HARMONIC_EXTRA_CONF"))]
|
#[clap(long, env = "HARMONIC_EXTRA_CONF")]
|
||||||
pub(crate) extra_conf: Option<String>,
|
pub extra_conf: Vec<String>,
|
||||||
|
|
||||||
/// If Harmonic should forcibly recreate files it finds existing
|
/// If Harmonic should forcibly recreate files it finds existing
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
|
@ -309,9 +309,8 @@ impl CommonSettings {
|
||||||
self.nix_package_url = url;
|
self.nix_package_url = url;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extra configuration lines for `/etc/nix.conf`
|
/// Extra configuration lines for `/etc/nix.conf`
|
||||||
pub fn extra_conf(&mut self, extra_conf: Option<String>) -> &mut Self {
|
pub fn extra_conf(&mut self, extra_conf: Vec<String>) -> &mut Self {
|
||||||
self.extra_conf = extra_conf;
|
self.extra_conf = extra_conf;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue