Skip to content

Commit

Permalink
Add build-vm subcommand to nh os.
Browse files Browse the repository at this point in the history
This commit adds the `nh os build-vm` subcommand to build a virtual
machine activation script instead of a full system.
This includes a new option --with-bootloader/-B that applies to just build-vm,
to build a VM with a bootloader.
  • Loading branch information
cdo256 committed Jan 13, 2025
1 parent f4da5cf commit a0261e2
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/darwin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl DarwinRebuildArgs {
}
}

let toplevel = toplevel_for(hostname, installable);
let toplevel = toplevel_for(hostname, installable, String::from("toplevel"));

commands::Build::new(toplevel)
.extra_arg("--out-link")
Expand Down
13 changes: 13 additions & 0 deletions src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,19 @@ pub enum OsSubcommand {

/// List available generations from profile path
Info(OsGenerationsArgs),

/// Build a NixOS VM image
BuildVm(OsBuildVmArgs),
}

#[derive(Debug, Args)]
pub struct OsBuildVmArgs {
#[command(flatten)]
pub common: OsRebuildArgs,

/// Build with bootloader. Bootloader is bypassed by default.
#[arg(long, short = 'B')]
pub with_bootloader: bool,
}

#[derive(Debug, Args)]
Expand Down
57 changes: 47 additions & 10 deletions src/nixos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::commands::Command;
use crate::generations;
use crate::installable::Installable;
use crate::interface::OsSubcommand::{self};
use crate::interface::{self, OsGenerationsArgs, OsRebuildArgs, OsReplArgs};
use crate::interface::{self, OsGenerationsArgs, OsBuildVmArgs, OsRebuildArgs, OsReplArgs};
use crate::update::update;

const SYSTEM_PROFILE: &str = "/nix/var/nix/profiles/system";
Expand All @@ -22,10 +22,11 @@ impl interface::OsArgs {
pub fn run(self) -> Result<()> {
use OsRebuildVariant::*;
match self.subcommand {
OsSubcommand::Boot(args) => args.rebuild(Boot),
OsSubcommand::Test(args) => args.rebuild(Test),
OsSubcommand::Switch(args) => args.rebuild(Switch),
OsSubcommand::Build(args) => args.rebuild(Build),
OsSubcommand::Boot(args) => args.rebuild(Boot, None),
OsSubcommand::Test(args) => args.rebuild(Test, None),
OsSubcommand::Switch(args) => args.rebuild(Switch, None),
OsSubcommand::Build(args) => args.rebuild(Build, None),
OsSubcommand::BuildVm(args) => args.build(BuildVm),
OsSubcommand::Repl(args) => args.run(),
OsSubcommand::Info(args) => args.info(),
}
Expand All @@ -38,10 +39,22 @@ enum OsRebuildVariant {
Switch,
Boot,
Test,
BuildVm,
}

impl OsBuildVmArgs {
fn build(self, variant: OsRebuildVariant) -> Result<()> {
use OsRebuildVariant::*;

let final_attr = Some(get_final_attr(true, self.with_bootloader));

self.common.rebuild(variant, final_attr)
}
}

impl OsRebuildArgs {
fn rebuild(self, variant: OsRebuildVariant) -> Result<()> {
// final_attr is the attribute of config.system.build.X to evaluate.
fn rebuild(self, variant: OsRebuildVariant, final_attr: Option<String>) -> Result<()> {
use OsRebuildVariant::*;

let elevate = if self.bypass_root_check {
Expand Down Expand Up @@ -77,13 +90,22 @@ impl OsRebuildArgs {

debug!(?out_path);

let toplevel = toplevel_for(hostname, self.common.installable.clone());
let toplevel = toplevel_for(
hostname,
self.common.installable.clone(),
final_attr.unwrap_or(String::from("toplevel"))
);

let message = match variant {
BuildVm => "Building NixOS VM image",
_ => "Building NixOS configuration",
};

commands::Build::new(toplevel)
.extra_arg("--out-link")
.extra_arg(out_path.get_path())
.extra_args(&self.extra_args)
.message("Building NixOS configuration")
.message(message)
.nom(!self.common.no_nom)
.run()?;

Expand Down Expand Up @@ -165,11 +187,26 @@ impl OsRebuildArgs {
}
}

pub fn toplevel_for<S: AsRef<str>>(hostname: S, installable: Installable) -> Installable {
pub fn get_final_attr(build_vm: bool, with_bootloader: bool) -> String {
let attr = if build_vm && with_bootloader {
"vmWithBootLoader"
} else if build_vm {
"vm"
} else {
"toplevel"
};
String::from(attr)
}

pub fn toplevel_for<S: AsRef<str>>(
hostname: S,
installable: Installable,
final_attr: String,
) -> Installable {
let mut res = installable.clone();
let hostname = hostname.as_ref().to_owned();

let toplevel = ["config", "system", "build", "toplevel"]
let toplevel = ["config", "system", "build", &final_attr]
.into_iter()
.map(String::from);

Expand Down

0 comments on commit a0261e2

Please sign in to comment.