Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use inspect to remove the dependency on nix fork for flake-schemas #277

Merged
merged 14 commits into from
Sep 19, 2024
Merged
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>
shivaraj-bh marked this conversation as resolved.
Show resolved Hide resolved
// 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
Loading