add utils subcmds

This commit is contained in:
Daylin Morgan 2025-01-25 18:20:26 -06:00
parent a0ac1b4520
commit 133ddc761f
Signed by: daylin
GPG key ID: 950D13E9719334AD
5 changed files with 160 additions and 25 deletions

View file

@ -1,7 +1,7 @@
## nix begat oizys
import std/[os, osproc, sequtils, strformat, strutils, tables]
import hwylterm, hwylterm/[hwylcli]
import oizys/[context, github, nix, logging]
import oizys/[context, github, nix, logging, utils]
proc checkExes() =
if findExe("nix") == "":
@ -37,6 +37,7 @@ hwylCli:
- m
preSub:
setupLoggers()
echo "ORIGINAL UPDATE CONTEXT"
updateContext(host, flake, verbose, resetCache)
subcommands:
@ -74,11 +75,6 @@ hwylCli:
[ci]
... "builtin ci"
# BUG: current behavior adds this block twice...
# when really I want it to only happen in the lowest "subcommand"
# needs to be fixed in hwylterm
preSub:
updateContext(host, flake, verbose, resetCache)
subcommands:
[update]
... "build current and updated hosts"
@ -162,3 +158,32 @@ hwylCli:
updateRepo()
nixosRebuild(NixosRebuildSubcmd.switch)
[utils]
... """
less common utils operations
some snippets I've reimplemented in nim so that nix isn't as annoying
"""
subcommands:
[hash]
... "collect build hash from failure"
positionals:
installable string
run:
stdout.write getBuildHash(installable)
[narinfo]
... """
check active caches for nix derivation
by default will use [yellow]nix config show[/] to determine
the binary cache urls
"""
positionals:
installables seq[string]
flags:
cache:
? "url of nix binary cache, can be repeated"
T seq[string]
run:
checkForCache(installables, cache)

View file

@ -7,8 +7,14 @@ import hwylterm
import hwylterm/spin/spinners # todo: remove after hwylterm update
func addArgs*(cmd: var string, args: varargs[string]) =
func addArgs*(cmd: string, args: varargs[string]): string =
## append to string for command
result = cmd & " " & args.join(" ")
func addArgs*(cmd: var string, args: varargs[string]): string {.discardable.} =
## append to string for command
cmd &= " " & args.join(" ")
result = cmd
# deprecate in favor of above?
func addArg*(cmd: var string, arg: string ) =
@ -50,32 +56,39 @@ proc runCmdCapt*(
result.stderr.add line & '\n'
result.exitCode = peekExitCode(p)
if result.exitCode != -1:
if CaptStdout in capture:
result.stdout.add outstrm.readAll()
if CaptStderr in capture:
result.stderr.add errstrm.readAll()
break
close p
proc formatStdoutStderr(stdout: string, stderr: string): BbString =
proc formatSubprocessError*(s: string): BbString =
for line in s.splitLines():
result.add bb("[red]->[/] " & line & "\n")
proc formatStdoutStderr*(stdout: string, stderr: string): BbString =
template add(stream: string) =
if stream.strip() != "":
result.add astToStr(stream).bb("bold") & ":\n"
for line in stream.splitlines():
result.add bb("[red]->[/] " & line & "\n")
result.add formatSubprocessError(stream)
add(stdout)
add(stderr)
proc runCmdCaptWithSpinner*(
cmd: string,
msg: BbString | string = bb"",
capture: set[CaptureGrp] = {CaptStdout}
capture: set[CaptureGrp] = {CaptStdout},
check: bool = true
): tuple[output, err: string] =
var
output, err: string
code: int
with(Dots2, msg):
(output, err, code) = runCmdCapt(cmd, capture)
if code != 0:
if check and code != 0:
stderr.write($formatStdoutStderr(output,err))
error fmt"{cmd} had non zero exit"
quit code

View file

@ -3,11 +3,12 @@ import std/[
enumerate, os, sequtils, sets, strformat,
strutils, sugar, logging, tables, times
]
export tables
import hwylterm, hwylterm/logging, jsony
import ./[context, exec]
proc nixCommand(cmd: string, nom: bool = false): string =
proc nixCommand*(cmd: string, nom: bool = false): string =
if nom:
if findExe("nom") == "":
fatalQuit "--nom requires nix-output-monitor is installed"
@ -53,13 +54,13 @@ proc nixosRebuild*(subcmd: NixosRebuildSubcmd, args: openArray[string] = [], rem
type
Derivation = object
storePath, hash, name: string
storePath*, hash*, name*: string
DryRunOutput = object
toBuild: seq[Derivation]
toFetch: seq[Derivation]
func toDerivation(pkg: string): Derivation =
func toDerivation*(pkg: string): Derivation =
let path = pkg.strip()
let s = path.split("-", 1)
result.storePath = path
@ -108,7 +109,7 @@ proc parseDryRunOutput(err: string): DryRunOutput =
result.toBuild.sort(cmpDrv)
result.toFetch.sort(cmpDrv)
proc trunc(s: string, limit: int): string =
proc trunc*(s: string, limit: int): string =
if s.len <= limit:
s
else:
@ -143,22 +144,30 @@ proc toBuildNixosConfiguration(): seq[string] =
type
DerivationOutput = object
path: string
path*: string
# hashAlgo: string
# hash: string
NixDerivation = object
inputDrvs: Table[string, JsonNode]
name: string
outputs: Table[string, DerivationOutput]
inputDrvs*: Table[string, JsonNode]
name*: string
outputs*: Table[string, DerivationOutput]
proc evaluateDerivations(drvs: seq[string]): Table[string, NixDerivation] =
# here a results var would be nice...
proc narHash*(s: string): string =
## get hash from nix store path
if not s.startsWith("/nix/store/") and s.len >= 44:
fatalQuit "failed to extract narHash from: " & s
let ss = s.split("-")
result = ss[0].split("/")[^1]
proc evaluateDerivations(drvs: openArray[string]): Table[string, NixDerivation] =
var cmd = "nix derivation show -r"
cmd.addArgs drvs
let (output, _) =
runCmdCaptWithSpinner(cmd, "evaluating derivations")
fromJson(output, Table[string, NixDerivation])
proc nixDerivationShow(drvs: seq[string]): Table[string, NixDerivation] =
proc nixDerivationShow*(drvs: openArray[string]): Table[string, NixDerivation] =
var cmd = "nix derivation show"
cmd.addArgs drvs
let (output, _ ) =

View file

@ -0,0 +1,89 @@
import std/[strformat, strutils, osproc, sugar, httpclient]
import hwylterm
import ./[nix, exec,logging]
# TODO: refactor runCmdCaptWithSpinner so it works in getBuildHash
proc checkBuild(installable: string): tuple[stdout: string, stderr: string] =
var
output, err: string
code: int
let cmd = nixCommand("build").addArgs(installable)
with(Dots2, bbfmt"attempt to build: [b]{installable}"):
(output, err, code) = runCmdCapt(cmd, capture = {CaptStdout, CaptStderr})
if code == 0:
fatalQuit fmt"{cmd} had zero exit"
result = (output, err)
proc getBuildHash*(installable: string): string =
let (output, err) = checkBuild(installable)
for line in err.splitLines():
if line.strip().startsWith("got: "):
let s = line.split("got:")
result = s[1].strip()
if result == "":
stderr.write formatStdoutStderr(output, err) & "\n"
fatalQuit "failed to find update hash from above output"
proc getCaches(): seq[string] =
## use nix to get the current cache urls
debug "determing caches to check"
let (output, code) = execCmdEx("nix config show")
if code != 0:
echo formatSubprocessError(output)
fatalQuit "error running `nix config show`"
for line in output.splitLines():
if line.startsWith("substituters ="):
let s = line.split("=")[1].strip()
for u in s.split():
result.add u
if result.len == 0:
echo formatSubprocessError(output)
fatalQuit "error running `nix config show`"
proc hasNarinfo*(cache: string, path: string): bool =
debug fmt"checking {cache} for {path}"
let
hash = narHash(path)
url = cache & "/" & hash & ".narinfo"
try:
let client = newHttpClient()
result = client.head(url).code == Http200
except:
result = false
proc prettyDerivation(path: string): BbString =
let drv = path.toDerivation()
const maxLen = 40
result.add drv.name.trunc(maxLen).alignLeft(maxLen)
result.add " "
result.add drv.hash.bb("faint")
proc checkForCache*(installables: seq[string], caches: seq[string]) =
let caches =
if caches.len > 0: caches
else: getCaches()
let drvs = nixDerivationShow(installables)
# outputs['outs'] might blow up
let outs = collect:
for name, drv in drvs:
{name: drv.outputs["out"].path}
for name, path in outs:
var found = false
for cache in caches:
if hasNarinfo(cache, path):
found = true
info prettyDerivation(path)
info fmt"exists in {cache}"
break
if not found:
info fmt"failed to find:"
info prettyDerivation(path)

View file

@ -4,7 +4,6 @@
Could make an `oizys check` command:
- `utils hash` given some flake-url or path attempt to build and extract "got: <hash>" string
- `check lock` could encapsulate any diagnostic code I want to run that does things like check for too many "inputs".
essentially getting the same output as this `jq < flake.lock '.nodes | keys[] | select(contains("_"))`
- `utils cache <path>` check for the narinfo in my cache given some nix store path