difftreelog
feat build specializations
in: trunk
2 files changed
cmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth1use std::{env::current_dir, process::Stdio, time::Duration};23use crate::{command::CommandExt, host::Config};4use anyhow::Result;5use clap::Parser;6use tokio::{process::Command, task::LocalSet, time::sleep};7use tracing::{error, field, info, info_span, warn, Instrument};89#[derive(Parser, Clone)]10pub struct BuildSystems {11 /// Jobs to run locally12 #[clap(long)]13 jobs: Option<usize>,14 /// Do not continue on error15 #[clap(long)]16 fail_fast: bool,17 /// Run builds as sudo18 #[clap(long)]19 privileged_build: bool,20 #[clap(subcommand)]21 subcommand: Subcommand,2223 /// --builders arg for nix24 #[clap(long)]25 builders: Option<String>,26 /// --show-trace arg for nix27 #[structopt(long)]28 show_trace: bool,29}3031enum UploadAction {32 Test,33 Boot,34 Switch,35}36impl UploadAction {37 fn name(&self) -> &'static str {38 match self {39 UploadAction::Test => "test",40 UploadAction::Boot => "boot",41 UploadAction::Switch => "switch",42 }43 }4445 pub(crate) fn should_switch_profile(&self) -> bool {46 matches!(self, Self::Switch | Self::Test)47 }48}4950enum PackageAction {51 SdImage,52}5354enum Action {55 Upload(Option<UploadAction>),56 Package(PackageAction),57}5859impl From<Subcommand> for Action {60 fn from(s: Subcommand) -> Self {61 match s {62 Subcommand::Upload => Self::Upload(None),63 Subcommand::Test => Self::Upload(Some(UploadAction::Test)),64 Subcommand::Boot => Self::Upload(Some(UploadAction::Boot)),65 Subcommand::Switch => Self::Upload(Some(UploadAction::Switch)),66 Subcommand::SdImage => Self::Package(PackageAction::SdImage),67 }68 }69}7071#[derive(Parser, Clone)]72enum Subcommand {73 /// Upload, but do not switch74 Upload,75 /// Upload + switch to built system until reboot76 Test,77 /// Upload + switch to built system after reboot78 Boot,79 /// Upload + test + boot80 Switch,8182 /// Build sd image83 SdImage,84}8586impl BuildSystems {87 async fn build_task(self, config: Config, host: String) -> Result<()> {88 info!("building");89 let built = {90 let dir = tempfile::tempdir()?;91 dir.path().to_owned()92 };9394 let mut nix_build = if self.privileged_build {95 let mut out = Command::new("sudo");96 out.arg("nix");97 out98 } else {99 Command::new("nix")100 };101 nix_build102 .args(&[103 "build",104 "--impure",105 "--json",106 // "--show-trace",107 "--no-link",108 "--out-link",109 ])110 .arg(&built)111 .arg(config.configuration_attr_name(&format!(112 "configuredSystems.{}.config.system.build.toplevel",113 host114 )));115116 if self.show_trace {117 nix_build.arg("--show-trace");118 }119 if let Some(builders) = &self.builders {120 nix_build.arg("--builders").arg(builders);121 }122 if let Some(jobs) = &self.jobs {123 nix_build.arg("--max-jobs");124 nix_build.arg(format!("{}", jobs));125 }126 if !self.fail_fast {127 nix_build.arg("--keep-going");128 }129130 nix_build.run_nix().await?;131 let built = std::fs::canonicalize(built)?;132133 let action = Action::from(self.subcommand.clone());134135 match action {136 Action::Upload(action) => {137 if !config.is_local(&host) {138 info!("uploading system closure");139 let mut tries = 0;140 loop {141 match Command::new("nix")142 .args(&["copy", "--to"])143 .arg(format!("ssh://root@{}", host))144 .arg(&built)145 .inherit_stdio()146 .run_nix()147 .await148 {149 Ok(()) => break,150 Err(e) if tries < 3 => {151 tries += 1;152 warn!("Copy failure ({}/3): {}", tries, e);153 sleep(Duration::from_millis(5000)).await;154 }155 Err(e) => return Err(e),156 }157 }158 }159 if let Some(action) = action {160 if action.should_switch_profile() {161 info!("switching generation");162 config163 .command_on(&host, "nix-env", true)164 .args(&["-p", "/nix/var/nix/profiles/system", "--set"])165 .arg(&built)166 .inherit_stdio()167 .run()168 .await?;169 }170 info!("executing activation script");171 let mut switch_script = built.clone();172 switch_script.push("bin");173 switch_script.push("switch-to-configuration");174 config175 .command_on(&host, switch_script, true)176 .arg(action.name())177 .stdout(Stdio::inherit())178 .run()179 .await?;180 }181 }182 Action::Package(PackageAction::SdImage) => {183 let mut out = current_dir()?;184 out.push(format!("sd-image-{}", host));185186 info!("building sd image to {:?}", out);187 let mut nix_build = if self.privileged_build {188 let mut out = Command::new("sudo");189 out.arg("nix");190 out191 } else {192 Command::new("nix")193 };194 nix_build195 .args(&["build", "--impure", "--no-link", "--out-link"])196 .arg(&out)197 .arg(config.configuration_attr_name(&format!(198 "configuredSystems.{}.config.system.build.sdImage",199 host,200 )));201 if let Some(builders) = &self.builders {202 nix_build.arg("--builders").arg(builders);203 }204 if let Some(jobs) = &self.jobs {205 nix_build.arg("--max-jobs");206 nix_build.arg(format!("{}", jobs));207 }208 if !self.fail_fast {209 nix_build.arg("--keep-going");210 }211212 nix_build.inherit_stdio().run_nix().await?;213 }214 };215 Ok(())216 }217218 pub async fn run(self, config: &Config) -> Result<()> {219 let hosts = config.list_hosts().await?;220 let set = LocalSet::new();221 let this = &self;222 for host in hosts.iter() {223 if config.should_skip(host) {224 continue;225 }226 let config = config.clone();227 let host = host.clone();228 let this = this.clone();229 let span = info_span!("deployment", host = field::display(&host));230 set.spawn_local(231 (async move {232 match this.build_task(config, host).await {233 Ok(_) => {}234 Err(e) => {235 error!("failed to deploy host: {}", e)236 }237 }238 })239 .instrument(span),240 );241 }242 set.await;243 Ok(())244 }245}lib/default.nixdiffbeforeafterboth--- a/lib/default.nix
+++ b/lib/default.nix
@@ -20,21 +20,17 @@
if failedAssertions != [ ]
then throw "Failed assertions:\n${nixpkgs.lib.concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
else nixpkgs.lib.showWarnings root.config.warnings root;
- in
- rec {
configuredHosts = rootAssertWarn.config.hosts;
configuredSecrets = rootAssertWarn.config.secrets;
- configuredSystems = nixpkgs.lib.listToAttrs (
+ configuredSystems = configuredSystemsWithExtraModules [ ];
+ configuredSystemsWithExtraModules = extraModules: nixpkgs.lib.listToAttrs (
map
(
name: {
inherit name;
value = nixpkgs.lib.nixosSystem {
system = configuredHosts.${name}.system;
- modules = configuredHosts.${name}.modules ++ (
- if configuredHosts.${name}.system == "aarch64-linux" then [ (nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix") ]
- else [ ]
- ) ++ [
+ modules = configuredHosts.${name}.modules ++ extraModules ++ [
({ ... }: {
nixpkgs.system = system;
nixpkgs.localSystem.system = system;
@@ -51,6 +47,22 @@
}
)
(builtins.attrNames rootAssertWarn.config.hosts)
- ); #nixpkgs.lib.nixosSystem {}
+ );
+ in
+ rec {
+ inherit configuredHosts configuredSecrets configuredSystems;
+ buildSystems = {
+ toplevel = builtins.mapAttrs (_name: value: value.config.system.build.toplevel) (configuredSystemsWithExtraModules [ ]);
+ sdImage = builtins.mapAttrs (_name: value: value.config.system.build.sdImage) (configuredSystemsWithExtraModules [
+ (nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix")
+ ]);
+ installationCd = builtins.mapAttrs (_name: value: value.config.system.build.isoImage) (configuredSystemsWithExtraModules [
+ (nixpkgs + "/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix")
+ ({ lib, ... }: {
+ # Needed for https://github.com/NixOS/nixpkgs/issues/58959
+ boot.supportedFilesystems = lib.mkForce [ "btrfs" "reiserfs" "vfat" "f2fs" "xfs" "ntfs" "cifs" ];
+ })
+ ]);
+ };
});
}