about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2019-08-03T17·27+0100
committerVincent Ambo <github@tazj.in>2019-08-03T22·11+0100
commit20103640fa6ab0678c72d3ba8a3ddc29ac264973 (patch)
treec9eb3fdfa0a7786a06b39f7731fe7b7a2e60ce2e
parentaa260af1fff8a07a34c776e5b4be2b5c63b96826 (diff)
fix(nix): Support retrieving differently cased top-level attributes
As described in issue #14, the registry API does not allow image names
with uppercase-characters in them.

However, the Nix package set has several top-level keys with uppercase
characters in them which could previously not be retrieved using
Nixery.

This change implements a method for retrieving those keys, but it is
explicitly only working for the top-level package set as nested
sets (such as `haskellPackages`) often contain packages that differ in
case only.
-rw-r--r--tools/nixery/build-registry-image.nix33
1 files changed, 29 insertions, 4 deletions
diff --git a/tools/nixery/build-registry-image.nix b/tools/nixery/build-registry-image.nix
index 20eb6d9e98..6a5312b444 100644
--- a/tools/nixery/build-registry-image.nix
+++ b/tools/nixery/build-registry-image.nix
@@ -113,11 +113,36 @@ let
   # For top-level items, the name of the key yields the result directly. Nested
   # items are fetched by using dot-syntax, as in Nix itself.
   #
-  # For example, `deepFetch pkgs "xorg.xev"` retrieves `pkgs.xorg.xev`.
-  deepFetch = s: n:
-    let path = lib.strings.splitString "." n;
+  # Due to a restriction of the registry API specification it is not possible to
+  # pass uppercase characters in an image name, however the Nix package set
+  # makes use of camelCasing repeatedly (for example for `haskellPackages`).
+  #
+  # To work around this, if no value is found on the top-level a second lookup
+  # is done on the package set using lowercase-names. This is not done for
+  # nested sets, as they often have keys that only differ in case.
+  #
+  # For example, `deepFetch pkgs "xorg.xev"` retrieves `pkgs.xorg.xev` and
+  # `deepFetch haskellpackages.stylish-haskell` retrieves
+  # `haskellPackages.stylish-haskell`.
+  deepFetch = with lib; s: n:
+    let path = splitString "." n;
         err = { error = "not_found"; pkg = n; };
-    in lib.attrsets.attrByPath path err s;
+        # The most efficient way I've found to do a lookup against
+        # case-differing versions of an attribute is to first construct a
+        # mapping of all lowercased attribute names to their differently cased
+        # equivalents.
+        #
+        # This map is then used for a second lookup if the top-level
+        # (case-sensitive) one does not yield a result.
+        hasUpper = str: (match ".*[A-Z].*" str) != null;
+        allUpperKeys = filter hasUpper (attrNames s);
+        lowercased = listToAttrs (map (k: {
+          name = toLower k;
+          value = k;
+          }) allUpperKeys);
+        caseAmendedPath = map (v: if hasAttr v lowercased then lowercased."${v}" else v) path;
+        fetchLower = attrByPath caseAmendedPath err s;
+    in attrByPath path fetchLower s;
 
   # allContents is the combination of all derivations and store paths passed in
   # directly, as well as packages referred to by name.