From 39ebd5001ebcbcc9d991784ec1ce6dd804dbdcd4 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 02:15:58 +0200
Subject: [PATCH 01/46] getAttrDef: RIP

---
 krebs/4lib/default.nix | 1 -
 krebs/4lib/types.nix   | 5 +++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/krebs/4lib/default.nix b/krebs/4lib/default.nix
index 585bd313f..e984614a0 100644
--- a/krebs/4lib/default.nix
+++ b/krebs/4lib/default.nix
@@ -41,7 +41,6 @@ let out = rec {
     mapAttrs (name: _: path + "/${name}")
              (filterAttrs (_: eq "directory") (readDir path));
 
-  getAttrDef = name: set: set.${name} or set.default or null;
   mapAttrValues = f: mapAttrs (_: f);
   setAttr = name: value: set: set // { ${name} = value; };
 
diff --git a/krebs/4lib/types.nix b/krebs/4lib/types.nix
index 66191d0b3..f78d601e3 100644
--- a/krebs/4lib/types.nix
+++ b/krebs/4lib/types.nix
@@ -199,8 +199,9 @@ types // rec {
         description = ''
           Set of user's PGP public keys.
 
-          Modules supporting PGP may use well-known key names to define option
-          defaults, e.g. using `getAttrDef well-known-name pubkeys`.
+          Modules supporting PGP may use well-known key names to define
+          default values for options, in which case the well-known name
+          should be documented in the respective option's description.
         '';
       };
       pubkey = mkOption {

From b5bdd9aed4530924bca3d515eedeed215cfd64c2 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 03:11:26 +0200
Subject: [PATCH 02/46] krebs.git.cgit :: { bool => submodule { enable :: bool
 } }

---
 krebs/3modules/git.nix | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index d2d73ba3d..542f1f388 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -13,7 +13,7 @@ let
   out = {
     options.krebs.git = api;
     config = with lib; mkIf cfg.enable (mkMerge [
-      (mkIf cfg.cgit cgit-imp)
+      (mkIf cfg.cgit.enable cgit-imp)
       git-imp
     ]);
   };
@@ -22,10 +22,13 @@ let
     enable = mkEnableOption "krebs.git";
 
     cgit = mkOption {
-      type = types.bool;
-      default = true;
+      type = types.submodule {
+        options = {
+          enable = mkEnableOption "krebs.git.cgit" // { default = true; };
+        };
+      };
+      default = {};
       description = ''
-          Enable cgit.
           Cgit is an attempt to create a fast web interface for the git version
           control system, using a built in cache to decrease pressure on the
           git server.

From 6fcc35afb0003f0885994b3c09e401f3178d7a08 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 22:36:40 +0200
Subject: [PATCH 03/46] krebs types.uint: init

---
 krebs/4lib/types.nix | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/krebs/4lib/types.nix b/krebs/4lib/types.nix
index f78d601e3..d4d28bc78 100644
--- a/krebs/4lib/types.nix
+++ b/krebs/4lib/types.nix
@@ -154,6 +154,12 @@ types // rec {
     merge = mergeOneOption;
   };
 
+  uint = mkOptionType {
+    name = "unsigned integer";
+    check = x: isInt x && x >= 0;
+    merge = mergeOneOption;
+  };
+
   secret-file = submodule ({ config, ... }: {
     options = {
       path = mkOption { type = str; };

From 4a34b27c1c6c3fea2b336c0316c597d74460b428 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 03:14:21 +0200
Subject: [PATCH 04/46] krebs.git.cgit: make `cache-root` configurable

... along with all the other stuff :)
---
 krebs/3modules/git.nix                 | 189 ++++++++++++++++---------
 krebs/4lib/default.nix                 |   1 -
 lass/2configs/git.nix                  |  22 +--
 makefu/2configs/git/brain-retiolum.nix |  14 +-
 makefu/2configs/git/cgit-retiolum.nix  |  26 ++--
 miefda/2configs/git.nix                |  18 ++-
 mv/2configs/git.nix                    |  12 +-
 shared/2configs/cgit-mirror.nix        |  10 +-
 tv/2configs/git.nix                    |  28 ++--
 9 files changed, 200 insertions(+), 120 deletions(-)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index 542f1f388..d1ab2ce64 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -25,6 +25,11 @@ let
       type = types.submodule {
         options = {
           enable = mkEnableOption "krebs.git.cgit" // { default = true; };
+          settings = mkOption {
+            apply = flip removeAttrs ["_module"];
+            default = {};
+            type = subtypes.cgit-settings;
+          };
         };
       };
       default = {};
@@ -66,22 +71,6 @@ let
         Repositories.
       '';
     };
-    root-desc = mkOption {
-      type = types.nullOr types.str;
-      default = null;
-      description = ''
-        Text printed below the heading on the repository index page.
-        Default value: "a fast webinterface for the git dscm".
-      '';
-    };
-    root-title = mkOption {
-      type = types.nullOr types.str;
-      default = null;
-      description = ''
-        Text printed as heading on the repository index page.
-        Default value: "Git Repository Browser".
-      '';
-    };
     rules = mkOption {
       type = types.listOf subtypes.rule;
       default = [];
@@ -102,8 +91,101 @@ let
 
   # TODO put into krebs/4lib/types.nix?
   subtypes = {
-    repo = types.submodule ({
+    cgit-settings = types.submodule {
+      # A setting's value of `null` means cgit's default should be used.
       options = {
+        cache-root = mkOption {
+          type = types.absolute-pathname;
+          default = "/tmp/cgit";
+        };
+        cache-size = mkOption {
+          type = types.uint;
+          default = 1000;
+        };
+        css = mkOption {
+          type = types.absolute-pathname;
+          default = "/static/cgit.css";
+        };
+        enable-commit-graph = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        enable-index-links = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        enable-index-owner = mkOption {
+          type = types.bool;
+          default = false;
+        };
+        enable-log-filecount = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        enable-log-linecount = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        enable-remote-branches = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        logo = mkOption {
+          type = types.absolute-pathname;
+          default = "/static/cgit.png";
+        };
+        max-stats = mkOption {
+          type =
+            types.nullOr (types.enum ["week" "month" "quarter" "year"]);
+          default = "year";
+        };
+        robots = mkOption {
+          type = types.nullOr (types.listOf types.str);
+          default = ["nofollow" "noindex"];
+        };
+        root-desc = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+        };
+        root-title = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+        };
+      };
+    };
+    repo = types.submodule ({ config, ... }: {
+      options = {
+        cgit = {
+          desc = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            description = ''
+              Repository description.
+            '';
+          };
+          path = mkOption {
+            type = types.str;
+            default = "${cfg.dataDir}/${config.name}";
+            description = ''
+              An absolute path to the repository directory. For non-bare
+              repositories this is the .git-directory.
+            '';
+          };
+          section = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            description = ''
+              Repository section.
+            '';
+          };
+          url = mkOption {
+            type = types.str;
+            default = config.name;
+            description = ''
+              The relative url used to access the repository.
+            '';
+          };
+        };
         collaborators = mkOption {
           type = types.listOf types.user;
           default = [];
@@ -115,20 +197,6 @@ let
             an example.
           '';
         };
-        desc = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          description = ''
-            Repository description.
-          '';
-        };
-        section = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          description = ''
-            Repository section.
-          '';
-        };
         name = mkOption {
           type = types.str;
           description = ''
@@ -266,43 +334,34 @@ let
       # socketType = "unix" (default)
     };
 
-    environment.etc."cgitrc".text = ''
-      css=/static/cgit.css
-      logo=/static/cgit.png
+    environment.etc."cgitrc".text = let
+      repo-to-cgitrc = _: repo:
+        optionals (isPublicRepo repo) (concatLists [
+          [""] # empty line
+          [(kv-to-cgitrc "repo.url" repo.cgit.url)]
+          (mapAttrsToList kv-to-cgitrc
+            (mapAttrs' (k: nameValuePair "repo.${k}")
+              (removeAttrs repo.cgit ["url"])))
+        ]);
 
-      # if you do not want that webcrawler (like google) index your site
-      robots=noindex, nofollow
-
-      virtual-root=/
-
-      # TODO make this nicer (and/or somewhere else)
-      cache-root=/tmp/cgit
-
-      cache-size=1000
-      enable-commit-graph=1
-      enable-index-links=1
-      enable-index-owner=0
-      enable-log-filecount=1
-      enable-log-linecount=1
-      enable-remote-branches=1
-
-      ${optionalString (cfg.root-title != null) "root-title=${cfg.root-title}"}
-      ${optionalString (cfg.root-desc != null) "root-desc=${cfg.root-desc}"}
-
-      snapshots=0
-      max-stats=year
-
-      ${concatMapStringsSep "\n" (repo: ''
-        repo.url=${repo.name}
-        repo.path=${cfg.dataDir}/${repo.name}
-        ${optionalString (repo.section != null) "repo.section=${repo.section}"}
-        ${optionalString (repo.desc != null) "repo.desc=${repo.desc}"}
-      '') (filter isPublicRepo (attrValues cfg.repos))}
-    '';
+      kv-to-cgitrc = k: v: getAttr (typeOf v) {
+        bool = kv-to-cgitrc k (if v then 1 else 0);
+        null = []; # This will be removed by `flatten`.
+        list = "${k}=${concatStringsSep ", " v}";
+        int = "${k}=${toString v}";
+        string = "${k}=${v}";
+      };
+    in
+      concatStringsSep "\n"
+        (flatten (
+          mapAttrsToList kv-to-cgitrc cfg.cgit.settings
+          ++
+          mapAttrsToList repo-to-cgitrc cfg.repos
+        ));
 
     system.activationScripts.cgit = ''
-      mkdir -m 0700 -p /tmp/cgit
-      chown ${toString fcgitwrap-user.uid}:${toString fcgitwrap-group.gid} /tmp/cgit
+      mkdir -m 0700 -p ${cfg.cgit.settings.cache-root}
+      chown ${toString fcgitwrap-user.uid}:${toString fcgitwrap-group.gid} ${cfg.cgit.settings.cache-root}
     '';
 
     krebs.nginx = {
diff --git a/krebs/4lib/default.nix b/krebs/4lib/default.nix
index e984614a0..bfe8c581c 100644
--- a/krebs/4lib/default.nix
+++ b/krebs/4lib/default.nix
@@ -41,7 +41,6 @@ let out = rec {
     mapAttrs (name: _: path + "/${name}")
              (filterAttrs (_: eq "directory") (readDir path));
 
-  mapAttrValues = f: mapAttrs (_: f);
   setAttr = name: value: set: set // { ${name} = value; };
 
   optionalTrace = c: msg: x: if c then trace msg x else x;
diff --git a/lass/2configs/git.nix b/lass/2configs/git.nix
index aac3f6e02..c0affe981 100644
--- a/lass/2configs/git.nix
+++ b/lass/2configs/git.nix
@@ -7,8 +7,12 @@ let
   out = {
     krebs.git = {
       enable = true;
-      root-title = "public repositories at ${config.krebs.build.host.name}";
-      root-desc = "keep calm and engage";
+      cgit = {
+        settings = {
+          root-title = "public repositories at ${config.krebs.build.host.name}";
+          root-desc = "keep calm and engage";
+        };
+      };
       repos = mapAttrs (_: s: removeAttrs s ["collaborators"]) repos;
       rules = rules;
     };
@@ -27,7 +31,7 @@ let
   public-repos = mapAttrs make-public-repo {
     painload = {};
     stockholm = {
-      desc = "take all the computers hostage, they'll love you!";
+      cgit.desc = "take all the computers hostage, they'll love you!";
     };
     wai-middleware-time = {};
     web-routes-wai-custom = {};
@@ -52,8 +56,8 @@ let
     import <secrets/repos.nix> { inherit config lib pkgs; }
   );
 
-  make-public-repo = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-public-repo = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -66,13 +70,13 @@ let
     };
   };
 
-  make-public-repo-silent = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-public-repo-silent = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
   };
 
-  make-restricted-repo = name: { collaborators ? [], desc ? null, ... }: {
-    inherit name collaborators desc;
+  make-restricted-repo = name: { collaborators ? [], ... }: {
+    inherit collaborators name;
     public = false;
   };
 
diff --git a/makefu/2configs/git/brain-retiolum.nix b/makefu/2configs/git/brain-retiolum.nix
index 80e4c87cf..ae54c6dbf 100644
--- a/makefu/2configs/git/brain-retiolum.nix
+++ b/makefu/2configs/git/brain-retiolum.nix
@@ -7,9 +7,7 @@ let
   rules = concatMap krebs-rules (attrValues krebs-repos) ++ concatMap priv-rules (attrValues priv-repos);
 
   krebs-repos = mapAttrs make-krebs-repo {
-    brain = {
-      desc = "braiiiins";
-    };
+    brain = { };
   };
 
   priv-repos = mapAttrs make-priv-repo {
@@ -18,13 +16,13 @@ let
   };
 
   # TODO move users to separate module
-  make-priv-repo = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-priv-repo = name: { ... }: {
+    inherit name;
     public = false;
   };
 
-  make-krebs-repo = with git; name: { desc ? null, ... }: {
-    inherit name desc;
+  make-krebs-repo = with git; name: { ... }: {
+    inherit name;
     public = false;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -63,7 +61,7 @@ in {
   imports = [ ];
   krebs.git = {
     enable = true;
-    cgit = false;
+    cgit.enable = false;
     inherit repos rules;
   };
 }
diff --git a/makefu/2configs/git/cgit-retiolum.nix b/makefu/2configs/git/cgit-retiolum.nix
index 44d759488..fe2c850f7 100644
--- a/makefu/2configs/git/cgit-retiolum.nix
+++ b/makefu/2configs/git/cgit-retiolum.nix
@@ -10,17 +10,17 @@ let
 
   krebs-repos = mapAttrs make-krebs-repo {
     stockholm = {
-      desc = "Make all the systems into 1systems!";
+      cgit.desc = "Make all the systems into 1systems!";
     };
     tinc_graphs = {
-      desc = "Tinc Advanced Graph Generation";
+      cgit.desc = "Tinc Advanced Graph Generation";
     };
     stockholm-init = {
-      desc = "Build new Stockholm hosts";
+      cgit.desc = "Build new Stockholm hosts";
     };
     cac-api = { };
     init-stockholm = {
-      desc = "Init stuff for stockholm";
+      cgit.desc = "Init stuff for stockholm";
     };
   };
 
@@ -32,19 +32,19 @@ let
     connector = { };
     minikrebs = { };
     mattermost = {
-      desc = "Mattermost Docker files";
+      cgit.desc = "Mattermost Docker files";
     };
   };
 
 
   # TODO move users to separate module
-  make-priv-repo = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-priv-repo = name: { ... }: {
+    inherit name;
     public = false;
   };
 
-  make-krebs-repo = with git; name: { desc ? null, ... }: {
-    inherit name desc;
+  make-krebs-repo = with git; name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -88,8 +88,12 @@ let
 in {
   krebs.git = {
     enable = true;
-    root-title = "public repositories";
-    root-desc = "keep on krebsing";
+    cgit = {
+      settings = {
+        root-title = "public repositories";
+        root-desc = "keep on krebsing";
+      };
+    };
     inherit repos rules;
   };
 }
diff --git a/miefda/2configs/git.nix b/miefda/2configs/git.nix
index 052cc4ab2..51679d2a5 100644
--- a/miefda/2configs/git.nix
+++ b/miefda/2configs/git.nix
@@ -7,8 +7,12 @@ let
   out = {
     krebs.git = {
       enable = true;
-      root-title = "public repositories at ${config.krebs.build.host.name}";
-      root-desc = "keep calm and engage";
+      cgit = {
+        settings = {
+          root-title = "public repositories at ${config.krebs.build.host.name}";
+          root-desc = "keep calm and engage";
+        };
+      };
       repos = mapAttrs (_: s: removeAttrs s ["collaborators"]) repos;
       rules = rules;
     };
@@ -27,7 +31,7 @@ let
   public-repos = mapAttrs make-public-repo {
     painload = {};
     stockholm = {
-      desc = "take all the computers hostage, they'll love you!";
+      cgit.desc = "take all the computers hostage, they'll love you!";
     };
     #wai-middleware-time = {};
     #web-routes-wai-custom = {};
@@ -46,8 +50,8 @@ let
     import <secrets/repos.nix> { inherit config lib pkgs; }
   );
 
-  make-public-repo = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-public-repo = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -60,8 +64,8 @@ let
     };
   };
 
-  make-restricted-repo = name: { collaborators ? [], desc ? null, ... }: {
-    inherit name collaborators desc;
+  make-restricted-repo = name: { collaborators ? [], ... }: {
+    inherit collaborators name;
     public = false;
   };
 
diff --git a/mv/2configs/git.nix b/mv/2configs/git.nix
index 933649f20..aee448cb6 100644
--- a/mv/2configs/git.nix
+++ b/mv/2configs/git.nix
@@ -7,8 +7,12 @@ let
   out = {
     krebs.git = {
       enable = true;
-      root-title = "public repositories at ${config.krebs.build.host.name}";
-      root-desc = "Hmhmh, im Moment nicht.";
+      cgit = {
+        settings = {
+          root-title = "public repositories at ${config.krebs.build.host.name}";
+          root-desc = "Hmhmh, im Moment nicht.";
+        };
+      };
       repos = mapAttrs (_: s: removeAttrs s ["collaborators"]) repos;
       rules = rules;
     };
@@ -22,8 +26,8 @@ let
     stockholm = {};
   };
 
-  make-public-repo = name: { desc ? null, section ? null, ... }: {
-    inherit name desc section;
+  make-public-repo = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
diff --git a/shared/2configs/cgit-mirror.nix b/shared/2configs/cgit-mirror.nix
index b984535c9..d9241a2b5 100644
--- a/shared/2configs/cgit-mirror.nix
+++ b/shared/2configs/cgit-mirror.nix
@@ -11,7 +11,7 @@ let
   stockholm-mirror = {
     public = true;
     name = "stockholm-mirror";
-    desc = "mirror for all stockholm branches";
+    cgit.desc = "mirror for all stockholm branches";
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
         nick = config.networking.hostName;
@@ -33,8 +33,12 @@ in {
   krebs.users.wolf-repo-sync = wolf-repo-sync;
   krebs.git = {
     enable = true;
-    root-title = "Shared Repos";
-    root-desc = "keep on krebsing";
+    cgit = {
+      settings = {
+        root-title = "Shared Repos";
+        root-desc = "keep on krebsing";
+      };
+    };
     inherit rules;
     repos.stockholm-mirror = stockholm-mirror;
   };
diff --git a/tv/2configs/git.nix b/tv/2configs/git.nix
index 2e5fc301b..9bcf8f31f 100644
--- a/tv/2configs/git.nix
+++ b/tv/2configs/git.nix
@@ -7,8 +7,12 @@ let
   out = {
     krebs.git = {
       enable = true;
-      root-title = "repositories at ${config.krebs.build.host.name}";
-      root-desc = "mostly krebs";
+      cgit = {
+        settings = {
+          root-title = "repositories at ${config.krebs.build.host.name}";
+          root-desc = "mostly krebs";
+        };
+      };
       repos = repos;
       rules = rules;
     };
@@ -21,9 +25,9 @@ let
   rules = concatMap make-rules (attrValues repos);
 
   public-repos = mapAttrs make-public-repo ({
-  } // mapAttrValues (setAttr "section" "1. miscellaneous") {
+  } // mapAttrs (_: recursiveUpdate { cgit.section = "1. miscellaneous"; }) {
     cac-api = {
-      desc = "CloudAtCost API command line interface";
+      cgit.desc = "CloudAtCost API command line interface";
     };
     get = {};
     hack = {};
@@ -35,13 +39,13 @@ let
     push = {};
     regfish = {};
     soundcloud = {
-      desc = "SoundCloud command line interface";
+      cgit.desc = "SoundCloud command line interface";
     };
     stockholm = {
-      desc = "NixOS configuration";
+      cgit.desc = "NixOS configuration";
     };
     with-tmpdir = {};
-  } // mapAttrValues (setAttr "section" "2. Haskell libraries") {
+  } // mapAttrs (_: recursiveUpdate { cgit.section = "2. Haskell libraries"; }) {
     blessings = {};
     mime = {};
     quipper = {};
@@ -50,7 +54,7 @@ let
     web-routes-wai-custom = {};
     xintmap = {};
     xmonad-stockholm = {};
-  } // mapAttrValues (setAttr "section" "3. museum") {
+  } // mapAttrs (_: recursiveUpdate { cgit.section = "3. museum"; }) {
     cgserver = {};
     crude-mail-setup = {};
     dot-xmonad = {};
@@ -68,8 +72,8 @@ let
     import <secrets/repos.nix> { inherit config lib pkgs; }
   );
 
-  make-public-repo = name: { desc ? null, section ? null, ... }: {
-    inherit name desc section;
+  make-public-repo = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = optionalAttrs (config.krebs.build.host.name == "cd") {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -82,8 +86,8 @@ let
     };
   };
 
-  make-restricted-repo = name: { collaborators ? [], desc ? null, ... }: {
-    inherit name collaborators desc;
+  make-restricted-repo = name: { collaborators ? [], ... }: {
+    inherit collaborators name;
     public = false;
   };
 

From c655e1246daa04abc4cd83dbb62b86dda1d357d4 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 22:41:40 +0200
Subject: [PATCH 05/46] krebs.git: s/ensureList/toList/

---
 krebs/3modules/git.nix | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index d1ab2ce64..9bc56fc85 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -290,15 +290,15 @@ let
     environment.etc."${etc-base}".source =
       scriptFarm "git-ssh-authorizers" {
         authorize-command = makeAuthorizeScript (map (rule: [
-          (map getName (ensureList rule.user))
-          (map getName (ensureList rule.repo))
+          (map getName (toList rule.user))
+          (map getName (toList rule.repo))
           (map getName rule.perm.allow-commands)
         ]) cfg.rules);
 
         authorize-push = makeAuthorizeScript (map (rule: [
-          (map getName (ensureList rule.user))
-          (map getName (ensureList rule.repo))
-          (ensureList rule.perm.allow-receive-ref)
+          (map getName (toList rule.user))
+          (map getName (toList rule.repo))
+          (toList rule.perm.allow-receive-ref)
           (map getName rule.perm.allow-receive-modes)
         ]) (filter (rule: rule.perm.allow-receive-ref != null) cfg.rules));
       };
@@ -400,10 +400,6 @@ let
     gid = fcgitwrap-user.uid;
   };
 
-
-  ensureList = x:
-    if typeOf x == "list" then x else [x];
-
   getName = x: x.name;
 
   isPublicRepo = getAttr "public"; # TODO this is also in ./cgit.nix
@@ -428,7 +424,7 @@ let
   makeAuthorizeScript =
     let
       # TODO escape
-      to-pattern = x: concatStringsSep "|" (ensureList x);
+      to-pattern = x: concatStringsSep "|" (toList x);
       go = i: ps:
         if ps == []
           then "exit 0"

From a3644a38a5f8af779d7db64cdaa6468240ae37d3 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 22:49:26 +0200
Subject: [PATCH 06/46] krebs.git: make user configurable

---
 krebs/3modules/git.nix | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index 9bc56fc85..aabf4614f 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -87,6 +87,14 @@ let
         access and permission rules for git repositories.
       '';
     };
+
+    user = mkOption {
+      type = types.user;
+      default = {
+        name = "git";
+        home = "/var/lib/git";
+      };
+    };
   };
 
   # TODO put into krebs/4lib/types.nix?
@@ -303,16 +311,15 @@ let
         ]) (filter (rule: rule.perm.allow-receive-ref != null) cfg.rules));
       };
 
-    # TODO cfg.user
-    users.users.git = rec {
+    users.users.${cfg.user.name} = {
+      inherit (cfg.user) home name uid;
+      createHome = true;
       description = "Git repository hosting user";
-      name = "git";
       shell = "/bin/sh";
       openssh.authorizedKeys.keys =
         mapAttrsToList (_: makeAuthorizedKey git-ssh-command)
                        (filterAttrs (_: user: isString user.pubkey)
                                     config.krebs.users);
-      uid = genid name;
     };
   };
 

From f90f8dc0004097f5f023ea47104b54dcd740e014 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 22:51:35 +0200
Subject: [PATCH 07/46] empty: init at 1.0.0

---
 krebs/5pkgs/default.nix | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/krebs/5pkgs/default.nix b/krebs/5pkgs/default.nix
index 53fc4de44..f2bbaf7f4 100644
--- a/krebs/5pkgs/default.nix
+++ b/krebs/5pkgs/default.nix
@@ -20,6 +20,8 @@ with config.krebs.lib;
               (filterAttrs (_: dir.has-default-nix)
                            (subdirsOf ./.))
   // {
+    empty = pkgs.runCommand "empty-1.0.0" {} "mkdir $out";
+
     haskellPackages = pkgs.haskellPackages.override {
       overrides = self: super:
         mapAttrs (name: path: self.callPackage path {})

From 922389ef205825163eb5b4e606b82a65deaa05c2 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:02:37 +0200
Subject: [PATCH 08/46] krebs.git.cgit.fcgiwrap: make user configurable

---
 krebs/3modules/git.nix | 47 +++++++++++++++++++++++-------------------
 1 file changed, 26 insertions(+), 21 deletions(-)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index aabf4614f..0d12155f4 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -25,6 +25,21 @@ let
       type = types.submodule {
         options = {
           enable = mkEnableOption "krebs.git.cgit" // { default = true; };
+          fcgiwrap = {
+            group = mkOption {
+              type = types.group;
+              default = {
+                name = "fcgiwrap";
+              };
+            };
+            user = mkOption {
+              type = types.user;
+              default = {
+                name = "fcgiwrap";
+                home = toString pkgs.empty;
+              };
+            };
+          };
           settings = mkOption {
             apply = flip removeAttrs ["_module"];
             default = {};
@@ -324,19 +339,20 @@ let
   };
 
   cgit-imp = {
-    users.extraUsers = lib.singleton {
-      inherit (fcgitwrap-user) group name uid;
-      home = toString (pkgs.runCommand "empty" {} "mkdir -p $out");
-    };
-
-    users.extraGroups = lib.singleton {
-      inherit (fcgitwrap-group) gid name;
+    users = {
+      groups.${cfg.cgit.fcgiwrap.group.name} = {
+        inherit (cfg.cgit.fcgiwrap.group) name gid;
+      };
+      users.${cfg.cgit.fcgiwrap.user.name} = {
+        inherit (cfg.cgit.fcgiwrap.user) home name uid;
+        group = cfg.cgit.fcgiwrap.group.name;
+      };
     };
 
     services.fcgiwrap = {
       enable = true;
-      user = fcgitwrap-user.name;
-      group = fcgitwrap-user.group;
+      user = cfg.cgit.fcgiwrap.user.name;
+      group = cfg.cgit.fcgiwrap.group.name;
       # socketAddress = "/run/fcgiwrap.sock" (default)
       # socketType = "unix" (default)
     };
@@ -368,7 +384,7 @@ let
 
     system.activationScripts.cgit = ''
       mkdir -m 0700 -p ${cfg.cgit.settings.cache-root}
-      chown ${toString fcgitwrap-user.uid}:${toString fcgitwrap-group.gid} ${cfg.cgit.settings.cache-root}
+      chown ${toString cfg.cgit.fcgiwrap.user.uid}:${toString cfg.cgit.fcgiwrap.group.gid} ${cfg.cgit.settings.cache-root}
     '';
 
     krebs.nginx = {
@@ -396,17 +412,6 @@ let
     };
   };
 
-  fcgitwrap-user = rec {
-    name = "fcgiwrap";
-    uid = genid name;
-    group = "fcgiwrap";
-  };
-
-  fcgitwrap-group = {
-    name = fcgitwrap-user.name;
-    gid = fcgitwrap-user.uid;
-  };
-
   getName = x: x.name;
 
   isPublicRepo = getAttr "public"; # TODO this is also in ./cgit.nix

From c8b0a57549ce2d340d7e513a33683e26fb5e7ddb Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:08:55 +0200
Subject: [PATCH 09/46] krebs.git.cgit: add server name for cgit.*.r

---
 krebs/3modules/git.nix | 1 +
 1 file changed, 1 insertion(+)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index 0d12155f4..357674a6a 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -392,6 +392,7 @@ let
       servers.cgit = {
         server-names = [
           "cgit.${config.networking.hostName}"
+          "cgit.${config.networking.hostName}.r"
           "cgit.${config.networking.hostName}.retiolum"
         ];
         locations = [

From 78dfd1fee8af963eb80d5fc71dcb52c6199a27d9 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:10:27 +0200
Subject: [PATCH 10/46] cgit: RIP

---
 krebs/5pkgs/cgit/default.nix | 64 ------------------------------------
 1 file changed, 64 deletions(-)
 delete mode 100644 krebs/5pkgs/cgit/default.nix

diff --git a/krebs/5pkgs/cgit/default.nix b/krebs/5pkgs/cgit/default.nix
deleted file mode 100644
index 3180a5bd3..000000000
--- a/krebs/5pkgs/cgit/default.nix
+++ /dev/null
@@ -1,64 +0,0 @@
-{ stdenv, fetchurl, openssl, zlib, asciidoc, libxml2, libxslt
-, docbook_xml_xslt, pkgconfig, luajit
-, gzip, bzip2, xz
-}:
-
-stdenv.mkDerivation rec {
-  name = "cgit-${version}";
-  version = "0.12";
-
-  src = fetchurl {
-    url = "http://git.zx2c4.com/cgit/snapshot/${name}.tar.xz";
-    sha256 = "1dx54hgfyabmg9nm5qp6d01f54nlbqbbdwhwl0llb9imjf237qif";
-  };
-
-  # cgit is tightly coupled with git and needs a git source tree to build.
-  # IMPORTANT: Remember to check which git version cgit needs on every version
-  # bump (look in the Makefile).
-  # NOTE: as of 0.10.1, the git version is compatible from 1.9.0 to
-  # 1.9.2 (see the repository history)
-  gitSrc = fetchurl {
-    url    = "mirror://kernel/software/scm/git/git-2.7.2.tar.xz";
-    sha256 = "086ga30ksijfxad085ply83ddf955d2b8qxph5sw6c9hab77j15j";
-  };
-
-  buildInputs = [
-    openssl zlib asciidoc libxml2 libxslt docbook_xml_xslt pkgconfig luajit
-  ];
-
-  postPatch = ''
-    sed -e 's|"gzip"|"${gzip}/bin/gzip"|' \
-        -e 's|"bzip2"|"${bzip2}/bin/bzip2"|' \
-        -e 's|"xz"|"${xz}/bin/xz"|' \
-        -i ui-snapshot.c
-  '';
-
-  # Give cgit a git source tree and pass configuration parameters (as make
-  # variables).
-  preBuild = ''
-    mkdir -p git
-    tar --strip-components=1 -xf "$gitSrc" -C git
-
-    makeFlagsArray+=(prefix="$out" CGIT_SCRIPT_PATH="$out/cgit/")
-  '';
-
-  # Install manpage.
-  postInstall = ''
-    # xmllint fails:
-    #make install-man
-
-    # bypassing xmllint works:
-    a2x --no-xmllint -f manpage cgitrc.5.txt
-    mkdir -p "$out/share/man/man5"
-    cp cgitrc.5 "$out/share/man/man5"
-  '';
-
-  meta = {
-    homepage = http://git.zx2c4.com/cgit/about/;
-    repositories.git = git://git.zx2c4.com/cgit;
-    description = "Web frontend for git repositories";
-    license = stdenv.lib.licenses.gpl2;
-    platforms = stdenv.lib.platforms.linux;
-    maintainers = with stdenv.lib.maintainers; [ bjornfor ];
-  };
-}

From 240d338bc587866b5d6dc4f6253221ffb010132b Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:11:03 +0200
Subject: [PATCH 11/46] tv nixpkgs: 40c586b -> 87fe38f

---
 tv/2configs/default.nix | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tv/2configs/default.nix b/tv/2configs/default.nix
index df5d5da29..741955eee 100644
--- a/tv/2configs/default.nix
+++ b/tv/2configs/default.nix
@@ -14,7 +14,7 @@ with config.krebs.lib;
       stockholm = "/home/tv/stockholm";
       nixpkgs = {
         url = https://github.com/NixOS/nixpkgs;
-        rev = "40c586b7ce2c559374df435f46d673baf711c543";
+        rev = "87fe38fd0e19ca83fc3ea338f8e0e7b12971d204";
       };
     } // optionalAttrs config.krebs.build.host.secure {
       secrets-master = "/home/tv/secrets/master";

From 72956b5d719d953bab5490f0553e3351118ec01f Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:12:18 +0200
Subject: [PATCH 12/46] tv q: use saner colors for calendar header and week
 numbers

---
 tv/5pkgs/q/default.nix | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tv/5pkgs/q/default.nix b/tv/5pkgs/q/default.nix
index 1d08a5447..eeb600300 100644
--- a/tv/5pkgs/q/default.nix
+++ b/tv/5pkgs/q/default.nix
@@ -13,10 +13,10 @@ let
         | ${pkgs.gawk}/bin/awk '{printf "%-23s\n", $0}' \
         | ${pkgs.gnused}/bin/sed '
               # colorize header
-              1,2s/.*/&/
+              1,2s/.*/&/
 
               # colorize week number
-              s/^[ 1-9][0-9]/&/
+              s/^[ 1-9][0-9]/&/
             '
     }'';
   in ''

From 5e91e789b66350302b3a5f90843d4a10f4fd2c75 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:22:12 +0200
Subject: [PATCH 13/46] krebs types.absolute-pathname: admit /

---
 krebs/4lib/types.nix | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/krebs/4lib/types.nix b/krebs/4lib/types.nix
index d4d28bc78..f65d5b68e 100644
--- a/krebs/4lib/types.nix
+++ b/krebs/4lib/types.nix
@@ -337,7 +337,7 @@ types // rec {
   # TODO two slashes
   absolute-pathname = mkOptionType {
     name = "POSIX absolute pathname";
-    check = s: pathname.check s && substring 0 1 s == "/";
+    check = s: s == "/" || (pathname.check s && substring 0 1 s == "/");
   };
 
   # POSIX.1‐2013, 3.267 Pathname

From 567800c98e7f17ead15aa99f388d4a91c2f58653 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:23:06 +0200
Subject: [PATCH 14/46] krebs.git.cgit.settings.virtual-root: init

---
 krebs/3modules/git.nix | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index 357674a6a..4fec38579 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -174,6 +174,10 @@ let
           type = types.nullOr types.str;
           default = null;
         };
+        virtual-root = mkOption {
+          type = types.nullOr types.absolute-pathname;
+          default = "/";
+        };
       };
     };
     repo = types.submodule ({ config, ... }: {

From a431e036e335d688aa8e59e039dd86fa1eefdc2a Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:32:28 +0200
Subject: [PATCH 15/46] krebs.git: move assert to etcDir's type

---
 krebs/3modules/git.nix | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index 4fec38579..db0b6b0ab 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -63,7 +63,11 @@ let
       description = "Directory used to store repositories.";
     };
     etcDir = mkOption {
-      type = types.str;
+      type = mkOptionType {
+        name = "${types.absolute-pathname.name} starting with `/etc/'";
+        check = x: types.absolute-pathname.check x && hasPrefix "/etc/" x;
+        merge = mergeOneOption;
+      };
       default = "/etc/git";
     };
     repos = mkOption {
@@ -314,7 +318,7 @@ let
     system.activationScripts.git-init = "${init-script}";
 
     # TODO maybe put all scripts here and then use PATH?
-    environment.etc."${etc-base}".source =
+    environment.etc.${removePrefix "/etc/" cfg.etcDir}.source =
       scriptFarm "git-ssh-authorizers" {
         authorize-command = makeAuthorizeScript (map (rule: [
           (map getName (toList rule.user))
@@ -642,9 +646,5 @@ let
     '';
   };
 
-  etc-base =
-    assert (hasPrefix "/etc/" cfg.etcDir);
-    removePrefix "/etc/" cfg.etcDir;
-
 in
 out

From b419d6cd365cda3202dcce5925bd99e973ba535e Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Tue, 7 Jun 2016 23:38:33 +0200
Subject: [PATCH 16/46] krebs.git.user.home: /var/lib/git -> pkgs.empty

---
 krebs/3modules/git.nix | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index db0b6b0ab..0f5e3172e 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -111,7 +111,7 @@ let
       type = types.user;
       default = {
         name = "git";
-        home = "/var/lib/git";
+        home = toString pkgs.empty;
       };
     };
   };
@@ -336,7 +336,6 @@ let
 
     users.users.${cfg.user.name} = {
       inherit (cfg.user) home name uid;
-      createHome = true;
       description = "Git repository hosting user";
       shell = "/bin/sh";
       openssh.authorizedKeys.keys =

From f9ebab44e454a436541962790f27d686583cfe30 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 00:53:41 +0200
Subject: [PATCH 17/46] tv vim: let {hs,nix}.vim

---
 tv/2configs/vim.nix | 66 ++++++++++++++++++++++++---------------------
 1 file changed, 35 insertions(+), 31 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 10e5aab52..21fb6cd44 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -88,38 +88,9 @@ let
             \ | hi TrailingSpace ctermbg=88
             \ | hi Normal ctermfg=White
 
-    au BufRead,BufNewFile *.hs so ${pkgs.writeText "hs.vim" ''
-      syn region String start=+\[[[:alnum:]]*|+ end=+|]+
-    ''}
+    au BufRead,BufNewFile *.hs so ${hs.vim}
 
-    au BufRead,BufNewFile *.nix so ${pkgs.writeText "nix.vim" ''
-      setf nix
-      set isk=@,48-57,_,192-255,-,'
-
-      " Ref <nix/src/libexpr/lexer.l>
-      syn match INT   /\<[0-9]\+\>/
-      syn match PATH  /[a-zA-Z0-9\.\_\-\+]*\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
-      syn match HPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
-      syn match SPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
-      syn match URI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
-      hi link INT Constant
-      hi link PATH Constant
-      hi link HPATH Constant
-      hi link SPATH Constant
-      hi link URI Constant
-
-      syn match String /"\([^\\"]\|\\.\)*"/
-      syn match Comment /\(^\|\s\)#.*/
-
-      " Haskell comments
-      syn region Comment start=/\(^\|\s\){-#/ end=/#-}/
-      syn match Comment /\(^\|\s\)--.*/
-
-      " Vim comments
-      syn match Comment /\(^\|\s\)"[^"]*$/
-
-      let b:current_syntax = "nix"
-    ''}
+    au BufRead,BufNewFile *.nix so ${nix.vim}
 
     au BufRead,BufNewFile /dev/shm/* set nobackup nowritebackup noswapfile
 
@@ -152,5 +123,38 @@ let
     noremap <esc>[d <nop> | noremap! <esc>[d <nop>
     vnoremap u <nop>
   '';
+
+  hs.vim = pkgs.writeText "hs.vim" ''
+    syn region String start=+\[[[:alnum:]]*|+ end=+|]+
+  '';
+
+  nix.vim = pkgs.writeText "nix.vim" ''
+    setf nix
+    set isk=@,48-57,_,192-255,-,'
+
+    " Ref <nix/src/libexpr/lexer.l>
+    syn match INT   /\<[0-9]\+\>/
+    syn match PATH  /[a-zA-Z0-9\.\_\-\+]*\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
+    syn match HPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
+    syn match SPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
+    syn match URI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
+    hi link INT Constant
+    hi link PATH Constant
+    hi link HPATH Constant
+    hi link SPATH Constant
+    hi link URI Constant
+
+    syn match String /"\([^\\"]\|\\.\)*"/
+    syn match Comment /\(^\|\s\)#.*/
+
+    " Haskell comments
+    syn region Comment start=/\(^\|\s\){-#/ end=/#-}/
+    syn match Comment /\(^\|\s\)--.*/
+
+    " Vim comments
+    syn match Comment /\(^\|\s\)"[^"]*$/
+
+    let b:current_syntax = "nix"
+  '';
 in
 out

From 94e05ff7c470404464392d8d7b61cd3c502684b5 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 03:19:12 +0200
Subject: [PATCH 18/46] tv nix.vim: highlight embedded {Haskell,Vim}

---
 tv/2configs/vim.nix | 66 +++++++++++++++++++++++++++++++++------------
 1 file changed, 49 insertions(+), 17 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 21fb6cd44..82d06f63b 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -132,27 +132,59 @@ let
     setf nix
     set isk=@,48-57,_,192-255,-,'
 
+    syn match NixCode /./
+
     " Ref <nix/src/libexpr/lexer.l>
-    syn match INT   /\<[0-9]\+\>/
-    syn match PATH  /[a-zA-Z0-9\.\_\-\+]*\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
-    syn match HPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
-    syn match SPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
-    syn match URI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
-    hi link INT Constant
-    hi link PATH Constant
-    hi link HPATH Constant
-    hi link SPATH Constant
-    hi link URI Constant
+    syn match NixINT   /\<[0-9]\+\>/
+    syn match NixPATH  /[a-zA-Z0-9\.\_\-\+]*\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
+    syn match NixHPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
+    syn match NixSPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
+    syn match NixURI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
 
-    syn match String /"\([^\\"]\|\\.\)*"/
-    syn match Comment /\(^\|\s\)#.*/
+    syn match NixString /"\([^\\"]\|\\.\)*"/
+    syn match NixCommentMatch /\(^\|\s\)#.*/
+    syn region NixCommentRegion start="/\*" end="\*/"
 
-    " Haskell comments
-    syn region Comment start=/\(^\|\s\){-#/ end=/#-}/
-    syn match Comment /\(^\|\s\)--.*/
+    hi NixCode ctermfg=034
+    hi NixData ctermfg=040
 
-    " Vim comments
-    syn match Comment /\(^\|\s\)"[^"]*$/
+    hi link NixComment Comment
+    hi link NixCommentMatch NixComment
+    hi link NixCommentRegion NixComment
+    hi link NixINT NixData
+    hi link NixPATH NixData
+    hi link NixHPATH NixData
+    hi link NixSPATH NixData
+    hi link NixURI NixData
+    hi link NixString NixData
+
+    hi link NixEnter NixCode
+    hi link NixExit NixData
+
+    syn include @HaskellSyntax syntax/haskell.vim
+    syn region HaskellBlock
+      \ matchgroup=NixExit
+      \ start="/\* haskell \*/ '''"
+      \ skip="''''"
+      \ end="'''"
+      \ contains=@HaskellSyntax
+    unlet b:current_syntax
+
+    syn include @VimSyntax syntax/vim.vim
+    syn region VimBlock
+      \ matchgroup=NixExit
+      \ start="\(/\* vim \*/\|write[-0-9A-Za-z'_]* *\"\(\([^\"]*\.\)\?vimrc\|[^\"]*\.vim\)\"\) *'''"
+      \ skip="''''"
+      \ end="'''"
+      \ contains=@VimSyntax
+    unlet b:current_syntax
+
+    syn region NixBlock
+      \ matchgroup=NixEnter
+      \ start="[$]{"
+      \ end="}"
+      \ contains=ALL
+      \ containedin=HaskellBlock,@HaskellSyntax,VimBlock,@VimSyntax
 
     let b:current_syntax = "nix"
   '';

From 104d5a6d47f9b33c3a075f1e4d2ce9ccacc836ae Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 04:22:46 +0200
Subject: [PATCH 19/46] tv hack.vim: init at 1.0

---
 tv/2configs/vim.nix | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 82d06f63b..ee2cbdb11 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -23,6 +23,28 @@ let
         sha256 = "0z47zq9rqh06ny0q8lpcdsraf3lyzn9xvb59nywnarf3nxrk6hx0";
       };
     })
+    ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
+      name = "hack";
+      version = "1.0";
+    in {
+      name = "vim-color-${name}-${version}";
+      destination = "/colors/${name}.vim";
+      text = /* vim */ ''
+        set background=dark
+        hi clear
+        if exists("syntax_on")
+          syntax clear
+        endif
+
+        let colors_name = ${toJSON name}
+
+        hi Normal   ctermbg=235
+        hi Comment  ctermfg=240
+
+        hi Tabstop        ctermbg=16
+        hi TrailingSpace  ctermbg=88
+      '';
+    })))
   ];
 
   dirs = {
@@ -79,14 +101,11 @@ let
     filetype plugin indent on
 
     set t_Co=256
-    colorscheme industry
+    colorscheme hack
     syntax on
 
     au Syntax * syn match Tabstop containedin=ALL /\t\+/
-            \ | hi Tabstop ctermbg=16
             \ | syn match TrailingSpace containedin=ALL /\s\+$/
-            \ | hi TrailingSpace ctermbg=88
-            \ | hi Normal ctermfg=White
 
     au BufRead,BufNewFile *.hs so ${hs.vim}
 

From e88cf24bcf706ec7e5d5af26770345a65a01e894 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 05:44:26 +0200
Subject: [PATCH 20/46] tv nix.vim: generate NixSubLangs

---
 tv/2configs/vim.nix | 48 ++++++++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 18 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index ee2cbdb11..e224df9fe 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -179,31 +179,43 @@ let
 
     hi link NixEnter NixCode
     hi link NixExit NixData
+    hi link NixQuote NixData
+    hi link NixQuote2 NixQuote
+    hi link NixQuote3 NixQuote
 
-    syn include @HaskellSyntax syntax/haskell.vim
-    syn region HaskellBlock
-      \ matchgroup=NixExit
-      \ start="/\* haskell \*/ '''"
-      \ skip="''''"
-      \ end="'''"
-      \ contains=@HaskellSyntax
-    unlet b:current_syntax
+    syn cluster NixSubLangs contains=NONE
 
-    syn include @VimSyntax syntax/vim.vim
-    syn region VimBlock
-      \ matchgroup=NixExit
-      \ start="\(/\* vim \*/\|write[-0-9A-Za-z'_]* *\"\(\([^\"]*\.\)\?vimrc\|[^\"]*\.vim\)\"\) *'''"
-      \ skip="''''"
-      \ end="'''"
-      \ contains=@VimSyntax
-    unlet b:current_syntax
+    ${concatStringsSep "\n" (mapAttrsToList (name: { start ? null }: let
+    in /* vim */ ''
+      syn include @${name}Syntax syntax/${name}.vim
+      syn region ${name}Block
+        \ matchgroup=NixExit
+        \ start="\(/\* ${name} \*/${optionalString (start != null) ''\|${start}''}\) '''"
+        \ skip="'''\('\|[$]\|\\[nrt]\)"
+        \ end="'''"
+        \ contains=@${name}Syntax
+      syn cluster NixSubLangs add=${name}Block,@${name}Syntax
+      unlet b:current_syntax
+    '') {
+      haskell = {};
+      vim.start = ''write[^ ]* *\"\(\([^\"]*\.\)\?vimrc\|[^\"]*\.vim\)\"'';
+    })}
 
     syn region NixBlock
       \ matchgroup=NixEnter
       \ start="[$]{"
       \ end="}"
-      \ contains=ALL
-      \ containedin=HaskellBlock,@HaskellSyntax,VimBlock,@VimSyntax
+      \ contains=TOP
+      \ containedin=@NixSubLangs
+
+    syn region NixBlockHack
+      \ start="{"
+      \ end="}"
+      \ contains=TOP
+
+    syn match NixQuote  "'''[$]"he=e-1  contained containedin=@NixSubLangs
+    syn match NixQuote2 "''''"he=s+1    contained containedin=@NixSubLangs
+    syn match NixQuote3 "'''\\[nrt]"    contained containedin=@NixSubLangs
 
     let b:current_syntax = "nix"
   '';

From 3fca2434b11ec5cefdf154d82967f0d18655dd25 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 05:45:13 +0200
Subject: [PATCH 21/46] tv nix.vim: highligh embedded sh

---
 tv/2configs/vim.nix | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index e224df9fe..f92861653 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -198,6 +198,7 @@ let
       unlet b:current_syntax
     '') {
       haskell = {};
+      sh.start = ''write\(Ba\|Da\)sh[^ ]* *\"[^\"]*\"'';
       vim.start = ''write[^ ]* *\"\(\([^\"]*\.\)\?vimrc\|[^\"]*\.vim\)\"'';
     })}
 

From f94ce5630a46210904960af047261339fdaa1afe Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 13:20:29 +0200
Subject: [PATCH 22/46] tv hack.vim: minor refactoring

---
 tv/2configs/vim.nix | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index f92861653..e6b9bf20a 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -25,9 +25,8 @@ let
     })
     ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
       name = "hack";
-      version = "1.0";
     in {
-      name = "vim-color-${name}-${version}";
+      name = "vim-color-${name}-1.0";
       destination = "/colors/${name}.vim";
       text = /* vim */ ''
         set background=dark

From 0b6103498b4555df63e29a31905627e66e25f610 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 13:22:47 +0200
Subject: [PATCH 23/46] tv vim-syntax-vim: init at 1.0.0

---
 tv/2configs/vim.nix | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index e6b9bf20a..0eb67fc52 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -44,6 +44,19 @@ let
         hi TrailingSpace  ctermbg=88
       '';
     })))
+    ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
+      name = "vim";
+    in {
+      name = "vim-syntax-${name}-1.0.0";
+      destination = "/syntax/${name}.vim";
+      text = /* vim */ ''
+        ${concatMapStringsSep "\n" (s: /* vim */ ''
+          syn keyword vimColor${s} ${s}
+            \ containedin=ALLBUT,vimComment,vimLineComment
+          hi vimColor${s} ctermfg=${s}
+        '') (map (i: lpad 3 "0" (toString i)) (range 0 255))}
+      '';
+    })))
   ];
 
   dirs = {

From 45d351191e3f76811ea861d1d2d01074835e3771 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 14:15:46 +0200
Subject: [PATCH 24/46] tv vim: {TrailingSpace,Tabstop} -> {Garbage,TabStop}

---
 tv/2configs/vim.nix | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 0eb67fc52..b20149b13 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -40,8 +40,8 @@ let
         hi Normal   ctermbg=235
         hi Comment  ctermfg=240
 
-        hi Tabstop        ctermbg=16
-        hi TrailingSpace  ctermbg=88
+        hi Garbage  ctermbg=088
+        hi TabStop  ctermbg=016
       '';
     })))
     ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
@@ -116,8 +116,8 @@ let
     colorscheme hack
     syntax on
 
-    au Syntax * syn match Tabstop containedin=ALL /\t\+/
-            \ | syn match TrailingSpace containedin=ALL /\s\+$/
+    au Syntax * syn match TabStop containedin=ALL /\t\+/
+            \ | syn match Garbage containedin=ALL /\s\+$/
 
     au BufRead,BufNewFile *.hs so ${hs.vim}
 

From 33e397171e54a19f4457e5f87befb9f2dc9cea08 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 14:28:27 +0200
Subject: [PATCH 25/46] tv hs.vim: hi link {ConId,VarId,hsDelimiter}

---
 tv/2configs/vim.nix | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index b20149b13..21922dc86 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -157,6 +157,10 @@ let
 
   hs.vim = pkgs.writeText "hs.vim" ''
     syn region String start=+\[[[:alnum:]]*|+ end=+|]+
+
+    hi link ConId Identifier
+    hi link VarId Identifier
+    hi link hsDelimiter Delimiter
   '';
 
   nix.vim = pkgs.writeText "nix.vim" ''

From e5c1862d1cad37695bf2e6aad452738d1dcd5af2 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 14:29:05 +0200
Subject: [PATCH 26/46] tv nix.vim: hi link Nix{Code,Data}

---
 tv/2configs/vim.nix | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 21922dc86..17a26d604 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -180,10 +180,10 @@ let
     syn match NixCommentMatch /\(^\|\s\)#.*/
     syn region NixCommentRegion start="/\*" end="\*/"
 
-    hi NixCode ctermfg=034
-    hi NixData ctermfg=040
-
+    hi link NixCode Statement
+    hi link NixData Constant
     hi link NixComment Comment
+
     hi link NixCommentMatch NixComment
     hi link NixCommentRegion NixComment
     hi link NixINT NixData

From a7fff175dc2b0164ce23457d0e2c8e5102c7808b Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 14:29:45 +0200
Subject: [PATCH 27/46] tv nix.vim: hi link ${name}Block

---
 tv/2configs/vim.nix | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 17a26d604..6a8465e32 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -212,6 +212,8 @@ let
         \ contains=@${name}Syntax
       syn cluster NixSubLangs add=${name}Block,@${name}Syntax
       unlet b:current_syntax
+
+      hi link ${name}Block Statement
     '') {
       haskell = {};
       sh.start = ''write\(Ba\|Da\)sh[^ ]* *\"[^\"]*\"'';

From daffd27cd33bffe38f1b709eb46d2ab9a1ddc4b6 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 14:30:19 +0200
Subject: [PATCH 28/46] tv vim-color-hack: 1.0 -> 1.0.1

---
 tv/2configs/vim.nix | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 6a8465e32..5039fbadf 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -26,7 +26,7 @@ let
     ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
       name = "hack";
     in {
-      name = "vim-color-${name}-1.0";
+      name = "vim-color-${name}-1.0.1";
       destination = "/colors/${name}.vim";
       text = /* vim */ ''
         set background=dark
@@ -37,11 +37,29 @@ let
 
         let colors_name = ${toJSON name}
 
-        hi Normal   ctermbg=235
-        hi Comment  ctermfg=240
+        hi Normal       ctermbg=235
+        hi Comment      ctermfg=242
+        hi Constant     ctermfg=255
+        hi Identifier   ctermfg=253
+        hi Function     ctermfg=253
+        hi Statement    ctermfg=253
+        hi PreProc      ctermfg=251
+        hi Type         ctermfg=251
+        hi Delimiter    ctermfg=251
+        hi Special      ctermfg=255
 
-        hi Garbage  ctermbg=088
-        hi TabStop  ctermbg=016
+        hi Garbage      ctermbg=088
+        hi TabStop      ctermbg=016
+
+        hi NixCode      ctermfg=040
+        hi NixData      ctermfg=046
+
+        hi diffNewFile  ctermfg=207
+        hi diffFile     ctermfg=207
+        hi diffLine     ctermfg=207
+        hi diffSubname  ctermfg=207
+        hi diffAdded    ctermfg=010
+        hi diffRemoved  ctermfg=009
       '';
     })))
     ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let

From 6d8bedbf239ae8865bc44866ded062cc44ecdf64 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 14:46:08 +0200
Subject: [PATCH 29/46] tv vim-plugin-showsyntax: init at 1.0.0

---
 tv/2configs/vim.nix | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 5039fbadf..eb6c432bb 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -75,6 +75,30 @@ let
         '') (map (i: lpad 3 "0" (toString i)) (range 0 255))}
       '';
     })))
+    ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
+      name = "showsyntax";
+    in {
+      name = "vim-plugin-${name}-1.0.0";
+      destination = "/plugin/${name}.vim";
+      text = /* vim */ ''
+        if exists('g:loaded_showsyntax')
+          finish
+        endif
+        let g:loaded_showsyntax = 0
+
+        fu! ShowSyntax()
+          let id = synID(line("."), col("."), 1)
+          let name = synIDattr(id, "name")
+          let transName = synIDattr(synIDtrans(id),"name")
+          if name != transName
+            let name .= " (" . transName . ")"
+          endif
+          echo "Syntax: " . name
+        endfu
+
+        command! -n=0 -bar ShowSyntax :call ShowSyntax()
+      '';
+    })))
   ];
 
   dirs = {

From ce79df9b7d43f3e68fac5de195a1d782e7c140aa Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 15:25:33 +0200
Subject: [PATCH 30/46] tv nix.vim: admit both kinds of strings

---
 tv/2configs/vim.nix | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index eb6c432bb..fd3bd8856 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -219,6 +219,12 @@ let
     syn match NixURI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
 
     syn match NixString /"\([^\\"]\|\\.\)*"/
+    syn region NixString2
+      \ matchgroup=NixString2
+      \ start="'''"
+      \ skip="'''\('\|[$]\|\\[nrt]\)"
+      \ end="'''"
+
     syn match NixCommentMatch /\(^\|\s\)#.*/
     syn region NixCommentRegion start="/\*" end="\*/"
 
@@ -234,6 +240,7 @@ let
     hi link NixSPATH NixData
     hi link NixURI NixData
     hi link NixString NixData
+    hi link NixString2 NixString
 
     hi link NixEnter NixCode
     hi link NixExit NixData
@@ -270,6 +277,7 @@ let
       \ containedin=@NixSubLangs
 
     syn region NixBlockHack
+      \ matchgroup=NixEnter
       \ start="{"
       \ end="}"
       \ contains=TOP

From dcf612c2765225543f832c37149033d9488f6fbe Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 17:02:23 +0200
Subject: [PATCH 31/46] tv nix.vim: syn clear corrupting sh syntax

---
 tv/2configs/vim.nix | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index fd3bd8856..d46c3303b 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -269,6 +269,10 @@ let
       vim.start = ''write[^ ]* *\"\(\([^\"]*\.\)\?vimrc\|[^\"]*\.vim\)\"'';
     })}
 
+    " Clear syntax that interferes with NixBlock.
+    " TODO redefine NixBlock so sh syntax don't have to be cleared
+    syn clear shOperator shSetList shVarAssign
+
     syn region NixBlock
       \ matchgroup=NixEnter
       \ start="[$]{"

From dbb43b644de8c706036bc533393284d8e4853773 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 22:05:14 +0200
Subject: [PATCH 32/46] tv nix.vim: set isk last

---
 tv/2configs/vim.nix | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index d46c3303b..13ec89841 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -207,7 +207,6 @@ let
 
   nix.vim = pkgs.writeText "nix.vim" ''
     setf nix
-    set isk=@,48-57,_,192-255,-,'
 
     syn match NixCode /./
 
@@ -291,6 +290,8 @@ let
     syn match NixQuote3 "'''\\[nrt]"    contained containedin=@NixSubLangs
 
     let b:current_syntax = "nix"
+
+    set isk=@,48-57,_,192-255,-,'
   '';
 in
 out

From 1e4e9e1b43d47b59c380a063afe880ea886d78fa Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Thu, 9 Jun 2016 22:05:32 +0200
Subject: [PATCH 33/46] tv nix.vim: syn sync fromstart

---
 tv/2configs/vim.nix | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 13ec89841..1e077dd4f 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -289,6 +289,8 @@ let
     syn match NixQuote2 "''''"he=s+1    contained containedin=@NixSubLangs
     syn match NixQuote3 "'''\\[nrt]"    contained containedin=@NixSubLangs
 
+    syn sync fromstart
+
     let b:current_syntax = "nix"
 
     set isk=@,48-57,_,192-255,-,'

From d32b7f9708c517eed050967d4009b94691755031 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 12:47:13 +0200
Subject: [PATCH 34/46] tv nix.vim: NixBlock containedin @NixStrings

---
 tv/2configs/vim.nix | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 1e077dd4f..480d93e35 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -216,14 +216,15 @@ let
     syn match NixHPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
     syn match NixSPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
     syn match NixURI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
-
-    syn match NixString /"\([^\\"]\|\\.\)*"/
-    syn region NixString2
-      \ matchgroup=NixString2
+    syn match NixSTRING /"\([^\\"]\|\\.\)*"/
+    syn region NixIND_STRING
+      \ matchgroup=NixIND_STRING
       \ start="'''"
       \ skip="'''\('\|[$]\|\\[nrt]\)"
       \ end="'''"
 
+    syn cluster NixStrings contains=NixSTRING,NixIND_STRING
+
     syn match NixCommentMatch /\(^\|\s\)#.*/
     syn region NixCommentRegion start="/\*" end="\*/"
 
@@ -238,8 +239,8 @@ let
     hi link NixHPATH NixData
     hi link NixSPATH NixData
     hi link NixURI NixData
-    hi link NixString NixData
-    hi link NixString2 NixString
+    hi link NixSTRING NixData
+    hi link NixIND_STRING NixData
 
     hi link NixEnter NixCode
     hi link NixExit NixData
@@ -277,7 +278,7 @@ let
       \ start="[$]{"
       \ end="}"
       \ contains=TOP
-      \ containedin=@NixSubLangs
+      \ containedin=@NixSubLangs,@NixStrings
 
     syn region NixBlockHack
       \ matchgroup=NixEnter

From 86a732e61aa6471f28774f4f42af9cb179e33a78 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 13:54:50 +0200
Subject: [PATCH 35/46] tv vim: match TODO everywhere

---
 tv/2configs/vim.nix | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 480d93e35..7da78b4ee 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -158,8 +158,9 @@ let
     colorscheme hack
     syntax on
 
-    au Syntax * syn match TabStop containedin=ALL /\t\+/
-            \ | syn match Garbage containedin=ALL /\s\+$/
+    au Syntax * syn match Garbage containedin=ALL /\s\+$/
+            \ | syn match TabStop containedin=ALL /\t\+/
+            \ | syn keyword Todo containedin=ALL TODO
 
     au BufRead,BufNewFile *.hs so ${hs.vim}
 

From c233b075b242985fc46338949b18d6658802e8fc Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 13:55:12 +0200
Subject: [PATCH 36/46] tv vim-color-hack: 1.0.1 -> 1.0.2

---
 tv/2configs/vim.nix | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 7da78b4ee..694f392df 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -26,7 +26,7 @@ let
     ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
       name = "hack";
     in {
-      name = "vim-color-${name}-1.0.1";
+      name = "vim-color-${name}-1.0.2";
       destination = "/colors/${name}.vim";
       text = /* vim */ ''
         set background=dark
@@ -50,6 +50,7 @@ let
 
         hi Garbage      ctermbg=088
         hi TabStop      ctermbg=016
+        hi Todo         ctermfg=174 ctermbg=NONE
 
         hi NixCode      ctermfg=040
         hi NixData      ctermfg=046

From 3b308d5ed92b4e12dca3a20c0c2811e2c407f47f Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 13:56:15 +0200
Subject: [PATCH 37/46] tv nix.vim: add c and cabal syntax

---
 tv/2configs/vim.nix | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 694f392df..c2f6c8455 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -266,6 +266,8 @@ let
 
       hi link ${name}Block Statement
     '') {
+      c = {};
+      cabal = {};
       haskell = {};
       sh.start = ''write\(Ba\|Da\)sh[^ ]* *\"[^\"]*\"'';
       vim.start = ''write[^ ]* *\"\(\([^\"]*\.\)\?vimrc\|[^\"]*\.vim\)\"'';

From fed55eed93033a2eae440b45b5bf54f120d0ac8b Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 14:43:38 +0200
Subject: [PATCH 38/46] tv nix.vim: NixString can span multiple lines

---
 tv/2configs/vim.nix | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index c2f6c8455..f6a5cc9c1 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -218,7 +218,11 @@ let
     syn match NixHPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
     syn match NixSPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
     syn match NixURI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
-    syn match NixSTRING /"\([^\\"]\|\\.\)*"/
+    syn region NixSTRING
+      \ matchgroup=NixSTRING
+      \ start='"'
+      \ skip='\\"'
+      \ end='"'
     syn region NixIND_STRING
       \ matchgroup=NixIND_STRING
       \ start="'''"

From d51179bf6b6ac4cbd6f6b06408735bc5ee49bbf8 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 16:07:47 +0200
Subject: [PATCH 39/46] tv nix.vim: support ${lang}Block in NixString

---
 tv/2configs/vim.nix | 43 +++++++++++++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index f6a5cc9c1..4fa3fdda5 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -256,25 +256,44 @@ let
 
     syn cluster NixSubLangs contains=NONE
 
-    ${concatStringsSep "\n" (mapAttrsToList (name: { start ? null }: let
+    ${concatStringsSep "\n" (mapAttrsToList (lang: { extraStart ? null }: let
+      startAlts = filter isString [
+        ''/\* ${lang} \*/''
+        extraStart
+      ];
+      sigil = ''\(${concatStringsSep ''\|'' startAlts}\)[ \t\r\n]*'';
     in /* vim */ ''
-      syn include @${name}Syntax syntax/${name}.vim
-      syn region ${name}Block
-        \ matchgroup=NixExit
-        \ start="\(/\* ${name} \*/${optionalString (start != null) ''\|${start}''}\) '''"
-        \ skip="'''\('\|[$]\|\\[nrt]\)"
-        \ end="'''"
-        \ contains=@${name}Syntax
-      syn cluster NixSubLangs add=${name}Block,@${name}Syntax
+      syn include @${lang}Syntax syntax/${lang}.vim
       unlet b:current_syntax
 
-      hi link ${name}Block Statement
+      syn region ${lang}Block_NixSTRING
+        \ matchgroup=NixExit
+        \ extend
+        \ start='${replaceStrings ["'"] ["\\'"] sigil}"'
+        \ skip='\\"'
+        \ end='"'
+        \ contains=@${lang}Syntax
+
+      syn region ${lang}Block_NixIND_STRING
+        \ matchgroup=NixExit
+        \ extend
+        \ start="${replaceStrings ["\""] ["\\\""] sigil}'''"
+        \ skip="'''\('\|[$]\|\\[nrt]\)"
+        \ end="'''"
+        \ contains=@${lang}Syntax
+
+      syn cluster NixSubLangs
+        \ add=@${lang}Syntax,${lang}Block_NixSTRING,${lang}Block_NixIND_STRING
+
+      hi link ${lang}Block_NixSTRING Statement
+      hi link ${lang}Block_NixIND_STRING Statement
     '') {
       c = {};
       cabal = {};
       haskell = {};
-      sh.start = ''write\(Ba\|Da\)sh[^ ]* *\"[^\"]*\"'';
-      vim.start = ''write[^ ]* *\"\(\([^\"]*\.\)\?vimrc\|[^\"]*\.vim\)\"'';
+      sh.extraStart = ''write\(Ba\|Da\)sh[^ \t\r\n]*[ \t\r\n]*"[^"]*"'';
+      vim.extraStart =
+        ''write[^ \t\r\n]*[ \t\r\n]*"\(\([^"]*\.\)\?vimrc\|[^"]*\.vim\)"'';
     })}
 
     " Clear syntax that interferes with NixBlock.

From d8fa951b60f66ee4c275179d8713b5c41300ff61 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 16:10:35 +0200
Subject: [PATCH 40/46] tv nix.vim: syn clear vimString

---
 tv/2configs/vim.nix | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 4fa3fdda5..56d4de648 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -297,8 +297,9 @@ let
     })}
 
     " Clear syntax that interferes with NixBlock.
-    " TODO redefine NixBlock so sh syntax don't have to be cleared
+    " TODO redefine NixBlock so syntax don't have to be cleared
     syn clear shOperator shSetList shVarAssign
+    syn clear vimString
 
     syn region NixBlock
       \ matchgroup=NixEnter

From 006fded4fb53852c36eb410fabe284007084bfd3 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 16:27:22 +0200
Subject: [PATCH 41/46] tv nix.vim: Vim, why are you so broken?!

---
 tv/2configs/vim.nix | 1 -
 1 file changed, 1 deletion(-)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 56d4de648..891823c93 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -299,7 +299,6 @@ let
     " Clear syntax that interferes with NixBlock.
     " TODO redefine NixBlock so syntax don't have to be cleared
     syn clear shOperator shSetList shVarAssign
-    syn clear vimString
 
     syn region NixBlock
       \ matchgroup=NixEnter

From e48ecf1e1c6cbda48045d5425e1a4e7ee6e993e9 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 10 Jun 2016 16:28:27 +0200
Subject: [PATCH 42/46] tv vim: hi NixQuote

---
 tv/2configs/vim.nix | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 891823c93..0d87d97af 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -54,6 +54,7 @@ let
 
         hi NixCode      ctermfg=040
         hi NixData      ctermfg=046
+        hi NixQuote     ctermfg=071
 
         hi diffNewFile  ctermfg=207
         hi diffFile     ctermfg=207

From c80aee7a0b5f3bc064e7f02d9c3d10dc83f1ce73 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Sat, 11 Jun 2016 16:11:22 +0200
Subject: [PATCH 43/46] krebs types.filename: admit <underscore>

---
 krebs/4lib/types.nix | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/krebs/4lib/types.nix b/krebs/4lib/types.nix
index f65d5b68e..b048f48d8 100644
--- a/krebs/4lib/types.nix
+++ b/krebs/4lib/types.nix
@@ -325,10 +325,7 @@ types // rec {
   # POSIX.1‐2013, 3.278 Portable Filename Character Set
   filename = mkOptionType {
     name = "POSIX filename";
-    check = let
-      filename-chars = stringToCharacters
-        "-.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-    in s: all (flip elem filename-chars) (stringToCharacters s);
+    check = x: match "[0-9A-Za-z._-]+" x != null;
     merge = mergeOneOption;
   };
 

From cda4c2d96b70c296ad97e4d9118aa55ea7c3a594 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Sat, 11 Jun 2016 16:29:18 +0200
Subject: [PATCH 44/46] krebs types.filename: maximize strictness

---
 krebs/4lib/types.nix | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/krebs/4lib/types.nix b/krebs/4lib/types.nix
index b048f48d8..628555a9e 100644
--- a/krebs/4lib/types.nix
+++ b/krebs/4lib/types.nix
@@ -325,7 +325,7 @@ types // rec {
   # POSIX.1‐2013, 3.278 Portable Filename Character Set
   filename = mkOptionType {
     name = "POSIX filename";
-    check = x: match "[0-9A-Za-z._-]+" x != null;
+    check = x: match "([0-9A-Za-z._])[0-9A-Za-z._-]*" x != null;
     merge = mergeOneOption;
   };
 
@@ -347,6 +347,6 @@ types // rec {
   # POSIX.1-2013, 3.431 User Name
   username = mkOptionType {
     name = "POSIX username";
-    check = s: filename.check s && substring 0 1 s != "-";
+    check = filename.check;
   };
 }

From 8353b1293e4e4c307e7b875a5449ac901a5afc7d Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Sat, 11 Jun 2016 16:36:42 +0200
Subject: [PATCH 45/46] krebs {{absolute-,}path,user}name: mergeOneOption

---
 krebs/4lib/types.nix | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/krebs/4lib/types.nix b/krebs/4lib/types.nix
index 628555a9e..678ae7a60 100644
--- a/krebs/4lib/types.nix
+++ b/krebs/4lib/types.nix
@@ -335,6 +335,7 @@ types // rec {
   absolute-pathname = mkOptionType {
     name = "POSIX absolute pathname";
     check = s: s == "/" || (pathname.check s && substring 0 1 s == "/");
+    merge = mergeOneOption;
   };
 
   # POSIX.1‐2013, 3.267 Pathname
@@ -342,11 +343,13 @@ types // rec {
   pathname = mkOptionType {
     name = "POSIX pathname";
     check = s: isString s && all filename.check (splitString "/" s);
+    merge = mergeOneOption;
   };
 
   # POSIX.1-2013, 3.431 User Name
   username = mkOptionType {
     name = "POSIX username";
     check = filename.check;
+    merge = mergeOneOption;
   };
 }

From fb8be5838adfe58fc5d13235ac82022cbdb8f6e4 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Sun, 12 Jun 2016 13:53:23 +0200
Subject: [PATCH 46/46] writeFiles: init

---
 krebs/5pkgs/builders.nix | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/krebs/5pkgs/builders.nix b/krebs/5pkgs/builders.nix
index f60bbc9d0..8ba0ab5a7 100644
--- a/krebs/5pkgs/builders.nix
+++ b/krebs/5pkgs/builders.nix
@@ -81,6 +81,26 @@ rec {
     mv "$textPath" $out
   '';
 
+  writeFiles = name: specs0:
+  let
+    specs = mapAttrsToList (path: spec0: {
+      path = assert types.pathname.check path; path;
+      var = "file_${hashString "sha1" path}";
+      text = spec0.text;
+    }) specs0;
+
+    filevars = genAttrs' specs (spec: nameValuePair spec.var spec.text);
+
+    env = filevars // { passAsFile = attrNames filevars; };
+  in
+    pkgs.runCommand name env /* sh */ ''
+      set -efu
+      PATH=${makeBinPath [pkgs.coreutils]}
+      ${concatMapStrings (spec: /* sh */ ''
+        install -D ''$${spec.var}Path $out${spec.path}
+      '') specs}
+    '';
+
   writeHaskell =
     k:
     let