nu
This commit is contained in:
parent
e598b04cae
commit
2d564db596
28 changed files with 436 additions and 742 deletions
376
meat.nu
Normal file
376
meat.nu
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
#!/usr/bin/env nu
|
||||
|
||||
const HEADER = "
|
||||
----- MEAT ----------------------------------------
|
||||
"
|
||||
|
||||
const FOOTER = "
|
||||
---------------------------------------------------
|
||||
"
|
||||
|
||||
const HELP_TEXT = "
|
||||
YUM - CONSUME DELICIOUS MEATS
|
||||
COOK - ONLY PREPARE MEATS
|
||||
POKE - TASTE SUSPICIOUS MEATS
|
||||
GUT - CLEAN MEAT STORES
|
||||
FRESH - HUNT FRESH MEATS
|
||||
LOOK - LOOK FOR FRESHER MEATS
|
||||
HUNT - HUNT FOR MEATS IN NIXPKGS
|
||||
RITUAL - PERFORM RITUAL THEN CONSUME
|
||||
TRADE - SEND MEATS FAR AWAY
|
||||
..-A - ..ALL MEATS"
|
||||
|
||||
def meat-print [text: string] {
|
||||
print $"\n \t($text)\n"
|
||||
}
|
||||
|
||||
def with-frame [body: closure] {
|
||||
print -n $HEADER
|
||||
do $body
|
||||
print -n $FOOTER
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
def nix-conf-attr []: nothing -> string {
|
||||
$"nixosConfigurations.(hn).config.environment.etc.\"nix/nix.conf\""
|
||||
}
|
||||
|
||||
def nix-build-nom [out: string, source: string, attr: string, extras: list<string> = []] {
|
||||
if ($env.MONITOR? | is-empty) {
|
||||
^nix-build --out-link $out $source -A $attr ...$extras
|
||||
} else {
|
||||
^nix-build --log-format internal-json --out-link $out $source -A $attr ...$extras out+err>| ^$env.MONITOR --json
|
||||
}
|
||||
}
|
||||
|
||||
def differ-step [build: string] {
|
||||
if ($env.DIFFER? | is-empty) {
|
||||
meat-print " (no DIFFER set; skipping diff)"
|
||||
} else {
|
||||
^$env.DIFFER /nix/var/nix/profiles/system $build
|
||||
}
|
||||
}
|
||||
|
||||
def activate [build_path: string] {
|
||||
^sudo $nu.current-exe -c $"nix-env --set -p /nix/var/nix/profiles/system ($build_path); ($build_path)/bin/switch-to-configuration switch"
|
||||
}
|
||||
|
||||
def do-build [extras: list<string> = []] {
|
||||
let tmpdir = ^mktemp -d -t "meat-build.XXXXXX" | str trim
|
||||
let build = $"($tmpdir)/build"
|
||||
nix-build-nom $build $"($env.MEATS)/entry.nix" (system-attr) $extras
|
||||
differ-step $build
|
||||
activate $build
|
||||
try { rm $build }
|
||||
}
|
||||
|
||||
def cmd-help [] {
|
||||
print -n $"($HEADER)($HELP_TEXT)($FOOTER)\n"
|
||||
}
|
||||
|
||||
def cmd-yum [...args: string] {
|
||||
with-frame {
|
||||
meat-print "CONSUMING DELICIOUS MEATS.."
|
||||
try { do-build $args } catch { print "FAILED TO CONSUME MEATS." }
|
||||
}
|
||||
}
|
||||
|
||||
def cmd-cook [...args: string] {
|
||||
with-frame {
|
||||
meat-print "PREPARING DELICIOUS MEATS.."
|
||||
try { ^nix build --no-link (flake-target) ...$args }
|
||||
}
|
||||
}
|
||||
|
||||
def cmd-poke [...args: string] {
|
||||
with-frame {
|
||||
meat-print "PREPARING SUSPICIOUS MEATS.."
|
||||
try { ^nix build --no-link (flake-target) --show-trace ...$args }
|
||||
}
|
||||
}
|
||||
|
||||
def cmd-gut [...args: string] {
|
||||
with-frame {
|
||||
meat-print "CLEANING MEAT STORES.."
|
||||
try { ^nh clean all ...$args }
|
||||
}
|
||||
}
|
||||
|
||||
def cmd-trade [] {
|
||||
with-frame {
|
||||
meat-print "TRADING FOREIGN MEATS.."
|
||||
meat-print "tbd"
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
# --refresh bypasses the ref→rev cache (otherwise stale within tarball-ttl).
|
||||
def prefetch-pin [url: string] {
|
||||
let r = ^nix flake prefetch --refresh --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).."
|
||||
}
|
||||
try { ^nix flake update $f --flake $root }
|
||||
}
|
||||
}
|
||||
}
|
||||
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|
|
||||
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
|
||||
} else { null }
|
||||
} catch {
|
||||
meat-print $"NO FIND ($row.name | str upcase).."
|
||||
null
|
||||
}
|
||||
} | compact
|
||||
if ($stale | is-empty) {
|
||||
meat-print "NO MEAT FRESHER"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def truncate-desc [s: string, n: int]: nothing -> string {
|
||||
if (($s | str length) > $n) {
|
||||
($s | str substring 0..$n) + ".."
|
||||
} else {
|
||||
$s
|
||||
}
|
||||
}
|
||||
|
||||
def cmd-hunt [...query: string] {
|
||||
with-frame {
|
||||
if ($query | is-empty) {
|
||||
meat-print "WHAT MEAT YOU SEEK?"
|
||||
return
|
||||
}
|
||||
let q = $query | str join " "
|
||||
meat-print $"HUNTING FOR ($q | str upcase).."
|
||||
let body = {
|
||||
from: 0,
|
||||
size: 20,
|
||||
sort: [{ _score: "desc", package_attr_name: "desc", package_pversion: "desc" }],
|
||||
collapse: { field: "package_attr_name" },
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { type: "package" } },
|
||||
{ multi_match: {
|
||||
type: "cross_fields",
|
||||
query: $q,
|
||||
fields: ["package_attr_name^9", "package_pname^6", "package_description^1.3", "package_longDescription^1"]
|
||||
} }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
let resp = (
|
||||
http post
|
||||
--user "aWVSALXpZv"
|
||||
--password "X8gPHnzL52wFEekuxsfQ9cSh"
|
||||
--content-type "application/json"
|
||||
"https://nixos-search-7-1733963800.us-east-1.bonsaisearch.net/nixos-*-unstable-*/_search"
|
||||
$body
|
||||
)
|
||||
let hits = $resp.hits?.hits? | default []
|
||||
if ($hits | is-empty) {
|
||||
meat-print "NO MEATS FOUND!"
|
||||
} else {
|
||||
for hit in $hits {
|
||||
let src = $hit._source
|
||||
let name = $src.package_attr_name? | default "?"
|
||||
let ver = $src.package_pversion? | default ""
|
||||
let desc = $src.package_description? | default ""
|
||||
let ver_str = if ($ver | is-empty) { "" } else { $" \(($ver)\)" }
|
||||
print $" \tnixpkgs#($name)($ver_str)"
|
||||
if not ($desc | is-empty) {
|
||||
print $" \t (truncate-desc $desc 60)"
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { |e|
|
||||
meat-print $"HUNT FAILED: ($e.msg)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def cmd-ritual [] {
|
||||
with-frame {
|
||||
meat-print "PREPARING RITUAL GROUND.."
|
||||
let tmpdir = ^mktemp -d -t "meat-chew.XXXXXX" | str trim
|
||||
let meats = $env.MEATS
|
||||
let nix_conf = $"($tmpdir)/nix.conf"
|
||||
let build = $"($tmpdir)/build"
|
||||
try {
|
||||
nix-build-nom $nix_conf $"($meats)/entry.nix" (nix-conf-attr)
|
||||
meat-print "CONSUMING MEATS.."
|
||||
with-env { NIX_USER_CONF_FILES: $nix_conf } {
|
||||
nix-build-nom $build $"($meats)/entry.nix" (system-attr)
|
||||
}
|
||||
^$env.DIFFER /nix/var/nix/profiles/system $build
|
||||
activate $build
|
||||
try { rm $nix_conf }
|
||||
try { rm $build }
|
||||
} catch {
|
||||
print "FAILED TO CONSUME MEATS."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def cmd-shelter [new_closure: string] {
|
||||
activate $new_closure
|
||||
}
|
||||
|
||||
def main [...args: string] {
|
||||
if ($env.MEATS? | is-empty) {
|
||||
meat-print "NO PATH TO RUNESTONE FOUND!"
|
||||
cmd-help
|
||||
return
|
||||
}
|
||||
let sub = $args | get 0? | default "" | str downcase
|
||||
let rest = $args | skip 1
|
||||
match $sub {
|
||||
"yum" => { cmd-yum ...$rest }
|
||||
"cook" => { cmd-cook ...$rest }
|
||||
"poke" => { cmd-poke ...$rest }
|
||||
"gut" => { cmd-gut ...$rest }
|
||||
"trade" => { cmd-trade }
|
||||
"look" => { cmd-look }
|
||||
"fresh" => { cmd-fresh ...$rest }
|
||||
"hunt" => { cmd-hunt ...$rest }
|
||||
"ritual" => { cmd-ritual }
|
||||
"shelter" => { cmd-shelter $rest }
|
||||
_ => { cmd-help }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue