From 483624a87b28fcc789d42dde341ff4fcd1dd4a97 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 17 May 2022 15:37:02 -0400 Subject: [PATCH 1/6] Embed specialisation's bootspec document into the root bootspec doc --- bootspec/src/lib.rs | 11 ----------- bootspec/src/v1.rs | 4 ++-- synthesize/src/lib.rs | 44 ++++-------------------------------------- synthesize/src/main.rs | 9 ++++++++- 4 files changed, 14 insertions(+), 54 deletions(-) diff --git a/bootspec/src/lib.rs b/bootspec/src/lib.rs index 25e9722..bd79500 100644 --- a/bootspec/src/lib.rs +++ b/bootspec/src/lib.rs @@ -12,17 +12,6 @@ pub struct SpecialisationName(pub String); /// A wrapper type describing the root directory of a NixOS system configuration. pub struct SystemConfigurationRoot(pub PathBuf); -#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)] -/// A wrapper type describing the path to the bootspec schema file. -pub struct BootSpecPath(pub PathBuf); - -#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)] -/// Provides the information necessary to consume a specialisation with or without a bootspec. -pub struct SpecialisationDescription { - /// The optional path to the specialisation's bootspec. - pub bootspec: BootSpecPath, -} - // !!! IMPORTANT: KEEP `BootJson`, `SCHEMA_VERSION`, and `JSON_FILENAME` IN SYNC !!! /// The current bootspec schema. pub type BootJson = v1::BootJsonV1; diff --git a/bootspec/src/v1.rs b/bootspec/src/v1.rs index 6394129..9edaebe 100644 --- a/bootspec/src/v1.rs +++ b/bootspec/src/v1.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; -use crate::{SpecialisationDescription, SpecialisationName, SystemConfigurationRoot}; +use crate::{SpecialisationName, SystemConfigurationRoot}; /// The V1 bootspec schema version. pub const SCHEMA_VERSION: u32 = 1; @@ -29,7 +29,7 @@ pub struct BootJsonV1 { /// Path to "append-initrd-secrets" script -- $toplevel/append-initrd-secrets pub initrd_secrets: Option, /// Mapping of specialisation names to their boot.json - pub specialisation: HashMap, + pub specialisation: HashMap, /// config.system.build.toplevel path pub toplevel: SystemConfigurationRoot, } diff --git a/synthesize/src/lib.rs b/synthesize/src/lib.rs index 9fb23f9..bec780f 100644 --- a/synthesize/src/lib.rs +++ b/synthesize/src/lib.rs @@ -3,10 +3,7 @@ use std::error::Error; use std::fs; use std::path::Path; -use bootspec::{ - BootJson, BootSpecPath, SpecialisationDescription, SpecialisationName, SystemConfigurationRoot, - JSON_FILENAME, SCHEMA_VERSION, -}; +use bootspec::{BootJson, SpecialisationName, SystemConfigurationRoot, SCHEMA_VERSION}; #[doc(hidden)] pub type Result> = core::result::Result; @@ -14,11 +11,7 @@ pub type Result> = core::result::R /// Synthesize a [`BootJson`] struct from the path to a generation. /// /// This is useful when used on generations that do not have a bootspec attached to it. -pub fn synthesize_schema_from_generation(generation: &Path, out_path: &Path) -> Result<()> { - fs::create_dir(&out_path)?; - let specialisationdir = out_path.join("specialisation"); - let mut specialisationdir_created = false; - +pub fn synthesize_schema_from_generation(generation: &Path) -> Result { let mut toplevelspec = describe_system(&generation)?; if let Ok(specialisations) = fs::read_dir(generation.join("specialisation")) { @@ -31,43 +24,14 @@ pub fn synthesize_schema_from_generation(generation: &Path, out_path: &Path) -> .ok_or("Specialisation dir name was invalid UTF8")?; let toplevel = fs::canonicalize(generation.join(format!("specialisation/{}", name)))?; - let boot_json_path = toplevel.join(JSON_FILENAME); - let boot_json_path = match boot_json_path.exists() { - true => boot_json_path, - false => { - if !specialisationdir_created { - fs::create_dir(&specialisationdir)?; - specialisationdir_created = true; - } - - let specname = specialisationdir.join(format!("{name}.json")); - let subspec = describe_system(&toplevel)?; - let pretty = serde_json::to_string_pretty(&subspec) - .map_err(|e| format!("Failed to make pretty JSON from bootspec:\n{e}"))?; - - fs::write(&specname, pretty).map_err(|e| { - format!("Failed to write JSON to '{}':\n{e}", out_path.display()) - })?; - - specname - } - }; toplevelspec.specialisation.insert( SpecialisationName(name.to_string()), - SpecialisationDescription { - bootspec: BootSpecPath(boot_json_path), - }, + describe_system(&toplevel)?, ); } } - let pretty = serde_json::to_string_pretty(&toplevelspec) - .map_err(|e| format!("Failed to make pretty JSON from bootspec:\n{}", e))?; - - fs::write(&out_path.join("boot.v1.json"), pretty) - .map_err(|e| format!("Failed to write JSON to '{}':\n{}", out_path.display(), e))?; - - Ok(()) + Ok(toplevelspec) } fn describe_system(generation: &Path) -> Result { diff --git a/synthesize/src/main.rs b/synthesize/src/main.rs index aef286a..728c90a 100644 --- a/synthesize/src/main.rs +++ b/synthesize/src/main.rs @@ -1,3 +1,4 @@ +use std::fs; use std::io::{self, Write}; use std::path::PathBuf; @@ -34,7 +35,7 @@ fn cli() -> Result<()> { .ok_or("Expected output path, got none.")? .parse::()?; - synthesize::synthesize_schema_from_generation(&generation_dir, &out_path).map_err(|e| { + let spec = synthesize::synthesize_schema_from_generation(&generation_dir).map_err(|e| { format!( "Failed to synthesize bootspec for {}:\n{}", generation_dir.display(), @@ -42,5 +43,11 @@ fn cli() -> Result<()> { ) })?; + let pretty = serde_json::to_string_pretty(&spec) + .map_err(|e| format!("Failed to make pretty JSON from bootspec:\n{}", e))?; + + fs::write(&out_path.join("boot.v1.json"), pretty) + .map_err(|e| format!("Failed to write JSON to '{}':\n{}", out_path.display(), e))?; + Ok(()) } From d164570299abc86606ec893961b6fb655a76a8a9 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 17 May 2022 15:43:35 -0400 Subject: [PATCH 2/6] synthesis test output: rename to a unified document format Now that the RFC says specialisations use a single document to express all of the specialisations instead of referring to other files, flatten the structure by renaming all the files into a single file in a single directory. Note the only diff here is embedding the 21.11-specialisations/example document into the 21.11-specialisations document. --- .../boot.v1.json => 15.09-plain.json} | 0 .../boot.v1.json => 16.03-plain.json} | 0 .../boot.v1.json => 16.09-plain.json} | 0 .../boot.v1.json => 17.03-plain.json} | 0 .../boot.v1.json => 17.09-plain.json} | 0 .../boot.v1.json => 18.03-plain.json} | 0 .../boot.v1.json => 18.09-plain.json} | 0 .../boot.v1.json => 19.03-plain.json} | 0 .../boot.v1.json => 19.09-plain.json} | 0 .../boot.v1.json => 20.03-plain.json} | 0 .../boot.v1.json => 20.09-plain.json} | 0 .../boot.v1.json => 21.05-plain.json} | 0 .../boot.v1.json => 21.11-plain.json} | 0 .../21.11-specialisations.json | 29 +++++++++++++++++++ .../21.11-specialisations/boot.v1.json | 18 ------------ .../specialisation/example.json | 14 --------- 16 files changed, 29 insertions(+), 32 deletions(-) rename synthesize/integration-test-cases/expected-synthesis/{15.09-plain/boot.v1.json => 15.09-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{16.03-plain/boot.v1.json => 16.03-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{16.09-plain/boot.v1.json => 16.09-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{17.03-plain/boot.v1.json => 17.03-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{17.09-plain/boot.v1.json => 17.09-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{18.03-plain/boot.v1.json => 18.03-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{18.09-plain/boot.v1.json => 18.09-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{19.03-plain/boot.v1.json => 19.03-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{19.09-plain/boot.v1.json => 19.09-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{20.03-plain/boot.v1.json => 20.03-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{20.09-plain/boot.v1.json => 20.09-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{21.05-plain/boot.v1.json => 21.05-plain.json} (100%) rename synthesize/integration-test-cases/expected-synthesis/{21.11-plain/boot.v1.json => 21.11-plain.json} (100%) create mode 100644 synthesize/integration-test-cases/expected-synthesis/21.11-specialisations.json delete mode 100644 synthesize/integration-test-cases/expected-synthesis/21.11-specialisations/boot.v1.json delete mode 100644 synthesize/integration-test-cases/expected-synthesis/21.11-specialisations/specialisation/example.json diff --git a/synthesize/integration-test-cases/expected-synthesis/15.09-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/15.09-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/15.09-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/15.09-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/16.03-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/16.03-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/16.03-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/16.03-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/16.09-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/16.09-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/16.09-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/16.09-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/17.03-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/17.03-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/17.03-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/17.03-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/17.09-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/17.09-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/17.09-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/17.09-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/18.03-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/18.03-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/18.03-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/18.03-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/18.09-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/18.09-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/18.09-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/18.09-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/19.03-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/19.03-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/19.03-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/19.03-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/19.09-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/19.09-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/19.09-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/19.09-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/20.03-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/20.03-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/20.03-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/20.03-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/20.09-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/20.09-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/20.09-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/20.09-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/21.05-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/21.05-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/21.05-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/21.05-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/21.11-plain/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/21.11-plain.json similarity index 100% rename from synthesize/integration-test-cases/expected-synthesis/21.11-plain/boot.v1.json rename to synthesize/integration-test-cases/expected-synthesis/21.11-plain.json diff --git a/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations.json b/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations.json new file mode 100644 index 0000000..1f2acad --- /dev/null +++ b/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": 1, + "label": "NixOS 21.11pre-git (Linux 5.10.81)", + "kernel": "/nix/store/hprwry55jwyd71ng7v7c2rhk3a3z1im8-linux-5.10.81/bzImage", + "kernelParams": [ + "loglevel=4", + "net.ifnames=0" + ], + "init": "/nix/store/kgkjwhscv52r2y1ha1y19lb9h4j3lfrc-nixos-system-nixos-21.11pre-git/init", + "initrd": "/nix/store/69bhfdfv77y0vclnlxqrd8pxjzbkz47w-initrd-linux-5.10.81/initrd", + "initrdSecrets": "/nix/store/kgkjwhscv52r2y1ha1y19lb9h4j3lfrc-nixos-system-nixos-21.11pre-git/append-initrd-secrets", + "specialisation": { + "example": { + "schemaVersion": 1, + "label": "NixOS 21.11pre-git (Linux 5.10.81)", + "kernel": "/nix/store/hprwry55jwyd71ng7v7c2rhk3a3z1im8-linux-5.10.81/bzImage", + "kernelParams": [ + "loglevel=4", + "net.ifnames=0" + ], + "init": "/nix/store/3w5kr91xq46638fd310q2sa9mjm6r6hn-nixos-system-nixos-21.11pre-git/init", + "initrd": "/nix/store/69bhfdfv77y0vclnlxqrd8pxjzbkz47w-initrd-linux-5.10.81/initrd", + "initrdSecrets": "/nix/store/3w5kr91xq46638fd310q2sa9mjm6r6hn-nixos-system-nixos-21.11pre-git/append-initrd-secrets", + "specialisation": {}, + "toplevel": "/nix/store/3w5kr91xq46638fd310q2sa9mjm6r6hn-nixos-system-nixos-21.11pre-git" + } + }, + "toplevel": "/nix/store/kgkjwhscv52r2y1ha1y19lb9h4j3lfrc-nixos-system-nixos-21.11pre-git" +} \ No newline at end of file diff --git a/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations/boot.v1.json b/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations/boot.v1.json deleted file mode 100644 index 22872bf..0000000 --- a/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations/boot.v1.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "schemaVersion": 1, - "label": "NixOS 21.11pre-git (Linux 5.10.81)", - "kernel": "/nix/store/hprwry55jwyd71ng7v7c2rhk3a3z1im8-linux-5.10.81/bzImage", - "kernelParams": [ - "loglevel=4", - "net.ifnames=0" - ], - "init": "/nix/store/kgkjwhscv52r2y1ha1y19lb9h4j3lfrc-nixos-system-nixos-21.11pre-git/init", - "initrd": "/nix/store/69bhfdfv77y0vclnlxqrd8pxjzbkz47w-initrd-linux-5.10.81/initrd", - "initrdSecrets": "/nix/store/kgkjwhscv52r2y1ha1y19lb9h4j3lfrc-nixos-system-nixos-21.11pre-git/append-initrd-secrets", - "specialisation": { - "example": { - "bootspec": "./generated-synthesis/21.11-specialisations/specialisation/example.json" - } - }, - "toplevel": "/nix/store/kgkjwhscv52r2y1ha1y19lb9h4j3lfrc-nixos-system-nixos-21.11pre-git" -} \ No newline at end of file diff --git a/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations/specialisation/example.json b/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations/specialisation/example.json deleted file mode 100644 index f0be414..0000000 --- a/synthesize/integration-test-cases/expected-synthesis/21.11-specialisations/specialisation/example.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "schemaVersion": 1, - "label": "NixOS 21.11pre-git (Linux 5.10.81)", - "kernel": "/nix/store/hprwry55jwyd71ng7v7c2rhk3a3z1im8-linux-5.10.81/bzImage", - "kernelParams": [ - "loglevel=4", - "net.ifnames=0" - ], - "init": "/nix/store/3w5kr91xq46638fd310q2sa9mjm6r6hn-nixos-system-nixos-21.11pre-git/init", - "initrd": "/nix/store/69bhfdfv77y0vclnlxqrd8pxjzbkz47w-initrd-linux-5.10.81/initrd", - "initrdSecrets": "/nix/store/3w5kr91xq46638fd310q2sa9mjm6r6hn-nixos-system-nixos-21.11pre-git/append-initrd-secrets", - "specialisation": {}, - "toplevel": "/nix/store/3w5kr91xq46638fd310q2sa9mjm6r6hn-nixos-system-nixos-21.11pre-git" -} \ No newline at end of file From ac7a98fd34da5c83b28634ebb838c1ecfcf85510 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 17 May 2022 15:44:57 -0400 Subject: [PATCH 3/6] fixup --- synthesize/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synthesize/src/main.rs b/synthesize/src/main.rs index 728c90a..06cffa0 100644 --- a/synthesize/src/main.rs +++ b/synthesize/src/main.rs @@ -46,7 +46,7 @@ fn cli() -> Result<()> { let pretty = serde_json::to_string_pretty(&spec) .map_err(|e| format!("Failed to make pretty JSON from bootspec:\n{}", e))?; - fs::write(&out_path.join("boot.v1.json"), pretty) + fs::write(&out_path, pretty) .map_err(|e| format!("Failed to write JSON to '{}':\n{}", out_path.display(), e))?; Ok(()) From 2cda8f975afdefacfdb6c7658e044918edd79103 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 17 May 2022 15:45:14 -0400 Subject: [PATCH 4/6] integration test case: update to generate single .json documents --- synthesize/integration-test-cases/verify.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synthesize/integration-test-cases/verify.sh b/synthesize/integration-test-cases/verify.sh index 3c06659..5cb60c9 100755 --- a/synthesize/integration-test-cases/verify.sh +++ b/synthesize/integration-test-cases/verify.sh @@ -12,7 +12,7 @@ done) | xargs --max-procs=$(nproc) --max-lines=1 nix-build rm -rf generated-synthesis mkdir generated-synthesis for out in ./builds/*; do - cargo run --bin synthesize -- "$out" "./generated-synthesis/$(basename "$out")" + cargo run --bin synthesize -- "$out" "./generated-synthesis/$(basename "$out").json" done diff -r ./expected-synthesis ./generated-synthesis From 220e78c9b280fe13c83da2f14a8d1f5ec20a6321 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 17 May 2022 15:48:24 -0400 Subject: [PATCH 5/6] Fixup clippy lint --- synthesize/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synthesize/src/lib.rs b/synthesize/src/lib.rs index bec780f..0a7aee3 100644 --- a/synthesize/src/lib.rs +++ b/synthesize/src/lib.rs @@ -12,7 +12,7 @@ pub type Result> = core::result::R /// /// This is useful when used on generations that do not have a bootspec attached to it. pub fn synthesize_schema_from_generation(generation: &Path) -> Result { - let mut toplevelspec = describe_system(&generation)?; + let mut toplevelspec = describe_system(generation)?; if let Ok(specialisations) = fs::read_dir(generation.join("specialisation")) { for spec in specialisations.map(|res| res.map(|e| e.path())) { From e2f4992f91574931504c728a5a32e95bc966633a Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 18 May 2022 15:09:02 -0400 Subject: [PATCH 6/6] Don't format in creating a path --- synthesize/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synthesize/src/lib.rs b/synthesize/src/lib.rs index 0a7aee3..9929853 100644 --- a/synthesize/src/lib.rs +++ b/synthesize/src/lib.rs @@ -22,7 +22,7 @@ pub fn synthesize_schema_from_generation(generation: &Path) -> Result .ok_or("Could not get name of specialisation dir")? .to_str() .ok_or("Specialisation dir name was invalid UTF8")?; - let toplevel = fs::canonicalize(generation.join(format!("specialisation/{}", name)))?; + let toplevel = fs::canonicalize(generation.join("specialisation").join(name))?; toplevelspec.specialisation.insert( SpecialisationName(name.to_string()),