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 flake-schemas #166

Merged
merged 20 commits into from
Aug 2, 2024
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: 3 additions & 0 deletions crates/nix_rs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- **DeterminateSystems/flake-schemas**
- Allow overriding the `nix` CLI command.
- Switch to flake schema given by <https://github.com/DeterminateSystems/flake-schemas>
- **`flake::schema::FlakeSchema`**
- Add `nixos_configurations`
- **`flake::url`**
Expand Down
7 changes: 6 additions & 1 deletion crates/nix_rs/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ pub struct NixCmd {
/// Consider all previously downloaded files out-of-date.
#[cfg_attr(feature = "clap", arg(long))]
pub refresh: bool,

/// The command to run instead of `nix`.
#[cfg_attr(feature = "clap", arg(long))]
pub command: Option<String>,
}

impl Default for NixCmd {
Expand All @@ -51,6 +55,7 @@ impl Default for NixCmd {
extra_experimental_features: vec![],
extra_access_tokens: vec![],
refresh: false,
command: None,
}
}
}
Expand Down Expand Up @@ -94,7 +99,7 @@ impl NixCmd {

/// Return a [Command] for this [NixCmd] configuration
pub fn command(&self) -> Command {
let mut cmd = Command::new("nix");
let mut cmd = Command::new(self.command.as_deref().unwrap_or("nix"));
cmd.kill_on_drop(true);
cmd.args(self.args());
cmd
Expand Down
116 changes: 102 additions & 14 deletions crates/nix_rs/src/flake/outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@ 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");

/// 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");

/// Represents the "outputs" of a flake
///
/// This structure is currently produced by `nix flake show`, thus to parse it we must toggle serde untagged.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FlakeOutputs {
Val(Val),
Leaf(Leaf),
Attrset(BTreeMap<String, FlakeOutputs>),
}

Expand All @@ -25,10 +35,10 @@ impl FlakeOutputs {
Ok(v.into_flake_outputs())
}

/// Get the non-attrset value
pub fn as_leaf(&self) -> Option<&Val> {
/// Get the non-attrset leaf
pub fn as_leaf(&self) -> Option<&Leaf> {
match self {
Self::Val(v) => Some(v),
Self::Leaf(v) => Some(v),
_ => None,
}
}
Expand Down Expand Up @@ -68,25 +78,94 @@ impl FlakeOutputs {
}
}

/// Represents a leaf value of a flake output
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Leaf {
Val(Val),
Unknown(Unknown),
Filtered(Filtered),
Skipped(Skipped),
/// Represents description for a flake output
/// (e.g. `Doc` for `formatter` will be "The `formatter` output specifies the package to use to format the project.")
Doc(String),
}

impl Leaf {
/// Get the value as a [Val]
pub fn as_val(&self) -> Option<&Val> {
match self {
Self::Val(v) => Some(v),
_ => None,
}
}
}

/// The metadata of a flake output value which is of non-attrset [Type]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Val {
#[serde(rename = "type")]
#[serde(rename = "what")]
pub type_: Type,
pub name: Option<String>,
pub description: Option<String>,
pub derivation_name: Option<String>,
pub short_description: Option<String>,
}

impl Default for Val {
fn default() -> Self {
Self {
type_: Type::Unknown,
derivation_name: None,
short_description: None,
}
}
}

/// Boolean flags at the leaf of a flake output
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Unknown {
pub unknown: bool,
}

/// Represents flake outputs that cannot be evaluated on current platform
/// (e.g. `nixosConfigurations` on darwin System)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename = "camelCase")]
pub struct Filtered {
pub filtered: bool,
}

/// Represents flake outputs that are skipped unless explicitly requested
/// (e.g. `legacyPackages`)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Skipped {
pub skipped: bool,
}

/// The type of a flake output [Val]
///
/// [Nix source ref](https://github.com/NixOS/nix/blob/2.14.1/src/nix/flake.cc#L1105)
/// These types can differ based on [DEFAULT_FLAKE_SCHEMAS].
/// The types here are based on <https://github.com/DeterminateSystems/flake-schemas>
/// For example, see [NixosModule type](https://github.com/DeterminateSystems/flake-schemas/blob/0a5c42297d870156d9c57d8f99e476b738dcd982/flake.nix#L268)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Type {
#[serde(rename = "NixOS module")]
NixosModule,
Derivation,
#[serde(rename = "NixOS configuration")]
NixosConfiguration,
#[serde(rename = "nix-darwin configuration")]
DarwinConfiguration,
#[serde(rename = "package")]
Package,
#[serde(rename = "development environment")]
DevShell,
#[serde(rename = "CI test")]
Check,
#[serde(rename = "app")]
shivaraj-bh marked this conversation as resolved.
Show resolved Hide resolved
App,
#[serde(rename = "template")]
Template,
#[serde(other)]
Unknown,
Expand All @@ -97,7 +176,11 @@ impl Type {
pub fn to_icon(&self) -> &'static str {
match self {
Self::NixosModule => "❄️",
Self::Derivation => "📦",
Self::NixosConfiguration => "🔧",
Self::DarwinConfiguration => "🍎",
Self::Package => "📦",
Self::DevShell => "🐚",
Self::Check => "🧪",
Self::App => "📱",
Self::Template => "🏗️",
Self::Unknown => "❓",
Expand All @@ -117,7 +200,7 @@ impl Display for Type {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
enum FlakeOutputsUntagged {
UVal(Val),
ULeaf(Leaf),
UAttrset(BTreeMap<String, FlakeOutputsUntagged>),
}

Expand All @@ -128,13 +211,18 @@ impl FlakeOutputsUntagged {
nix_cmd: &crate::command::NixCmd,
flake_url: &super::url::FlakeUrl,
) -> Result<Self, crate::command::NixCmdError> {
let v = nix_cmd
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"),
shivaraj-bh marked this conversation as resolved.
Show resolved Hide resolved
&flake_url.to_string(),
])
.await?;
Expand All @@ -144,7 +232,7 @@ impl FlakeOutputsUntagged {
/// Convert to [FlakeOutputs]
fn into_flake_outputs(self) -> FlakeOutputs {
match self {
Self::UVal(v) => FlakeOutputs::Val(v),
Self::ULeaf(v) => FlakeOutputs::Leaf(v),
Self::UAttrset(v) => FlakeOutputs::Attrset(
v.into_iter()
.map(|(k, v)| (k, v.into_flake_outputs()))
Expand Down
55 changes: 39 additions & 16 deletions crates/nix_rs/src/flake/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};

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

Expand All @@ -16,16 +16,22 @@ use super::{
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FlakeSchema {
pub system: System,
pub packages: BTreeMap<String, Val>,
pub legacy_packages: BTreeMap<String, Val>,
pub devshells: BTreeMap<String, Val>,
pub checks: BTreeMap<String, Val>,
pub apps: BTreeMap<String, Val>,
pub formatter: Option<Val>,
pub nixos_configurations: BTreeMap<String, Val>,
pub packages: BTreeMap<String, Leaf>,
pub legacy_packages: BTreeMap<String, Leaf>,
pub devshells: BTreeMap<String, Leaf>,
pub checks: BTreeMap<String, Leaf>,
pub apps: BTreeMap<String, Leaf>,
pub formatter: Option<Leaf>,
pub nixos_configurations: BTreeMap<String, Leaf>,
pub darwin_configurations: BTreeMap<String, Leaf>,
shivaraj-bh marked this conversation as resolved.
Show resolved Hide resolved
srid marked this conversation as resolved.
Show resolved Hide resolved
pub home_configurations: BTreeMap<String, Leaf>,
pub nixos_modules: BTreeMap<String, Leaf>,
pub docker_images: BTreeMap<String, Leaf>,
pub overlays: BTreeMap<String, Leaf>,
pub templates: BTreeMap<String, Leaf>,
pub schemas: BTreeMap<String, Leaf>,
/// Other unrecognized keys.
pub other: Option<BTreeMap<String, FlakeOutputs>>,
// TODO: Add nixosModules, nixosConfigurations, darwinModules, etc.
}

impl FlakeSchema {
Expand All @@ -35,8 +41,8 @@ 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, Val> {
let mut f = || -> Option<BTreeMap<String, Val>> {
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
Expand All @@ -52,14 +58,21 @@ impl FlakeSchema {
output.pop(ks);
mr.unwrap_or(BTreeMap::new())
};
let pop_per_system_tree = |output: &mut FlakeOutputs, k: &str| -> BTreeMap<String, Val> {
pop_tree(output, &[k, system.as_ref()])
let pop_per_system_tree = |output: &mut FlakeOutputs, k: &str| -> BTreeMap<String, Leaf> {
pop_tree(
output,
&[k, "output", "children", system.as_ref(), "children"],
)
};
let pop_leaf_type = |output: &mut FlakeOutputs, k: &str| -> Option<Val> {
let leaf = output.pop(&[k, system.as_ref()])?.as_leaf()?.clone();
let pop_leaf_type = |output: &mut FlakeOutputs, k: &str| -> Option<Leaf> {
let leaf = output
.pop(&[k, "output", "children", system.as_ref()])?
.as_leaf()?
.clone();
output.pop(&[k]);
Some(leaf)
};

FlakeSchema {
system: system.clone(),
packages: pop_per_system_tree(output, "packages"),
Expand All @@ -68,7 +81,17 @@ impl FlakeSchema {
checks: pop_per_system_tree(output, "checks"),
apps: pop_per_system_tree(output, "apps"),
formatter: pop_leaf_type(output, "formatter"),
srid marked this conversation as resolved.
Show resolved Hide resolved
nixos_configurations: pop_tree(output, &["nixosConfigurations"]),
nixos_configurations: pop_tree(output, &["nixosConfigurations", "output", "children"]),
darwin_configurations: pop_tree(
output,
&["darwinConfigurations", "output", "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(),
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/omnix-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ tabled = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
clap_complete = { workspace = true }
serde_json = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
Expand Down
Loading