From cdca8dd72a92ba751643af15fcb3bf3cfdaa61ac Mon Sep 17 00:00:00 2001 From: atagen Date: Wed, 20 May 2026 14:42:24 +1000 Subject: [PATCH] switch to homegrown pinning --- meat.nu | 172 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 123 insertions(+), 49 deletions(-) diff --git a/meat.nu b/meat.nu index c0ecbfb..51dc8e3 100644 --- a/meat.nu +++ b/meat.nu @@ -34,10 +34,6 @@ def hn []: nothing -> string { ^hostname | str trim } -def flake-target []: nothing -> string { - $"($env.MEATS)#nixosConfigurations.(hn).config.system.build.toplevel" -} - def system-attr []: nothing -> string { $"nixosConfigurations.(hn).config.system.build.toplevel" } @@ -77,14 +73,14 @@ def cmd-yum [...args: string] { def cmd-cook [...args: string] { with-frame { meat-print "PREPARING DELICIOUS MEATS.." - try { ^nix build --no-link (flake-target) ...$args } + try { ^nix-build --no-out-link $"($env.MEATS)/entry.nix" -A (system-attr) ...$args } } } def cmd-poke [...args: string] { with-frame { meat-print "PREPARING SUSPICIOUS MEATS.." - try { ^nix build --no-link (flake-target) --show-trace ...$args } + try { ^nix-build --no-out-link --show-trace $"($env.MEATS)/entry.nix" -A (system-attr) ...$args } } } @@ -102,64 +98,142 @@ def cmd-trade [] { } } -def cmd-fresh [...flakes: string] { +def pins-path [] { $"($env.MEATS)/pins/pins.toml" } +def lock-path [] { $"($env.MEATS)/pins/pins.lock.json" } + +def load-pins [] { + let p = pins-path + if not ($p | path exists) { error make { msg: $"no pins file at ($p)" } } + open --raw $p | from toml +} + +def load-lock [] { + let p = lock-path + if ($p | path exists) { open --raw $p | from json } else { {} } +} + +# Sort keys alphabetically and write atomically. +def write-lock [lock: record] { + let p = lock-path + let sorted = $lock | columns | sort | reduce -f {} { |k, acc| $acc | insert $k ($lock | get $k) } + let tmp = $"($p).tmp" + $sorted | to json --indent 2 | save -f $tmp + ^mv $tmp $p +} + +# scheme:rest → expansion via {path} template, or pass-through. +def expand-shorturl [url: string, shorturls: record] { + let parts = $url | split row ":" -n 2 + if (($parts | length) < 2) { return $url } + let scheme = $parts | get 0 + if not ($scheme in ($shorturls | columns)) { return $url } + ($shorturls | get $scheme) | str replace --regex '\{path\}' ($parts | get 1) +} + +# Decompose to {git_url, ref?} for cheap ls-remote queries (used by `look`). +def parse-git-target [url: string] { + if ($url | str starts-with "github:") { + let body = $url | str substring 7.. + let path_query = $body | split row "?" -n 2 + let segs = ($path_query | get 0) | split row "/" + let owner = $segs | get 0 + let repo = $segs | get 1 + let ref = if (($segs | length) > 2) { $segs | skip 2 | str join "/" } else { null } + return { git_url: $"https://github.com/($owner)/($repo).git", ref: $ref } + } + if ($url | str starts-with "git+") { + let stripped = $url | str substring 4.. + let parts = $stripped | split row "?" -n 2 + let base = $parts | get 0 + let ref = if (($parts | length) > 1) { + ($parts | get 1) | split row "&" | each { |kv| + let kvp = $kv | split row "=" -n 2 + { k: ($kvp | get 0), v: ($kvp | get 1?) } + } | where k == "ref" | get -o 0 | get -o v + } else { null } + return { git_url: $base, ref: $ref } + } + error make { msg: $"unsupported url scheme for ls-remote: ($url)" } +} + +# Cheap "is upstream ahead?" check via git ls-remote. Returns the rev string. +def ls-remote-head [url: string] { + let tgt = parse-git-target $url + let target_ref = if ($tgt.ref? | is-not-empty) { $"refs/heads/($tgt.ref)" } else { "HEAD" } + let r = ^git ls-remote $tgt.git_url $target_ref | complete + if $r.exit_code != 0 { error make { msg: $"ls-remote failed: ($r.stderr)" } } + let first = $r.stdout | str trim | lines | get -o 0 + if ($first | is-empty) { error make { msg: "no refs returned" } } + $first | split row "\t" | get 0 +} + +# Real prefetch — caches in /nix/store and returns the full locked attrset. +# Output JSON: { hash: SRI, locked: {...}, original: {...}, storePath: PATH } +def prefetch-pin [url: string] { + let r = ^nix flake prefetch --json $url | complete + if $r.exit_code != 0 { error make { msg: $"prefetch failed for ($url): ($r.stderr)" } } + let j = $r.stdout | from json + $j.locked | insert narHash $j.hash +} + +def cmd-fresh [...names: string] { with-frame { meat-print "HUNTING FRESH MEATS.." - let root = $env.MEATS - if ($flakes | is-empty) { - try { ^nix flake update --flake $root } - } else { - for f in $flakes { - if ($f | str downcase) == "meat" { - meat-print "PROCESSING REAL MEAT.." - } else { - meat-print $"PROCESSING FRESH MEAT ($f | str upcase).." + let pins = load-pins + let shorturls = $pins.shorturls? | default {} + let inputs = $pins.inputs + mut lock = load-lock + + let targets = if ($names | is-empty) { $inputs | columns } else { $names } + + for name in $targets { + if not ($name in ($inputs | columns)) { + meat-print $"NO MEAT CALLED ($name | str upcase).." + continue + } + meat-print $"PROCESSING ($name | str upcase).." + try { + let pin = $inputs | get $name + let expanded = expand-shorturl $pin.url $shorturls + let entry = prefetch-pin $expanded + let old_rev = ($lock | get -o $name) | default {} | get -o rev + let new_rev = $entry.rev + if $old_rev == $new_rev { + meat-print $" ($name | str upcase) STILL FRESH" + continue } - try { ^nix flake update $f --flake $root } + $lock = $lock | upsert $name $entry + write-lock $lock + let from = if ($old_rev | is-empty) { "NEW" } else { $old_rev | str substring 0..8 } + meat-print $" ($name | str upcase): ($from) -> ($new_rev | str substring 0..8)" + } catch { |e| + meat-print $" NO FIND ($name | str upcase): ($e.msg)" } } } print "" } -def lookup-input [nodes: record, name: string, node_ref: any] { - let node_name = if (($node_ref | describe) == "string") { $node_ref } else { $name } - let node = $nodes | get $node_name - let locked = $node.locked - let original = $node.original - let url = match $locked.type { - "github" => $"https://github.com/($locked.owner)/($locked.repo).git" - "git" => $original.url - _ => { error make { msg: $"unsupported type: ($locked.type)" } } - } - let target_ref = if ('ref' in $original) { $"refs/heads/($original.ref)" } else { "HEAD" } - let out = ^git ls-remote $url $target_ref | complete - if $out.exit_code != 0 { - error make { msg: "git ls-remote failed" } - } - let first_line = $out.stdout | str trim | lines | get 0? - if ($first_line | is-empty) { - error make { msg: "no refs returned" } - } - let latest = $first_line | split row "\t" | first - { name: $name, locked: $locked.rev, latest: $latest } -} - def cmd-look [] { with-frame { meat-print "LOOK FOR NEW MEATS.." - let lock = open --raw $"($env.MEATS)/flake.lock" | from json - let nodes = $lock.nodes - let root_inputs = $nodes.root.inputs - let stale = $root_inputs | transpose name node_ref | each { |row| + let pins = load-pins + let shorturls = $pins.shorturls? | default {} + let inputs = $pins.inputs + let lock = load-lock + + let stale = $inputs | transpose name pin | each { |row| try { - let r = lookup-input $nodes $row.name $row.node_ref - if $r.latest != $r.locked { - meat-print $"($r.name | str upcase) MEAT RIPE!" - $r.name + let expanded = expand-shorturl $row.pin.url $shorturls + let new_rev = ls-remote-head $expanded + let old_rev = ($lock | get -o $row.name) | default {} | get -o rev + if $old_rev != $new_rev { + let from = if ($old_rev | is-empty) { "NEW" } else { $old_rev | str substring 0..8 } + meat-print $" ($row.name | str upcase): ($from) -> ($new_rev | str substring 0..8)" + $row.name } else { null } } catch { - meat-print $"NO FIND ($row.name | str upcase).." + meat-print $" NO FIND ($row.name | str upcase).." null } } | compact