From 096ea9e49cb9f98aa1af27fd17e51152613995a4 Mon Sep 17 00:00:00 2001 From: erikarvstedt <36110478+erikarvstedt@users.noreply.github.com> Date: Thu, 6 Jan 2022 15:42:31 +0100 Subject: [PATCH] Support flake attr `nixosModule` (#401) * flake_info.nix: remove helper fn `default` Nix has builtin syntax for fetching attrs with a default value. * flake_info.nix: minor refactor - Remove commented out code - Simplify pkg filtering - Break overlong line * flake_info.nix: support flake attr `nixosModule` * Support default modules in rust backend and elm Co-authored-by: Yannik Sander --- flake-info/src/commands/flake_info.nix | 80 +++++++++++++------------- flake-info/src/data/export.rs | 4 +- flake-info/src/data/import.rs | 40 ++++++++++++- src/Page/Options.elm | 29 ++++++---- 4 files changed, 99 insertions(+), 54 deletions(-) diff --git a/flake-info/src/commands/flake_info.nix b/flake-info/src/commands/flake_info.nix index 58d2a52..ba702bc 100644 --- a/flake-info/src/commands/flake_info.nix +++ b/flake-info/src/commands/flake_info.nix @@ -5,43 +5,32 @@ let nixpkgs = (import {}); lib = nixpkgs.lib; - - default = drv: attr: default: if drv ? ${attr} then drv.${attr} else default; - # filter = lib.filterAttrs (key: _ : key == "apps" || key == "packages"); withSystem = fn: lib.mapAttrs (system: drvs: (fn system drvs)); isValid = d: let - r = builtins.tryEval (lib.isDerivation d && ! (lib.attrByPath [ "meta" "broken" ] false d) && builtins.seq d.name true && d ? outputs); + r = builtins.tryEval (lib.isDerivation d && ! (lib.attrByPath [ "meta" "broken" ] false d) && + builtins.seq d.name true && d ? outputs); in r.success && r.value; - all = pkgs: - let - validPkgs = lib.filterAttrs (k: v: isValid v) pkgs; - in - validPkgs; - - + validPkgs = lib.filterAttrs (k: v: isValid v); readPackages = system: drvs: lib.mapAttrsToList ( attribute_name: drv: ( - # if isValid drv then { attribute_name = attribute_name; system = system; name = drv.name; # TODO consider using `builtins.parseDrvName` - version = default drv "version" ""; + version = drv.version or ""; outputs = drv.outputs; # paths = builtins.listToAttrs ( map (output: {name = output; value = drv.${output};}) drv.outputs ); } // lib.optionalAttrs (drv ? meta && drv.meta ? description) { inherit (drv.meta) description; } // lib.optionalAttrs (drv ? meta && drv.meta ? license) { inherit (drv.meta) license; } - - # else {} ) - ) (all drvs); + ) (validPkgs drvs); readApps = system: apps: lib.mapAttrsToList ( attribute_name: app: ( { @@ -54,8 +43,7 @@ let ) ) apps; - readOptions = modules: isNixOS: let - + readOptions = let declarations = module: ( lib.evalModules { modules = (if lib.isList module then module else [ module ]) ++ [ @@ -70,7 +58,7 @@ let } ).options; - cleanUpOption = module: opt: + cleanUpOption = extraAttrs: opt: let applyOnAttr = n: f: lib.optionalAttrs (builtins.hasAttr n opt) { ${n} = f opt.${n}; }; mkDeclaration = decl: @@ -96,31 +84,47 @@ let // applyOnAttr "example" substFunction # (_: { __type = "function"; }) // applyOnAttr "type" substFunction // applyOnAttr "declarations" (map mkDeclaration) - // lib.optionalAttrs (!isNixOS) { flake = [ flake module ]; }; - - - options = lib.mapAttrs ( - attr: module: let - list = lib.optionAttrSetToDocList (declarations module); - in - map (cleanUpOption attr) (lib.filter (x: !x.internal) list) - ) modules; + // extraAttrs; in - lib.flatten (builtins.attrValues options); + { module, modulePath ? null }: let + opts = lib.optionAttrSetToDocList (declarations module); + extraAttrs = lib.optionalAttrs (modulePath != null) { + flake = modulePath; + }; + in + map (cleanUpOption extraAttrs) (lib.filter (x: !x.internal) opts); + readFlakeOptions = let + nixosModulesOpts = builtins.concatLists (lib.mapAttrsToList (moduleName: module: + readOptions { + inherit module; + modulePath = [ flake moduleName ]; + } + ) (resolved.nixosModules or {})); + + nixosModuleOpts = lib.optionals (resolved ? nixosModule) ( + readOptions { + module = resolved.nixosModule; + modulePath = [ flake ]; + } + ); + in + # We assume that `nixosModules` includes `nixosModule` when there + # are multiple modules + if nixosModulesOpts != [] then nixosModulesOpts else nixosModuleOpts; read = reader: set: lib.flatten (lib.attrValues (withSystem reader set)); - legacyPackages' = read readPackages (default resolved "legacyPackages" {}); - packages' = read readPackages (default resolved "packages" {}); + legacyPackages' = read readPackages (resolved.legacyPackages or {}); + packages' = read readPackages (resolved.packages or {}); - apps' = read readApps (default resolved "apps" {}); + apps' = read readApps (resolved.apps or {}); collectSystems = lib.lists.foldr ( drv@{ attribute_name, system, ... }: set: let - present = default set "${attribute_name}" ({ platforms = []; } // drv); + present = set."${attribute_name}" or ({ platforms = []; } // drv); drv' = present // { platforms = present.platforms ++ [ system ]; @@ -138,11 +142,9 @@ rec { legacyPackages = lib.attrValues (collectSystems legacyPackages'); packages = lib.attrValues (collectSystems packages'); apps = lib.attrValues (collectSystems apps'); - options = readOptions (default resolved "nixosModules" {}) false; - nixos-options = readOptions ( - { - "nixos" = import "${builtins.fetchTarball { url = flake; }}/nixos/modules/module-list.nix"; - } - ) true; + options = readFlakeOptions; + nixos-options = readOptions { + module = import "${builtins.fetchTarball { url = flake; }}/nixos/modules/module-list.nix"; + }; all = packages ++ apps ++ options; } diff --git a/flake-info/src/data/export.rs b/flake-info/src/data/export.rs index ad55480..74e6f18 100644 --- a/flake-info/src/data/export.rs +++ b/flake-info/src/data/export.rs @@ -3,7 +3,7 @@ /// Flakes, or Nixpkgs. use std::{convert::TryInto, path::PathBuf}; -use super::{import::DocValue, pandoc::PandocExt}; +use super::{import::{DocValue, ModulePath}, pandoc::PandocExt}; use crate::data::import::NixOption; use log::error; use serde::{Deserialize, Serialize}; @@ -110,7 +110,7 @@ pub enum Derivation { option_example: Option, - option_flake: Option<(String, String)>, + option_flake: Option, }, } diff --git a/flake-info/src/data/import.rs b/flake-info/src/data/import.rs index 599e01a..5ece055 100644 --- a/flake-info/src/data/import.rs +++ b/flake-info/src/data/import.rs @@ -63,8 +63,18 @@ pub struct NixOption { pub default: Option, pub example: Option, - /// If defined in a flake, contains defining flake and module - pub flake: Option<(String, String)>, + /// If defined in a flake, contains defining flake and optionally a module + pub flake: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ModulePath { + /// A module taken from .nixosModule + /// JSON representation is a list, therefore use a 1-Tuple as representation + DefaultModule((String,)), + /// A module taken from .nixosModules. + NamedModule((String, String)), } #[derive(Debug, Clone, PartialEq, Deserialize)] @@ -345,6 +355,32 @@ mod tests { .collect(); } + #[test] + fn test_flake_option() { + let json = r#" + { + "declarations": [], + "name": "test-option", + "flake": ["flake", "module"] + } + "#; + + serde_json::from_str::(json).unwrap(); + } + + #[test] + fn test_flake_option_default_module() { + let json = r#" + { + "declarations": [], + "name": "test-option", + "flake": ["flake"] + } + "#; + + serde_json::from_str::(json).unwrap(); + } + #[test] fn test_option_parsing() {} } diff --git a/src/Page/Options.elm b/src/Page/Options.elm index 949aeba..dc6dec1 100644 --- a/src/Page/Options.elm +++ b/src/Page/Options.elm @@ -68,7 +68,7 @@ type alias ResultItemSource = , source : Maybe String -- flake - , flake : Maybe ( String, String ) + , flake : Maybe (List String) , flakeName : Maybe String , flakeDescription : Maybe String , flakeUrl : Maybe String @@ -274,17 +274,20 @@ viewResultItem channel _ show item = Maybe.map asGithubLink item.source.source flakeOrNixpkgs = - case ( item.source.flakeName, item.source.flake, item.source.flakeUrl ) of + let + mkLink flake url = + a [ href url ] [ text flake ] + in + case ( item.source.flake, item.source.flakeUrl ) of -- its a flake - ( Just name, Just ( flake, attr ), Just flakeUrl_ ) -> + ( Just (flake :: []), Just url ) -> Just - [ li [] - [ a [ href flakeUrl_ ] [ text flake ] - , text "#" - , text attr - ] + [ li [] [ mkLink flake url ] ] + ( Just (flake :: moduleName :: []), Just url ) -> + Just [ li [] [ mkLink flake url, text "#", text moduleName ] ] + _ -> Nothing in @@ -344,12 +347,16 @@ findSource channel source = flakeOrNixpkgs = case ( source.flake, source.flakeUrl ) of -- its a flake - ( Just ( name, module_ ), Just flakeUrl_ ) -> + ( Just (name :: attrs), Just flakeUrl_ ) -> + let + module_ = + Maybe.withDefault "(default)" <| Maybe.map (\m -> "(Module: " ++ m ++ ")") <| List.head attrs + in Just <| List.append (Maybe.withDefault [] <| Maybe.map (\sourceFile_ -> [ sourceFile_, span [] [ text " in " ] ]) sourceFile) [ span [] [ text "Flake: " ] - , a [ href flakeUrl_ ] [ text <| name ++ "(Module: " ++ module_ ++ ")" ] + , a [ href flakeUrl_ ] [ text <| name ++ module_ ] ] ( Nothing, _ ) -> @@ -420,7 +427,7 @@ decodeResultItemSource = |> Json.Decode.Pipeline.optional "option_example" (Json.Decode.map Just Json.Decode.string) Nothing |> Json.Decode.Pipeline.optional "option_source" (Json.Decode.map Just Json.Decode.string) Nothing |> Json.Decode.Pipeline.optional "option_flake" - (Json.Decode.map Just <| Json.Decode.map2 Tuple.pair (Json.Decode.index 0 Json.Decode.string) (Json.Decode.index 1 Json.Decode.string)) + (Json.Decode.map Just <| Json.Decode.list Json.Decode.string) Nothing |> Json.Decode.Pipeline.optional "flake_name" (Json.Decode.map Just Json.Decode.string) Nothing |> Json.Decode.Pipeline.optional "flake_description" (Json.Decode.map Just Json.Decode.string) Nothing