Generate flake.nix from module options. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ dendrix.oeiuwq.com/Dendritic.html
dendritic nix inputs

nixlock schemes (#78)

npins schemes

authored by oeiuwq.com and committed by

GitHub b0477cfb d4ce85e8

+238 -69
+72 -1
dev/_bootstrap-tests.nix
··· 19 19 outputs = _: { }; 20 20 }; 21 21 22 + all-inputs-schemes = bootstrap { 23 + inputs.simple.url = "github:vic/empty-flake"; 24 + inputs.withBranch.url = "github:vic/empty-flake/main"; 25 + inputs.noflake = { 26 + url = "github:vic/empty-flake/main"; 27 + flake = false; 28 + }; 29 + inputs.gitHttps.url = "git+https://github.com/vic/empty-flake"; 30 + inputs.tarball.url = "https://github.com/vic/empty-flake/archive/main.tar.gz"; 31 + inputs.tarballPlus.url = "tarball+https://github.com/vic/empty-flake/archive/main.tar.gz"; 32 + inputs.fileHttps.url = "file+https://github.com/vic/empty-flake/archive/main.tar.gz"; 33 + inputs.attrGh = { 34 + type = "github"; 35 + owner = "vic"; 36 + repo = "empty-flake"; 37 + }; 38 + inputs.attrGhRef = { 39 + type = "github"; 40 + owner = "vic"; 41 + repo = "empty-flake"; 42 + ref = "main"; 43 + }; 44 + inputs.followsSimple.follows = "simple"; 45 + }; 46 + 22 47 flake-parts = bootstrap { 23 48 inputs.flake-parts.url = "github:hercules-ci/flake-parts"; 24 49 }; ··· 114 139 ''; 115 140 }; 116 141 142 + test-npins-schemes = pkgs.writeShellApplication { 143 + name = "test-npins-schemes"; 144 + runtimeInputs = [ 145 + (all-inputs-schemes.flake-file.apps.write-npins pkgs) 146 + pkgs.jq 147 + ]; 148 + text = '' 149 + write-npins 150 + cat ${outdir}/npins/sources.json 151 + jq -e '.pins | has("simple")' ${outdir}/npins/sources.json 152 + jq -e '.pins | has("withBranch")' ${outdir}/npins/sources.json 153 + jq -e '.pins | has("noflake")' ${outdir}/npins/sources.json 154 + jq -e '.pins | has("gitHttps")' ${outdir}/npins/sources.json 155 + jq -e '.pins | has("tarball")' ${outdir}/npins/sources.json 156 + jq -e '.pins | has("tarballPlus")' ${outdir}/npins/sources.json 157 + jq -e '.pins | has("fileHttps")' ${outdir}/npins/sources.json 158 + jq -e '.pins | has("attrGh")' ${outdir}/npins/sources.json 159 + jq -e '.pins | has("attrGhRef")' ${outdir}/npins/sources.json 160 + jq -e '.pins | has("followsSimple") | not' ${outdir}/npins/sources.json 161 + ''; 162 + }; 163 + 117 164 test-unflake = pkgs.writeShellApplication { 118 165 name = "test-unflake"; 119 166 runtimeInputs = [ ··· 132 179 ]; 133 180 text = '' 134 181 write-nixlock 135 - grep vic/empty-flake/archive ${outdir}/nixlock.lock.nix 182 + grep empty ${outdir}/nixlock.lock.nix 183 + ''; 184 + }; 185 + 186 + test-nixlock-schemes = pkgs.writeShellApplication { 187 + name = "test-nixlock-schemes"; 188 + runtimeInputs = [ 189 + (all-inputs-schemes.flake-file.apps.write-nixlock pkgs) 190 + ]; 191 + text = '' 192 + write-nixlock 193 + cat ${outdir}/nixlock.lock.nix 194 + grep '"simple"' ${outdir}/nixlock.lock.nix 195 + grep '"withBranch"' ${outdir}/nixlock.lock.nix 196 + grep '"noflake"' ${outdir}/nixlock.lock.nix 197 + grep '"gitHttps"' ${outdir}/nixlock.lock.nix 198 + grep '"tarball"' ${outdir}/nixlock.lock.nix 199 + grep '"tarballPlus"' ${outdir}/nixlock.lock.nix 200 + grep '"fileHttps"' ${outdir}/nixlock.lock.nix 201 + grep '"attrGh"' ${outdir}/nixlock.lock.nix 202 + grep '"attrGhRef"' ${outdir}/nixlock.lock.nix 203 + if grep '"followsSimple"' ${outdir}/nixlock.lock.nix; then exit 1; fi 204 + grep vic/empty-flake ${outdir}/nixlock.lock.nix 136 205 ''; 137 206 }; 138 207 ··· 143 212 test-flake 144 213 test-unflake 145 214 test-npins 215 + test-npins-schemes 146 216 test-npins-skip 147 217 test-npins-follows 148 218 test-npins-transitive 149 219 test-nixlock 220 + test-nixlock-schemes 150 221 ]; 151 222 }
+1 -53
modules/nixlock/default.nix
··· 12 12 13 13 nlLibs = (import "${nixlock-source}/${flake-file.nixlock.version}").libs; 14 14 15 - parseGithub = 16 - path: 17 - let 18 - parts = lib.splitString "/" path; 19 - owner = builtins.elemAt parts 0; 20 - repo = builtins.elemAt parts 1; 21 - ref = if builtins.length parts > 2 then builtins.elemAt parts 2 else "HEAD"; 22 - in 23 - { 24 - type = "gitArchive"; 25 - url = "https://github.com/${owner}/${repo}"; 26 - inherit ref; 27 - }; 28 - 29 - parseGitlab = 30 - path: 31 - let 32 - parts = lib.splitString "/" path; 33 - owner = builtins.elemAt parts 0; 34 - repo = builtins.elemAt parts 1; 35 - ref = if builtins.length parts > 2 then builtins.elemAt parts 2 else "HEAD"; 36 - in 37 - { 38 - type = "gitArchive"; 39 - url = "https://gitlab.com/${owner}/${repo}"; 40 - inherit ref; 41 - }; 42 - 43 - flakeUrlToNixlock = 44 - url: 45 - let 46 - scheme = builtins.head (lib.splitString ":" url); 47 - rest = lib.concatStringsSep ":" (builtins.tail (lib.splitString ":" url)); 48 - in 49 - if scheme == "github" then 50 - parseGithub rest 51 - else if scheme == "gitlab" then 52 - parseGitlab rest 53 - else if lib.hasPrefix "git+" url then 54 - { 55 - type = "git"; 56 - url = lib.removePrefix "git+" url; 57 - ref = "HEAD"; 58 - } 59 - else if lib.hasPrefix "http" url then 60 - { 61 - type = "archive"; 62 - inherit url; 63 - } 64 - else 65 - null; 66 - 67 - toNixlockInput = _name: input: if input ? url then flakeUrlToNixlock input.url else null; 15 + inherit (import ./parse.nix lib) toNixlockInput; 68 16 69 17 inputsFile = lib.filterAttrs (_: v: v != null) (lib.mapAttrs toNixlockInput inputs); 70 18
+113
modules/nixlock/parse.nix
··· 1 + lib: 2 + let 3 + 4 + parseRef = 5 + urlWithParams: 6 + let 7 + pairs = builtins.filter (lib.hasPrefix "ref=") ( 8 + lib.splitString "&" (lib.last (lib.splitString "?" urlWithParams)) 9 + ); 10 + in 11 + if pairs != [ ] then lib.removePrefix "ref=" (builtins.head pairs) else "HEAD"; 12 + 13 + baseUrl = urlWithParams: builtins.head (lib.splitString "?" urlWithParams); 14 + 15 + parseGitHost = 16 + base: path: 17 + let 18 + parts = lib.splitString "/" path; 19 + owner = builtins.elemAt parts 0; 20 + repo = builtins.elemAt parts 1; 21 + ref = if builtins.length parts > 2 then builtins.elemAt parts 2 else "HEAD"; 22 + in 23 + { 24 + type = "gitArchive"; 25 + url = "${base}/${owner}/${repo}"; 26 + inherit ref; 27 + }; 28 + 29 + parseGithub = parseGitHost "https://github.com"; 30 + parseGitlab = parseGitHost "https://gitlab.com"; 31 + parseSourcehut = parseGitHost "https://git.sr.ht"; 32 + 33 + parseGitUrl = urlWithParams: { 34 + type = "git"; 35 + url = baseUrl urlWithParams; 36 + ref = parseRef urlWithParams; 37 + }; 38 + 39 + attrsetBases = input: { 40 + github = "https://github.com"; 41 + gitlab = "https://${input.host or "gitlab.com"}"; 42 + sourcehut = "https://${input.host or "git.sr.ht"}"; 43 + }; 44 + 45 + attrsetToNixlock = 46 + input: 47 + let 48 + mgithost = (attrsetBases input).${input.type} or null; 49 + in 50 + if mgithost != null then 51 + { 52 + type = "gitArchive"; 53 + url = "${mgithost}/${input.owner}/${input.repo}"; 54 + ref = input.ref or "HEAD"; 55 + } 56 + else if input.type == "git" then 57 + { 58 + type = "git"; 59 + url = input.url; 60 + ref = input.ref or "HEAD"; 61 + } 62 + else if 63 + lib.elem input.type [ 64 + "tarball" 65 + "file" 66 + ] 67 + then 68 + { 69 + type = "archive"; 70 + url = input.url; 71 + } 72 + else 73 + null; 74 + 75 + flakeUrlToNixlock = 76 + url: 77 + let 78 + scheme = builtins.head (lib.splitString ":" url); 79 + rest = lib.concatStringsSep ":" (builtins.tail (lib.splitString ":" url)); 80 + in 81 + if scheme == "github" then 82 + parseGithub rest 83 + else if scheme == "gitlab" then 84 + parseGitlab rest 85 + else if scheme == "sourcehut" then 86 + parseSourcehut rest 87 + else if lib.hasPrefix "git+" url then 88 + parseGitUrl (lib.removePrefix "git+" url) 89 + else if lib.hasPrefix "tarball+" url then 90 + flakeUrlToNixlock (lib.removePrefix "tarball+" url) 91 + else if lib.hasPrefix "file+" url then 92 + flakeUrlToNixlock (lib.removePrefix "file+" url) 93 + else if lib.hasPrefix "http" url then 94 + { 95 + type = "archive"; 96 + inherit url; 97 + } 98 + else 99 + null; 100 + 101 + toNixlockInput = 102 + _name: input: 103 + if input ? url then 104 + flakeUrlToNixlock input.url 105 + else if input ? type then 106 + attrsetToNixlock input 107 + else 108 + null; 109 + 110 + in 111 + { 112 + inherit toNixlockInput; 113 + }
+20 -5
modules/npins/default.nix
··· 4 4 inherit (import ../lib.nix lib) inputsExpr; 5 5 6 6 inputs = inputsExpr flake-file.inputs; 7 - esc = lib.escapeShellArg; 7 + 8 + # Synthesise a canonical URL from attrset-form inputs (no url field). 9 + gitHostScheme = { github = "github"; gitlab = "gitlab"; sourcehut = "sourcehut"; }; 10 + 11 + syntheticUrl = input: 12 + let 13 + scheme = gitHostScheme.${input.type or ""} or null; 14 + ref = if input.ref or "" != "" then "/${input.ref}" else ""; 15 + in 16 + if scheme != null && input.owner or "" != "" then 17 + "${scheme}:${input.owner}/${input.repo or ""}${ref}" 18 + else 19 + null; 8 20 9 - pinnableInputs = lib.filterAttrs (_: v: v.url or "" != "") inputs; 21 + inputUrl = input: 22 + if input.url or "" != "" then input.url 23 + else syntheticUrl input; 24 + 25 + pinnableInputs = lib.filterAttrs (_: input: inputUrl input != null) inputs; 10 26 11 27 # Seed the runtime queue with one tab-separated "name\turl" line per declared input. 12 28 queueSeed = 13 - let 14 - lines = lib.mapAttrsToList (name: input: "${name}\t${input.url or ""}") pinnableInputs; 15 - in lib.concatStringsSep "\n" lines; 29 + lib.concatStringsSep "\n" 30 + (lib.mapAttrsToList (name: input: "${name}\t${inputUrl input}") pinnableInputs); 16 31 17 32 # Collect names of inputs that are explicitly skipped (follows = "") at any nesting level. 18 33 collectSkipped =
+32 -10
modules/npins/npins.bash
··· 13 13 14 14 # Add a pin by its flake-style URL (github:o/r, gitlab:o/r, channel URL, etc.) 15 15 npins_add_url() { 16 - local name="$1" url="$2" spec owner repo ref channel 16 + local name="$1" url="$2" spec owner repo ref channel gitbase q 17 + # Strip wrapper prefixes, then re-dispatch. 17 18 case "$url" in 19 + tarball+*) npins_add_url "$name" "${url#tarball+}"; return ;; 20 + file+*) npins_add_url "$name" "${url#file+}"; return ;; 21 + git+*) 22 + gitbase="${url#git+}" 23 + if [[ "$gitbase" == *"?"* ]]; then 24 + q="${gitbase#*\?}" gitbase="${gitbase%%\?*}" 25 + ref=$(printf '%s' "$q" | tr '&' '\n' | sed -n 's/^ref=//p' | head -1) 26 + else 27 + ref="" 28 + fi 29 + if [ -n "$ref" ]; then 30 + npins add git --name "$name" -b "$ref" "$gitbase" 31 + else 32 + npins add git --name "$name" "$gitbase" 2>/dev/null \ 33 + || npins add git --name "$name" -b main "$gitbase" 2>/dev/null \ 34 + || npins add git --name "$name" -b master "$gitbase" 35 + fi ;; 18 36 github:*) 19 37 spec="${url#github:}" owner="${spec%%/*}" spec="${spec#*/}" 20 38 repo="${spec%%/*}" ref="${spec#*/}" 21 39 if [ "$ref" != "$repo" ]; then 22 - npins add github --name "$name" -b "$ref" "$owner" "$repo" 40 + npins add github --name "$name" -b "$ref" "$owner" "$repo" 23 41 else 24 - # No explicit ref: prefer a release tag, fall back to common branches. 25 - npins add github --name "$name" "$owner" "$repo" 2>/dev/null \ 26 - || npins add github --name "$name" -b main "$owner" "$repo" 2>/dev/null \ 27 - || npins add github --name "$name" -b master "$owner" "$repo" 42 + npins add github --name "$name" "$owner" "$repo" 2>/dev/null \ 43 + || npins add github --name "$name" -b main "$owner" "$repo" 2>/dev/null \ 44 + || npins add github --name "$name" -b master "$owner" "$repo" 28 45 fi ;; 29 46 gitlab:*) 30 47 spec="${url#gitlab:}" owner="${spec%%/*}" spec="${spec#*/}" 31 48 repo="${spec%%/*}" ref="${spec#*/}" 32 49 if [ "$ref" != "$repo" ]; then 33 - npins add gitlab --name "$name" -b "$ref" "$owner" "$repo" 50 + npins add gitlab --name "$name" -b "$ref" "$owner" "$repo" 34 51 else 35 - npins add gitlab --name "$name" "$owner" "$repo" 2>/dev/null \ 36 - || npins add gitlab --name "$name" -b main "$owner" "$repo" 2>/dev/null \ 37 - || npins add gitlab --name "$name" -b master "$owner" "$repo" 52 + npins add gitlab --name "$name" "$owner" "$repo" 2>/dev/null \ 53 + || npins add gitlab --name "$name" -b main "$owner" "$repo" 2>/dev/null \ 54 + || npins add gitlab --name "$name" -b master "$owner" "$repo" 38 55 fi ;; 56 + sourcehut:*) 57 + spec="${url#sourcehut:}" owner="${spec%%/*}" repo="${spec#*/}" 58 + ref="${repo#*/}" repo="${repo%%/*}" 59 + [ "$ref" = "$repo" ] && ref="main" 60 + npins add git --name "$name" -b "$ref" "https://git.sr.ht/${owner}/${repo}" ;; 39 61 https://channels.nixos.org/*|https://releases.nixos.org/*) 40 62 channel=$(printf '%s' "$url" | sed 's|https://[^/]*/||;s|/.*||') 41 63 npins add channel --name "$name" "$channel" ;;