Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion dev/_bootstrap-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,31 @@ let
outputs = _: { };
};

all-inputs-schemes = bootstrap {
inputs.simple.url = "github:vic/empty-flake";
inputs.withBranch.url = "github:vic/empty-flake/main";
inputs.noflake = {
url = "github:vic/empty-flake/main";
flake = false;
};
inputs.gitHttps.url = "git+https://github.com/vic/empty-flake";
inputs.tarball.url = "https://github.com/vic/empty-flake/archive/main.tar.gz";
inputs.tarballPlus.url = "tarball+https://github.com/vic/empty-flake/archive/main.tar.gz";
inputs.fileHttps.url = "file+https://github.com/vic/empty-flake/archive/main.tar.gz";
inputs.attrGh = {
type = "github";
owner = "vic";
repo = "empty-flake";
};
inputs.attrGhRef = {
type = "github";
owner = "vic";
repo = "empty-flake";
ref = "main";
};
inputs.followsSimple.follows = "simple";
};

flake-parts = bootstrap {
inputs.flake-parts.url = "github:hercules-ci/flake-parts";
};
Expand Down Expand Up @@ -114,6 +139,28 @@ let
'';
};

test-npins-schemes = pkgs.writeShellApplication {
name = "test-npins-schemes";
runtimeInputs = [
(all-inputs-schemes.flake-file.apps.write-npins pkgs)
pkgs.jq
];
text = ''
write-npins
cat ${outdir}/npins/sources.json
jq -e '.pins | has("simple")' ${outdir}/npins/sources.json
jq -e '.pins | has("withBranch")' ${outdir}/npins/sources.json
jq -e '.pins | has("noflake")' ${outdir}/npins/sources.json
jq -e '.pins | has("gitHttps")' ${outdir}/npins/sources.json
jq -e '.pins | has("tarball")' ${outdir}/npins/sources.json
jq -e '.pins | has("tarballPlus")' ${outdir}/npins/sources.json
jq -e '.pins | has("fileHttps")' ${outdir}/npins/sources.json
jq -e '.pins | has("attrGh")' ${outdir}/npins/sources.json
jq -e '.pins | has("attrGhRef")' ${outdir}/npins/sources.json
jq -e '.pins | has("followsSimple") | not' ${outdir}/npins/sources.json
'';
};

test-unflake = pkgs.writeShellApplication {
name = "test-unflake";
runtimeInputs = [
Expand All @@ -132,7 +179,29 @@ let
];
text = ''
write-nixlock
grep vic/empty-flake/archive ${outdir}/nixlock.lock.nix
grep empty ${outdir}/nixlock.lock.nix
'';
};

test-nixlock-schemes = pkgs.writeShellApplication {
name = "test-nixlock-schemes";
runtimeInputs = [
(all-inputs-schemes.flake-file.apps.write-nixlock pkgs)
];
text = ''
write-nixlock
cat ${outdir}/nixlock.lock.nix
grep '"simple"' ${outdir}/nixlock.lock.nix
grep '"withBranch"' ${outdir}/nixlock.lock.nix
grep '"noflake"' ${outdir}/nixlock.lock.nix
grep '"gitHttps"' ${outdir}/nixlock.lock.nix
grep '"tarball"' ${outdir}/nixlock.lock.nix
grep '"tarballPlus"' ${outdir}/nixlock.lock.nix
grep '"fileHttps"' ${outdir}/nixlock.lock.nix
grep '"attrGh"' ${outdir}/nixlock.lock.nix
grep '"attrGhRef"' ${outdir}/nixlock.lock.nix
if grep '"followsSimple"' ${outdir}/nixlock.lock.nix; then exit 1; fi
grep vic/empty-flake ${outdir}/nixlock.lock.nix
'';
};

Expand All @@ -143,9 +212,11 @@ pkgs.mkShell {
test-flake
test-unflake
test-npins
test-npins-schemes
test-npins-skip
test-npins-follows
test-npins-transitive
test-nixlock
test-nixlock-schemes
];
}
54 changes: 1 addition & 53 deletions modules/nixlock/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,7 @@ let

nlLibs = (import "${nixlock-source}/${flake-file.nixlock.version}").libs;

parseGithub =
path:
let
parts = lib.splitString "/" path;
owner = builtins.elemAt parts 0;
repo = builtins.elemAt parts 1;
ref = if builtins.length parts > 2 then builtins.elemAt parts 2 else "HEAD";
in
{
type = "gitArchive";
url = "https://github.com/${owner}/${repo}";
inherit ref;
};

parseGitlab =
path:
let
parts = lib.splitString "/" path;
owner = builtins.elemAt parts 0;
repo = builtins.elemAt parts 1;
ref = if builtins.length parts > 2 then builtins.elemAt parts 2 else "HEAD";
in
{
type = "gitArchive";
url = "https://gitlab.com/${owner}/${repo}";
inherit ref;
};

flakeUrlToNixlock =
url:
let
scheme = builtins.head (lib.splitString ":" url);
rest = lib.concatStringsSep ":" (builtins.tail (lib.splitString ":" url));
in
if scheme == "github" then
parseGithub rest
else if scheme == "gitlab" then
parseGitlab rest
else if lib.hasPrefix "git+" url then
{
type = "git";
url = lib.removePrefix "git+" url;
ref = "HEAD";
}
else if lib.hasPrefix "http" url then
{
type = "archive";
inherit url;
}
else
null;

toNixlockInput = _name: input: if input ? url then flakeUrlToNixlock input.url else null;
inherit (import ./parse.nix lib) toNixlockInput;

inputsFile = lib.filterAttrs (_: v: v != null) (lib.mapAttrs toNixlockInput inputs);

Expand Down
113 changes: 113 additions & 0 deletions modules/nixlock/parse.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
lib:
let

parseRef =
urlWithParams:
let
pairs = builtins.filter (lib.hasPrefix "ref=") (
lib.splitString "&" (lib.last (lib.splitString "?" urlWithParams))
);
in
if pairs != [ ] then lib.removePrefix "ref=" (builtins.head pairs) else "HEAD";

baseUrl = urlWithParams: builtins.head (lib.splitString "?" urlWithParams);

parseGitHost =
base: path:
let
parts = lib.splitString "/" path;
owner = builtins.elemAt parts 0;
repo = builtins.elemAt parts 1;
ref = if builtins.length parts > 2 then builtins.elemAt parts 2 else "HEAD";
in
{
type = "gitArchive";
url = "${base}/${owner}/${repo}";
inherit ref;
};

parseGithub = parseGitHost "https://github.com";
parseGitlab = parseGitHost "https://gitlab.com";
parseSourcehut = parseGitHost "https://git.sr.ht";

parseGitUrl = urlWithParams: {
type = "git";
url = baseUrl urlWithParams;
ref = parseRef urlWithParams;
};

attrsetBases = input: {
github = "https://github.com";
gitlab = "https://${input.host or "gitlab.com"}";
sourcehut = "https://${input.host or "git.sr.ht"}";
};

attrsetToNixlock =
input:
let
mgithost = (attrsetBases input).${input.type} or null;
in
if mgithost != null then
{
type = "gitArchive";
url = "${mgithost}/${input.owner}/${input.repo}";
ref = input.ref or "HEAD";
}
else if input.type == "git" then
{
type = "git";
url = input.url;
ref = input.ref or "HEAD";
}
else if
lib.elem input.type [
"tarball"
"file"
]
then
{
type = "archive";
url = input.url;
}
else
null;

flakeUrlToNixlock =
url:
let
scheme = builtins.head (lib.splitString ":" url);
rest = lib.concatStringsSep ":" (builtins.tail (lib.splitString ":" url));
in
if scheme == "github" then
parseGithub rest
else if scheme == "gitlab" then
parseGitlab rest
else if scheme == "sourcehut" then
parseSourcehut rest
else if lib.hasPrefix "git+" url then
parseGitUrl (lib.removePrefix "git+" url)
else if lib.hasPrefix "tarball+" url then
flakeUrlToNixlock (lib.removePrefix "tarball+" url)
else if lib.hasPrefix "file+" url then
flakeUrlToNixlock (lib.removePrefix "file+" url)
else if lib.hasPrefix "http" url then
{
type = "archive";
inherit url;
}
else
null;

toNixlockInput =
_name: input:
if input ? url then
flakeUrlToNixlock input.url
else if input ? type then
attrsetToNixlock input
else
null;

in
{
inherit toNixlockInput;
}
25 changes: 20 additions & 5 deletions modules/npins/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,30 @@ let
inherit (import ../lib.nix lib) inputsExpr;

inputs = inputsExpr flake-file.inputs;
esc = lib.escapeShellArg;

pinnableInputs = lib.filterAttrs (_: v: v.url or "" != "") inputs;
# Synthesise a canonical URL from attrset-form inputs (no url field).
gitHostScheme = { github = "github"; gitlab = "gitlab"; sourcehut = "sourcehut"; };

syntheticUrl = input:
let
scheme = gitHostScheme.${input.type or ""} or null;
ref = if input.ref or "" != "" then "/${input.ref}" else "";
in
if scheme != null && input.owner or "" != "" then
"${scheme}:${input.owner}/${input.repo or ""}${ref}"
else
null;

inputUrl = input:
if input.url or "" != "" then input.url
else syntheticUrl input;

pinnableInputs = lib.filterAttrs (_: input: inputUrl input != null) inputs;

# Seed the runtime queue with one tab-separated "name\turl" line per declared input.
queueSeed =
let
lines = lib.mapAttrsToList (name: input: "${name}\t${input.url or ""}") pinnableInputs;
in lib.concatStringsSep "\n" lines;
lib.concatStringsSep "\n"
(lib.mapAttrsToList (name: input: "${name}\t${inputUrl input}") pinnableInputs);

# Collect names of inputs that are explicitly skipped (follows = "") at any nesting level.
collectSkipped =
Expand Down
42 changes: 32 additions & 10 deletions modules/npins/npins.bash
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,51 @@ echo "$queueSeed" > "$QUEUE_FILE"

# Add a pin by its flake-style URL (github:o/r, gitlab:o/r, channel URL, etc.)
npins_add_url() {
local name="$1" url="$2" spec owner repo ref channel
local name="$1" url="$2" spec owner repo ref channel gitbase q
# Strip wrapper prefixes, then re-dispatch.
case "$url" in
tarball+*) npins_add_url "$name" "${url#tarball+}"; return ;;
file+*) npins_add_url "$name" "${url#file+}"; return ;;
git+*)
gitbase="${url#git+}"
if [[ "$gitbase" == *"?"* ]]; then
q="${gitbase#*\?}" gitbase="${gitbase%%\?*}"
ref=$(printf '%s' "$q" | tr '&' '\n' | sed -n 's/^ref=//p' | head -1)
else
ref=""
fi
if [ -n "$ref" ]; then
npins add git --name "$name" -b "$ref" "$gitbase"
else
npins add git --name "$name" "$gitbase" 2>/dev/null \
|| npins add git --name "$name" -b main "$gitbase" 2>/dev/null \
|| npins add git --name "$name" -b master "$gitbase"
fi ;;
github:*)
spec="${url#github:}" owner="${spec%%/*}" spec="${spec#*/}"
repo="${spec%%/*}" ref="${spec#*/}"
if [ "$ref" != "$repo" ]; then
npins add github --name "$name" -b "$ref" "$owner" "$repo"
npins add github --name "$name" -b "$ref" "$owner" "$repo"
else
# No explicit ref: prefer a release tag, fall back to common branches.
npins add github --name "$name" "$owner" "$repo" 2>/dev/null \
|| npins add github --name "$name" -b main "$owner" "$repo" 2>/dev/null \
|| npins add github --name "$name" -b master "$owner" "$repo"
npins add github --name "$name" "$owner" "$repo" 2>/dev/null \
|| npins add github --name "$name" -b main "$owner" "$repo" 2>/dev/null \
|| npins add github --name "$name" -b master "$owner" "$repo"
fi ;;
gitlab:*)
spec="${url#gitlab:}" owner="${spec%%/*}" spec="${spec#*/}"
repo="${spec%%/*}" ref="${spec#*/}"
if [ "$ref" != "$repo" ]; then
npins add gitlab --name "$name" -b "$ref" "$owner" "$repo"
npins add gitlab --name "$name" -b "$ref" "$owner" "$repo"
else
npins add gitlab --name "$name" "$owner" "$repo" 2>/dev/null \
|| npins add gitlab --name "$name" -b main "$owner" "$repo" 2>/dev/null \
|| npins add gitlab --name "$name" -b master "$owner" "$repo"
npins add gitlab --name "$name" "$owner" "$repo" 2>/dev/null \
|| npins add gitlab --name "$name" -b main "$owner" "$repo" 2>/dev/null \
|| npins add gitlab --name "$name" -b master "$owner" "$repo"
fi ;;
sourcehut:*)
spec="${url#sourcehut:}" owner="${spec%%/*}" repo="${spec#*/}"
ref="${repo#*/}" repo="${repo%%/*}"
[ "$ref" = "$repo" ] && ref="main"
npins add git --name "$name" -b "$ref" "https://git.sr.ht/${owner}/${repo}" ;;
https://channels.nixos.org/*|https://releases.nixos.org/*)
channel=$(printf '%s' "$url" | sed 's|https://[^/]*/||;s|/.*||')
npins add channel --name "$name" "$channel" ;;
Expand Down