1# Asset generation pipeline using Nix, which generates a directory structure to be served via obelisk-asset-serve-*
8# Default encoding generation function for this platform; usually zopfliEncodings, but gzipEncodings on darwin due to zopfli not building on darwin.
10# defaultEncodings :: String -> Derivation
12 # zopfli isn't supported on macOS as of nixpkgs eafd703a63
13 if nixpkgs.stdenv.isDarwin then gzipEncodings else zopfliEncodings;
15# Encoding generation function which uses zopfli to encode the asset with very high compression efficiency, at the cost of CPU time compressing.
16# Generates gzip, compress/zlib, and deflate outputs all using zopfli with 5 iterations.
18# zopfliEncodings :: String -> Derivation
19zopfliEncodings = file:
20 nixpkgs.stdenv.mkDerivation {
25 builder = builtins.toFile "builder.sh" ''
26 source "$stdenv/setup"
30 ln -s "$input" "$out/identity"
32 zopfli -c --i5 --gzip "$input" >"$out/gzip"
33 zopfli -c --i5 --zlib "$input" >"$out/compress"
34 zopfli -c --i5 --deflate "$input" >"$out/deflate"
42# Encoding generation function which uses gzip to encode the asset with decent compression efficiency and a small CPU cost. Only generates a gzip output.
44# gzipEncodings :: String -> Derivation
46 nixpkgs.stdenv.mkDerivation {
51 builder = builtins.toFile "builder.sh" ''
52 source "$stdenv/setup"
56 ln -s "$input" "$out/identity"
58 gzip -c7 "$input" > "$out/gzip"
66# Encoding generation function which doesn't do any compression.
68# noEncodings :: String -> Derivation
70 nixpkgs.stdenv.mkDerivation {
75 builder = builtins.toFile "builder.sh" ''
76 source "$stdenv/setup"
80 ln -s "$input" "$out/identity"
87# Convert an attrset into a list of name/value pairs.
89# setToList :: AttrSet a -> [{ name :: String, value :: a }]
90setToList = mapAttrsToList (name: value: { inherit name value; });
92# Apply some function (chunkDerivations, for example) to the values in a list of name/value pairs then reattach the names.
94# mapSnds :: ([b] -> [b']) -> [{ fst :: a, snd :: b }] -> [{ fst :: a, snd :: b' }]
95mapSnds = f: l: zipLists (map (x: x.fst) l) (f (map (x: x.snd) l));
97# Chunk derivations in groups of up to a given size to avoid https://github.com/NixOS/nix/issues/875
99# It works by creating a dependent chain of derivations that must be built to finish evaluation of the overall nix expression, without evaluating too many.
100# Based on the current operation of nix, n should not be greater than 1012, and should probably be substantially less.
102# chunkDerivations :: Int -> [a] -> [a]
103chunkDerivations = n: l:
106 go = x: if x == [] then builtins.toFile "emptyList.nix" "[]" else makeChunkDerivation (take n x) (go (drop n x));
108 # makeChunkDerivation :: [a] -> ExprFile [a] -> ExprFile [a]
109 makeChunkDerivation = chunk': next:
110 let chunk = map (path: "(import ${builtins.unsafeDiscardOutputDependency path.drvPath}).${path.outputName}") chunk';
111 in builtins.seq (builtins.toPath next) (nixpkgs.stdenv.mkDerivation {
112 name = "chunkDerivations";
114 preferLocalBuild = true;
115 builder = builtins.toFile "chunkDerivations.sh" ''
116 source "$stdenv/setup"
120 echo "[ $chunk ] ++ (import $next)" >"$out"
124 in a ++ import (go b);
126# Recursively map some function over a @AttrSet DirEntry@, unioning the results of the application at each level.
128# unionMapFilesWithName :: ({name :: String, value :: DirEntry} -> [{name :: String, value :: a}]) -> AttrSet DirEntry -> AttrSet DirEntry
129unionMapFilesWithName = f: d:
130 let go = {name, value}:
131 if value.type == "directory" then [{
134 inherit (value) type;
135 contents = unionMapFilesWithName f value.contents;
137 }] else setToList (f {
140 in builtins.listToAttrs (builtins.concatLists (map go (setToList d)));
142# Read a directory structure recursively into an @AttrSet DirEntry@.
144# readDirRecursive :: String -> AttrSet DirEntry
145readDirRecursive = dir:
146 let d = filterAttrs (n: d: !(hasPrefix "." n)) (builtins.readDir dir);
148 let path = dir + "/${name}";
150 in if type == "directory" then {
154 contents = readDirRecursive path;
162 in builtins.listToAttrs (map go (builtins.attrNames d));
164# Render a string as a double quoted string with any internal double quotes or backslashes escaped using backslash, appropriate for a nix expression.
166# doubleQuoteString :: String -> String
167doubleQuoteString = s: "\"" + builtins.replaceStrings ["\\" "\""] ["\\\\" "\\\""] s + "\"";
169# Given an @AttrSet DirEntry@ generate a derivation with symlink entries for each @DirEntry@ given.
171# dirToPath :: AttrSet DirEntry -> Derivation
173 let pairsToBashArray = xs: builtins.concatStringsSep " " (map (np: "[" + doubleQuoteString np.fst + "]=" + doubleQuoteString (toString np.snd)) xs);
174 files = pairsToBashArray (mapSnds (chunkDerivations 128) (mapAttrsToList (fst: entry: { inherit fst; snd = toPath entry; }) contents));
175 filesIsLarge = builtins.stringLength files > 65536;
177 nixpkgs.stdenv.mkDerivation {
180 passAsFile = optional filesIsLarge "files";
181 preferLocalBuild = true;
182 builder = builtins.toFile "builder.sh" ''
184 source "$stdenv/setup"
186 eval "declare -A files=(${if filesIsLarge then ''$(cat "$filesPath")'' else ''$files''})"
190 for filename in "''${!files[@]}" ; do
191 ln -s "''${files[$filename]}" "$out/$filename"
196# Given a @DirEntry@ either use 'dirToPath' to make a derivation with its contents or return the path directly.
197toPath = x: if x.type == "directory" then dirToPath x.contents else x.path;
199# Given a file path, hash its contents as nix does and return the hash string.
201 builtins.readFile (nixpkgs.runCommand "hashFile" {
205 preferLocalBuild = true;
208 nix-hash --flat --base32 --type sha256 "$path" | tr -d '\n' >"$out"
211# Build a DirEntry of the shape { type :: String, contents :: {DirEntry} } with type set to directory. Used to create a data model to pass to toPath.
213# dir :: String -> DirEntry
219# Build a DirEntry of the shape { type :: String, path :: String } with type set to symlink. Used to create a data model to pass to toPath.
221# symlink :: String -> DirEntry
227# Given a encoding generation function and a file entry resulting from readDirRecursive in the form { name :: String, value: { path :: String } },
228# build a DirEntry for dirToPath with the various encodings of the asset for dirToPath to build into a final directory tree.
229mkAsset = encodings: {name, value}:
230 let nameWithHash = "${hashFile value.path}-${name}";
232 ${nameWithHash} = dir {
233 type = symlink (builtins.toFile "type" "immutable");
234 encodings = symlink (encodings value.path);
237 type = symlink (builtins.toFile "type" "redirect");
238 target = symlink (builtins.toFile "target" "${nameWithHash}");
242# Given an encoding generation function to use and a directory containing assets, recursively walk the directory and encode each asset.
244# mkAssetsWith :: (String -> Derivation) -> String -> Derivation
245mkAssetsWith = encodings: d: dirToPath (unionMapFilesWithName (mkAsset encodings) (readDirRecursive d));
247# Given an input directory containing assets, recursively walk the directory and encode each asset with the default encodings.
249# mkAssets :: String -> Derivation
250mkAssets = mkAssetsWith defaultEncodings;
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list