*: add default.nix/readTree

This makes all Nix files addressable from root by file path.

For instance, if a file is located in //foo/bar:baz.nix containing:

    { pkgs, ... }:

    pkgs.stdenv.mkDerivation {
      pname = "foo";
      # ...
    }

You can then do:

    nix-build -A foo.bar.baz

All nix files loaded this way must be a function taking a 'config'
attrset - see nix/readTree.nix for more information. Currently the
config attrset contains the following fields:

 - hscloud: the root of the hscloud repository itself, which allows
            for traversal via readTree (eg. hscloud.foo.bar.baz)
 - pkgs: nixpkgs
 - pkgsSrc: nixpkgs souce/channel, useful to load NixOS modules.
 - lib, stdenv: lib and stdenv from pkgs.

Change-Id: Ieaacdcabceec18dd6c670d346928bff08b66cf79
master
q3k 2020-10-03 00:13:45 +02:00 committed by q3k
parent fbe234bdb2
commit 2efb698d22
2 changed files with 133 additions and 0 deletions

37
default.nix Normal file
View File

@ -0,0 +1,37 @@
{ ... }@args:
with builtins;
let
fix = f: let x = f x; in x;
readTree = import ./nix/readtree.nix {};
# Tracking nixos-unstable as of 2020-08-22.
nixpkgsCommit = "c59ea8b8a0e7f927e7291c14ea6cd1bd3a16ff38";
nixpkgsSrc = fetchTarball {
url = "https://github.com/NixOS/nixpkgs-channels/archive/${nixpkgsCommit}.tar.gz";
sha256 = "1ak7jqx94fjhc68xh1lh35kh3w3ndbadprrb762qgvcfb8351x8v";
};
nixpkgs = import nixpkgsSrc {
config.allowUnfree = true;
config.allowBroken = true;
};
in fix (self: rec {
config = {
hscloud = self // {
root = ./.;
};
pkgs = nixpkgs;
pkgsSrc = nixpkgsSrc;
inherit (nixpkgs) lib stdenv;
};
bgpwtf = readTree config ./bgpwtf;
cluster = readTree config ./cluster;
ops = readTree config ./ops;
pkgs = nixpkgs;
})

96
nix/readtree.nix Normal file
View File

@ -0,0 +1,96 @@
# The MIT License (MIT)
#
# Copyright (c) 2019 Vincent Ambo
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
{ ... }:
args: initPath:
let
inherit (builtins)
attrNames
baseNameOf
filter
hasAttr
head
isAttrs
length
listToAttrs
map
match
readDir
substring;
argsWithPath = parts:
let meta.locatedAt = parts;
in meta // (if isAttrs args then args else args meta);
readDirVisible = path:
let
children = readDir path;
isVisible = f: f == ".skip-subtree" || (substring 0 1 f) != ".";
names = filter isVisible (attrNames children);
in listToAttrs (map (name: {
inherit name;
value = children.${name};
}) names);
# The marker is added to every set that was imported directly by
# readTree.
importWithMark = path: parts:
let imported = import path (argsWithPath parts);
in if (isAttrs imported)
then imported // { __readTree = true; }
else imported;
nixFileName = file:
let res = match "(.*)\.nix" file;
in if res == null then null else head res;
readTree = path: parts:
let
dir = readDirVisible path;
self = importWithMark path parts;
joinChild = c: path + ("/" + c);
# Import subdirectories of the current one, unless the special
# `.skip-subtree` file exists which makes readTree ignore the
# children.
#
# This file can optionally contain information on why the tree
# should be ignored, but its content is not inspected by
# readTree
filterDir = f: dir."${f}" == "directory";
children = if hasAttr ".skip-subtree" dir then [] else map (c: {
name = c;
value = readTree (joinChild c) (parts ++ [ c ]);
}) (filter filterDir (attrNames dir));
# Import Nix files
nixFiles = filter (f: f != null) (map nixFileName (attrNames dir));
nixChildren = map (c: let p = joinChild (c + ".nix"); in {
name = c;
value = importWithMark p (parts ++ [ c ]);
}) nixFiles;
in if dir ? "default.nix"
then (if isAttrs self then self // (listToAttrs children) else self)
else listToAttrs (nixChildren ++ children);
in readTree initPath [ (baseNameOf initPath) ]