Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/nix_health/crate.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ in
);
inherit (rust-project.crates."nix_rs".crane.args)
DEFAULT_FLAKE_SCHEMAS
NIX_FLAKE_SCHEMAS_BIN
INSPECT_FLAKE
NIX_SYSTEMS
;
nativeBuildInputs = with pkgs; [
nix # Tests need nix cli
Expand Down
1 change: 1 addition & 0 deletions crates/nix_rs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- Add module, for `nix run`, `nix build` and `nix develop`
- **`version`**:
- Add `NixVersion::get`
- **`system_list`**: New module

## 1.0.0

Expand Down
1 change: 1 addition & 0 deletions crates/nix_rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ bytesize = { workspace = true }
clap = { workspace = true, optional = true }
nonempty = { workspace = true }
whoami = { workspace = true }
lazy_static = { workspace = true }

[features]
clap = ["dep:clap"]
8 changes: 7 additions & 1 deletion crates/nix_rs/crate.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ in
name = "flake-schemas";
src = flake.inputs.self + /nix/flake-schemas;
};
NIX_FLAKE_SCHEMAS_BIN = lib.getExe pkgs.nix-flake-schemas;
INSPECT_FLAKE = inputs.inspect;
NIX_SYSTEMS = builtins.toJSON {
x86_64-linux = inputs.nix-systems-x86_64-linux;
aarch64-linux = inputs.nix-systems-aarch64-linux;
x86_64-darwin = inputs.nix-systems-x86_64-darwin;
aarch64-darwin = inputs.nix-systems-aarch64-darwin;
};
};
};
}
2 changes: 1 addition & 1 deletion crates/nix_rs/src/flake/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl Flake {
nix_config: &NixConfig,
url: FlakeUrl,
) -> Result<Flake, NixCmdError> {
let output = FlakeOutputs::from_nix(nix_cmd, &url).await?;
let output = FlakeOutputs::from_nix(nix_cmd, &url, &nix_config.system.value).await?;
let schema = FlakeSchema::from(&output, &nix_config.system.value);
Ok(Flake {
url,
Expand Down
110 changes: 49 additions & 61 deletions crates/nix_rs/src/flake/outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,71 @@ use std::{
fmt::Display,
};

/// Absolute path to the `nix` binary compiled with flake schemas support
///
/// We expect this environment to be set in Nix build and shell.
pub const NIX_FLAKE_SCHEMAS_BIN: &str = env!("NIX_FLAKE_SCHEMAS_BIN");
use crate::system_list::SystemsListFlakeRef;

/// Flake URL of the default flake schemas
///
/// We expect this environment to be set in Nix build and shell.
pub const DEFAULT_FLAKE_SCHEMAS: &str = env!("DEFAULT_FLAKE_SCHEMAS");

/// Flake URL of the flake that defines functions for inspecting flake outputs
///
/// We expect this environment to be set in Nix build and shell.
pub const INSPECT_FLAKE: &str = env!("INSPECT_FLAKE");

/// Represents the "outputs" of a flake
///
/// This structure is currently produced by `nix flake show`, thus to parse it we must toggle serde untagged.
/// TODO: Rename this to `FlakeSchema` while generalizing the existing `schema.rs` module.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FlakeOutputs {
pub struct FlakeOutputs {
pub inventory: BTreeMap<String, InventoryItem>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum InventoryItem {
Leaf(Leaf),
Attrset(BTreeMap<String, FlakeOutputs>),
Attrset(BTreeMap<String, InventoryItem>),
}

impl FlakeOutputs {
/// Run `nix flake show` on the given flake url
/// Determine flake outputs using [INSPECT_FLAKE] and [DEFAULT_FLAKE_SCHEMAS]
pub async fn from_nix(
nix_cmd: &crate::command::NixCmd,
flake_url: &super::url::FlakeUrl,
system: &super::System,
) -> Result<Self, crate::command::NixCmdError> {
let v = FlakeOutputsUntagged::from_nix(nix_cmd, flake_url).await?;
Ok(v.into_flake_outputs())
let v = nix_cmd
.run_with_args_expecting_json(&[
"eval",
"--json",
"--override-input",
"flake-schemas",
env!("DEFAULT_FLAKE_SCHEMAS"),
"--override-input",
"flake",
flake_url,
"--override-input",
"systems",
// TODO: don't use unwrap
&SystemsListFlakeRef::from_known_system(system).unwrap().0,
"--no-write-lock-file",
// Why `exculdingOutputPaths`?
// This function is much faster than `includingOutputPaths` and also solves <https://github.com/juspay/omnix/discussions/231>
// Also See: https://github.com/DeterminateSystems/inspect/blob/7f0275abbdc46b3487ca69e2acd932ce666a03ff/flake.nix#L139
//
//
// Note: We might need to use `includingOutputPaths` in the future, when replacing `devour-flake`.
// In which case, `om ci` and `om show` can invoke the appropriate function from `INSPECT_FLAKE`.
//
&format!("{}#contents.excludingOutputPaths", env!("INSPECT_FLAKE")),
])
.await?;
Ok(v)
}
}

impl InventoryItem {
/// Get the non-attrset leaf
pub fn as_leaf(&self) -> Option<&Leaf> {
match self {
Expand All @@ -44,7 +80,7 @@ impl FlakeOutputs {
}

/// Ensure the value is an attrset, and get it
pub fn as_attrset(&self) -> Option<&BTreeMap<String, FlakeOutputs>> {
pub fn as_attrset(&self) -> Option<&BTreeMap<String, InventoryItem>> {
match self {
Self::Attrset(v) => Some(v),
_ => None,
Expand All @@ -55,8 +91,8 @@ impl FlakeOutputs {
///
/// # Example
/// ```no_run
/// let tree : &nix_rs::flake::outputs::FlakeOutputs = todo!();
/// let val = tree.pop(&["packages", "aarch64-darwin", "default"]);
/// let tree : &nix_rs::flake::outputs::InventoryItem = todo!();
/// let val = tree.pop(&["aarch64-darwin", "default"]);
/// ```
pub fn pop(&mut self, path: &[&str]) -> Option<Self> {
let mut curr = self;
Expand Down Expand Up @@ -193,51 +229,3 @@ impl Display for Type {
f.write_str(&format!("{:?}", self))
}
}

/// This type is identical to [FlakeOutputs] except for the serde untagged attribute, which enables parsing the JSON output of `nix flake show`.
///
/// This separation exists to workaround <https://github.com/DioxusLabs/dioxus-std/issues/20>
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
enum FlakeOutputsUntagged {
ULeaf(Leaf),
UAttrset(BTreeMap<String, FlakeOutputsUntagged>),
}

impl FlakeOutputsUntagged {
/// Run `nix flake show` on the given flake url
#[tracing::instrument(name = "flake-show")]
async fn from_nix(
nix_cmd: &crate::command::NixCmd,
flake_url: &super::url::FlakeUrl,
) -> Result<Self, crate::command::NixCmdError> {
let mut nix_flake_schemas_cmd = nix_cmd.clone();
nix_flake_schemas_cmd.command = Some(env!("NIX_FLAKE_SCHEMAS_BIN").to_string());

let v = nix_flake_schemas_cmd
.run_with_args_expecting_json(&[
"flake",
"show",
"--legacy", // for showing nixpkgs legacyPackages
"--allow-import-from-derivation",
"--json",
"--default-flake-schemas",
env!("DEFAULT_FLAKE_SCHEMAS"),
flake_url,
])
.await?;
Ok(v)
}

/// Convert to [FlakeOutputs]
fn into_flake_outputs(self) -> FlakeOutputs {
match self {
Self::ULeaf(v) => FlakeOutputs::Leaf(v),
Self::UAttrset(v) => FlakeOutputs::Attrset(
v.into_iter()
.map(|(k, v)| (k, v.into_flake_outputs()))
.collect(),
),
}
}
}
80 changes: 45 additions & 35 deletions crates/nix_rs/src/flake/schema.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
//! High-level schema of a flake
//!
//! TODO: Use <https://github.com/DeterminateSystems/flake-schemas>
//! TODO: Consolidate with `outputs.rs`
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use super::{
outputs::{FlakeOutputs, Leaf},
outputs::{FlakeOutputs, InventoryItem, Leaf},
System,
};

/// High-level schema of a flake
///
/// TODO: Use <https://github.com/DeterminateSystems/flake-schemas>
/// TODO: Consolidate with `outputs.rs`
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FlakeSchema {
pub system: System,
Expand All @@ -31,7 +31,7 @@ pub struct FlakeSchema {
pub templates: BTreeMap<String, Leaf>,
pub schemas: BTreeMap<String, Leaf>,
/// Other unrecognized keys.
pub other: Option<BTreeMap<String, FlakeOutputs>>,
pub other: Option<FlakeOutputs>,
}

impl FlakeSchema {
Expand All @@ -41,35 +41,39 @@ impl FlakeSchema {
/// as is (in [FlakeSchema::other]).
pub fn from(output: &FlakeOutputs, system: &System) -> Self {
let output: &mut FlakeOutputs = &mut output.clone();
let pop_tree = |output: &mut FlakeOutputs, ks: &[&str]| -> BTreeMap<String, Leaf> {
let mut f = || -> Option<BTreeMap<String, Leaf>> {
let out = output.pop(ks)?;
let outs = out.as_attrset()?;
let r = outs
.iter()
.filter_map(|(k, v)| {
let v = v.as_leaf()?;
Some((k.clone(), v.clone()))
})
.collect();
Some(r)
};
let mr = f();
output.pop(ks);
mr.unwrap_or(BTreeMap::new())
let pop_tree = |inventory_item: &mut Option<&mut InventoryItem>,
ks: &[&str]|
-> BTreeMap<String, Leaf> {
let mut result = BTreeMap::new();

if let Some(item) = inventory_item {
if let Some(out) = item.pop(ks) {
if let Some(outs) = out.as_attrset() {
for (k, v) in outs {
if let Some(leaf) = v.as_leaf() {
result.insert(k.clone(), leaf.clone());
}
}
}
}
item.pop(ks);
}

result
};
let pop_per_system_tree = |output: &mut FlakeOutputs, k: &str| -> BTreeMap<String, Leaf> {
pop_tree(
output,
&[k, "output", "children", system.as_ref(), "children"],
&mut output.inventory.get_mut(k),
&["children", system.as_ref(), "children"],
)
};
let pop_leaf_type = |output: &mut FlakeOutputs, k: &str| -> Option<Leaf> {
let leaf = output
.pop(&[k, "output", "children", system.as_ref()])?
let inventory_item = output.inventory.get_mut(k)?;
let leaf = inventory_item
.pop(&["children", system.as_ref()])?
.as_leaf()?
.clone();
output.pop(&[k]);
inventory_item.pop(&[k]);
Some(leaf)
};

Expand All @@ -81,18 +85,24 @@ impl FlakeSchema {
checks: pop_per_system_tree(output, "checks"),
apps: pop_per_system_tree(output, "apps"),
formatter: pop_leaf_type(output, "formatter"),
nixos_configurations: pop_tree(output, &["nixosConfigurations", "output", "children"]),
nixos_configurations: pop_tree(
&mut output.inventory.get_mut("nixosConfigurations"),
&["children"],
),
darwin_configurations: pop_tree(
output,
&["darwinConfigurations", "output", "children"],
&mut output.inventory.get_mut("darwinConfigurations"),
&["children"],
),
home_configurations: pop_tree(
&mut output.inventory.get_mut("homeConfigurations"),
&["children"],
),
home_configurations: pop_tree(output, &["homeConfigurations", "output", "children"]),
nixos_modules: pop_tree(output, &["nixosModules", "output", "children"]),
docker_images: pop_tree(output, &["dockerImages", "output", "children"]),
overlays: pop_tree(output, &["overlays", "output", "children"]),
templates: pop_tree(output, &["templates", "output", "children"]),
schemas: pop_tree(output, &["schemas", "output", "children"]),
other: (*output).as_attrset().cloned(),
nixos_modules: pop_tree(&mut output.inventory.get_mut("nixosModules"), &["children"]),
docker_images: pop_tree(&mut output.inventory.get_mut("dockerImages"), &["children"]),
overlays: pop_tree(&mut output.inventory.get_mut("overlays"), &["children"]),
templates: pop_tree(&mut output.inventory.get_mut("templates"), &["children"]),
schemas: pop_tree(&mut output.inventory.get_mut("schemas"), &["children"]),
other: Some(output.clone()),
}
}
}
1 change: 1 addition & 0 deletions crates/nix_rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ pub mod flake;
pub mod info;
pub mod refs;
pub mod store;
pub mod system_list;
pub mod version;
Loading