oizys nim wip

This commit is contained in:
Daylin Morgan 2024-08-28 11:08:58 -05:00
parent d1b7c652d2
commit 8fe319e525
Signed by: daylin
GPG key ID: 950D13E9719334AD
21 changed files with 1035 additions and 39 deletions

View file

@ -20,11 +20,11 @@
]
},
"locked": {
"lastModified": 1724273991,
"narHash": "sha256-+aUSOXKGpS5CRm1oTitgNAr05ThQNbKIXalZHl3nC6Y=",
"lastModified": 1724781866,
"narHash": "sha256-ItgACCJCwn8Rx7p8hJBpnU9eCtrdmkg4AbqMZL/rXlY=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "9a3161ad4c78dc420d1cbb3aae638222608c7de4",
"rev": "7cc3d3179c06caf3769afb3eb0c69aa55676c96a",
"type": "github"
},
"original": {
@ -323,11 +323,11 @@
"xdph": "xdph"
},
"locked": {
"lastModified": 1724701003,
"narHash": "sha256-kTceEi5B4t2u5oG3bKnGgZkHxFTsvYAUiAmyYA/6Y3o=",
"lastModified": 1724850433,
"narHash": "sha256-WSjYdnlg6Qq0xrjCgL/WJ7a+kEWQtQmYrw0TV4ykppY=",
"ref": "refs/heads/main",
"rev": "eb42adc4c090918ad6be9fcb24066da8cdfd9bd0",
"revCount": 5145,
"rev": "98e99cd03df5b4421f72f2a3f2d7de53f8261f1f",
"revCount": 5152,
"submodules": true,
"type": "git",
"url": "https://github.com/hyprwm/Hyprland/"
@ -504,11 +504,11 @@
"lix": {
"flake": false,
"locked": {
"lastModified": 1724714224,
"narHash": "sha256-Fn72c2ycgYERAdwWEPmIUhuOmgOwuvZhTNdqOuiR0uw=",
"rev": "0dc486a5bf218870aafd5552586ab4330881647e",
"lastModified": 1724863402,
"narHash": "sha256-2xrZAo4m0SNklS71RCfWzNXu79OAZMrHNpe6RGOh4OY=",
"rev": "422550fd68a5877534b1ca577fc3c7d89b6706dd",
"type": "tarball",
"url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/0dc486a5bf218870aafd5552586ab4330881647e.tar.gz?rev=0dc486a5bf218870aafd5552586ab4330881647e"
"url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/422550fd68a5877534b1ca577fc3c7d89b6706dd.tar.gz?rev=422550fd68a5877534b1ca577fc3c7d89b6706dd"
},
"original": {
"type": "tarball",
@ -538,11 +538,29 @@
"url": "https://git.lix.systems/lix-project/nixos-module/archive/main.tar.gz"
}
},
"nim2nix": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1724878933,
"narHash": "sha256-VP0+Lal3jJJqDH1EzQX73rP9Ue8ZTIyAErJvDz6PQSg=",
"owner": "daylinmorgan",
"repo": "nim2nix",
"rev": "5153c772e4c6a4f5645efa85ce536fe1c5063ebb",
"type": "github"
},
"original": {
"owner": "daylinmorgan",
"repo": "nim2nix",
"type": "github"
}
},
"nix-eval-jobs": {
"inputs": {
"flake-parts": "flake-parts",
"nix-github-actions": "nix-github-actions",
"nixpkgs": "nixpkgs_5",
"nixpkgs": "nixpkgs_6",
"treefmt-nix": "treefmt-nix"
},
"locked": {
@ -630,7 +648,7 @@
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_3"
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1724664098,
@ -682,14 +700,14 @@
"flake-compat": "flake-compat_2",
"lib-aggregate": "lib-aggregate",
"nix-eval-jobs": "nix-eval-jobs",
"nixpkgs": "nixpkgs_6"
"nixpkgs": "nixpkgs_7"
},
"locked": {
"lastModified": 1724746994,
"narHash": "sha256-krOFIWrWMW44GBpjjKKo4dcM6oPXI9YpFj+Wqf2PwxU=",
"lastModified": 1724858360,
"narHash": "sha256-Z78j0lWk4j8U5jC34S2ON37I5Z4r9yJt1orN4PBIra0=",
"owner": "nix-community",
"repo": "nixpkgs-wayland",
"rev": "f42d86203af2967f81c50cf4f0722c013bc22170",
"rev": "4fad2594f6dc0c0ac3b58272b7060b51d460b607",
"type": "github"
},
"original": {
@ -715,6 +733,22 @@
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1724479785,
"narHash": "sha256-pP3Azj5d6M5nmG68Fu4JqZmdGt4S4vqI5f8te+E/FTw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d0e1602ddde669d5beb01aec49d71a51937ed7be",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1724316499,
"narHash": "sha256-Qb9MhKBUTCfWg/wqqaxt89Xfi6qTD3XpTzQ9eXi3JmE=",
@ -730,13 +764,13 @@
"type": "github"
}
},
"nixpkgs_4": {
"nixpkgs_5": {
"locked": {
"lastModified": 1724395761,
"narHash": "sha256-zRkDV/nbrnp3Y8oCADf5ETl1sDrdmAW6/bBVJ8EbIdQ=",
"lastModified": 1724748588,
"narHash": "sha256-NlpGA4+AIf1dKNq76ps90rxowlFXUsV9x7vK/mN37JM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "ae815cee91b417be55d43781eb4b73ae1ecc396c",
"rev": "a6292e34000dc93d43bccf78338770c1c5ec8a99",
"type": "github"
},
"original": {
@ -746,7 +780,7 @@
"type": "github"
}
},
"nixpkgs_5": {
"nixpkgs_6": {
"locked": {
"lastModified": 1723221148,
"narHash": "sha256-7pjpeQlZUNQ4eeVntytU3jkw9dFK3k1Htgk2iuXjaD8=",
@ -762,7 +796,7 @@
"type": "github"
}
},
"nixpkgs_6": {
"nixpkgs_7": {
"locked": {
"lastModified": 1724479785,
"narHash": "sha256-pP3Azj5d6M5nmG68Fu4JqZmdGt4S4vqI5f8te+E/FTw=",
@ -809,11 +843,11 @@
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1724759134,
"narHash": "sha256-E4nBGiBGkdB1e01rGLAEiDhM5cKMnVbRPUXwXf3CjaU=",
"lastModified": 1724859070,
"narHash": "sha256-/jeh/OJm5+gmg+dQxpWetL9jgjk+zGe7uCRLvbNvukA=",
"owner": "roc-lang",
"repo": "roc",
"rev": "7811d8876865dc9927d707edb8fd8a426a595a0e",
"rev": "479feca39618a58770db68b44072be1f29df8d84",
"type": "github"
},
"original": {
@ -830,9 +864,10 @@
"hyprman": "hyprman",
"lix": "lix",
"lix-module": "lix-module",
"nim2nix": "nim2nix",
"nix-index-database": "nix-index-database",
"nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs_4",
"nixpkgs": "nixpkgs_5",
"nixpkgs-wayland": "nixpkgs-wayland",
"pixi": "pixi",
"roc": "roc",
@ -1040,11 +1075,11 @@
]
},
"locked": {
"lastModified": 1723841055,
"narHash": "sha256-ykjmNEXFJ4lrW1LeB4dVsNlPKlNB0VWNnOTp1sLbOqQ=",
"lastModified": 1724782685,
"narHash": "sha256-/gzaaWVqXbnj78pNC7cOW/MDZidO127v9QRpAyBgczU=",
"ref": "refs/heads/main",
"rev": "eca60579ef577ad45aa1a371f006697ddc2611d6",
"revCount": 5,
"rev": "5289c14095ec7fe0676a0dd3c3084ead095da1a5",
"revCount": 6,
"type": "git",
"url": "https://git.dayl.in/daylin/utils.git"
},
@ -1092,11 +1127,11 @@
]
},
"locked": {
"lastModified": 1724718400,
"narHash": "sha256-i83icUP6218jkNLBiHj2JyhKrVbgkU2mOGFvpFjw0WM=",
"lastModified": 1724847079,
"narHash": "sha256-bbcvuJqmjShg6Uet6CptnxkuPBRL2drQmvnaXi832Xk=",
"owner": "mitchellh",
"repo": "zig-overlay",
"rev": "20a8e0316531f825284ef15fe20b4f9848fbb4e4",
"rev": "ce690c0c732a9627a9736f145d1739d56d4905c4",
"type": "github"
},
"original": {

View file

@ -26,6 +26,7 @@
zig-overlay.url = "github:mitchellh/zig-overlay";
zls.url = "github:zigtools/zls";
nim2nix.url = "github:daylinmorgan/nim2nix";
pixi.url = "github:daylinmorgan/pixi-flake";
f1multiviewer.url = "github:daylinmorgan/f1multiviewer-flake";
tsm.url = "github:daylinmorgan/tsm?dir=nix";

View file

@ -12,7 +12,8 @@
nix-ld = enabled // {
overkill = enabled;
};
languages = "misc|nim|node|nushell|python|roc|tex|zig" |> listify;
# languages = "misc|nim|node|nushell|python|roc|tex|zig" |> listify;
languages = "misc|nim|node|nushell|python|roc|tex" |> listify;
}
// (
''

View file

@ -9,7 +9,10 @@ let
inherit (import ./generators.nix { inherit lib self inputs; }) mkIso mkSystem;
#supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
supportedSystems = [ "x86_64-linux" ];
forAllSystems = f: genAttrs supportedSystems (system: f (import nixpkgs { inherit system; }));
forAllSystems = f: genAttrs supportedSystems (system: f (import nixpkgs {
inherit system;
overlays = [inputs.nim2nix.overlays.default];
}));
inheritFlakePkgs =
pkgs: flakes:
@ -27,6 +30,7 @@ let
pkgs:
rec {
default = oizys-cli;
oizys-nim = pkgs.callPackage ../pkgs/oizys-nim { };
oizys-cli = pkgs.callPackage ../pkgs/oizys { };
iso = mkIso.config.system.build.isoImage;
roc = (pkgsFromSystem pkgs.system "roc").full;
@ -39,10 +43,11 @@ let
);
devShells = forAllSystems (pkgs: {
default = pkgs.mkShell {
oizys = pkgs.mkShell {
packages = with pkgs; [
git
deadnix
openssl
nim
nimble
];
};
});

1
pkgs/oizys-nim/.envrc Normal file
View file

@ -0,0 +1 @@
use flake "../..#oizys"

9
pkgs/oizys-nim/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
nimbledeps
nimble.paths
nimble.develop
# binaries
oizys*
!src/oizys
!src/oizys.nim*
oizys.out

View file

@ -0,0 +1,8 @@
task build, "build oizys":
selfExec "c -o:oizys src/oizys.nim"
# begin Nimble config (version 2)
--noNimblePath
when withDir(thisDir(), system.fileExists("nimble.paths")):
include "nimble.paths"
# end Nimble config

View file

@ -0,0 +1,12 @@
{
lib,
openssl,
buildNimblePackage,
}:
buildNimblePackage {
name = "oizys";
verions = "unstable";
src = lib.cleanSource ./.;
nativeBuildInputs = [ openssl ];
nimbleDepsHash = "sha256-Eheeve4MbB3v1oVj0mB36Mv2Q3vJGLEbbShds1af23g=";
}

View file

@ -0,0 +1,36 @@
{
"version": 2,
"packages": {
"bbansi": {
"version": "0.1.1",
"vcsRevision": "9a85d9ed028f06f1ed1ee6851480a51408a6004e",
"url": "https://github.com/daylinmorgan/bbansi",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "b338433f9a7a1b788b7583674c2b14096ced29ee"
}
},
"cligen": {
"version": "1.7.0",
"vcsRevision": "4193f802796f15559c81c6dd56724d6f20345917",
"url": "https://github.com/c-blake/cligen.git",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "300bd7fdb6e48d2d98e34ed0661206b50331e99c"
}
},
"jsony": {
"version": "1.1.5",
"vcsRevision": "ea811bec7fa50f5abd3088ba94cda74285e93f18",
"url": "https://github.com/treeform/jsony",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "6aeb83e7481ca8686396a568096054bc668294df"
}
}
},
"tasks": {}
}

View file

@ -0,0 +1,16 @@
# Package
version = "0.1.0"
author = "Daylin Morgan"
description = "nix begat oizys"
license = "MIT"
srcDir = "src"
bin = @["oizys"]
# Dependencies
requires "nim >= 2.0.8"
requires "cligen"
requires "jsony"
requires "https://github.com/daylinmorgan/bbansi#9a85d9e"

View file

@ -0,0 +1,128 @@
## nix begat oizys
import std/[os, tables, sequtils, strformat,]
import cligen, bbansi
import oizys/[context, github, nix, overlay, logging]
addHandler(
newFancyConsoleLogger(
levelThreshold=lvlAll,
useStderr = true,
fmtPrefix = $bb"[b magenta]oizys"
)
)
overlay:
proc pre(
flake: string = "",
host: seq[string] = @[],
debug: bool = false,
resetCache: bool = false,
rest: seq[string],
) =
if not debug: setLogFilter(lvlInfo)
updateContext(host, flake, debug, resetCache)
proc dry(minimal: bool = false) =
## dry run build
nixBuildHostDry(minimal, rest)
proc output(yes: bool = false) =
## output
echo nixosConfigAttrs().join(" ")
proc update(yes: bool = false) =
## *TBD* update and run nixos-rebuild
fatal "not implemented"
proc build(minimal: bool = false) =
## nix build
nixBuild(minimal, rest)
proc cache(name: string = "daylin") =
## *TBD* build and push to cachix
fatal "not implemented"
proc osCmd() =
## nixos-rebuild
if len(rest) == 0: quit "please provide subcmd"
let subcmd = rest[0]
if subcmd notin nixosSubcmds:
error (
&"unknown nixos-rebuild subcmd: {subcmd}\nexpected one of: \n" &
nixosSubcmds.mapIt(" " & it).join("\n")
)
quit QuitFailure
nixosRebuild(subcmd, rest[1..^1])
proc ci(`ref`: string = "main") =
## *TBD* trigger GHA update flow
if rest.len == 0:
fatal "expected workflow file name"; quit QuitFailure
createDispatch(rest[0], `ref`)
proc checkExes() =
if findExe("nix") == "":
quit("oizys requires nix", QuitFailure)
proc `//`(t1: Table[string, string], t2: Table[string, string]): Table[string, string] =
# nix style table merge
for k, v in t1.pairs(): result[k] = v
for k, v in t2.pairs(): result[k] = v
proc setupCligen() =
let isColor = getEnv("NO_COLOR") == ""
if clCfg.useMulti == "":
clCfg.useMulti =
if isColor:
"${doc}\e[1mUsage\e[m:\n $command {SUBCMD} [sub-command options & parameters]\n\n\e[1msubcommands\e[m:\n$subcmds"
else:
"${doc}Usage:\n $command {SUBCMD} [sub-command options & parameters]\n\nsubcommands:\n$subcmds"
if not isColor: return
if clCfg.helpAttr.len == 0:
clCfg.helpAttr = {"cmd": "\e[1;36m", "clDescrip": "", "clDflVal": "\e[33m",
"clOptKeys": "\e[32m", "clValType": "\e[31m", "args": "\e[3m"}.toTable()
clCfg.helpAttrOff = {"cmd": "\e[m", "clDescrip": "\e[m", "clDflVal": "\e[m",
"clOptKeys": "\e[m", "clValType": "\e[m", "args": "\e[m"}.toTable()
# clCfg.use does nothing?
clCfg.useHdr = "\e[1mUsage\e[m:\n "
when isMainModule:
checkExes()
setupCligen()
let (optOpen, optClose) =
if getEnv("NO_COLOR") == "": ("\e[1m","\e[m")
else: ("","")
let
usage = &"$command [flags]\n$doc{optOpen}Options{optClose}:\n$options"
osUsage = &"$command [subcmd] [flags]\n$doc{optOpen}Options{optClose}:\n$options"
const
sharedHelp = {
"flake" : "path/to/flake",
"host" : "host(s) to build",
"debug" : "enable debug mode",
"resetCache" : "set cache timeout to 0"
}.toTable()
updateHelp = {
"yes" : "skip all confirmation prompts"
}.toTable() // sharedHelp
ciHelp = {
"ref" : "git ref/branch/tag to trigger workflow on"
}.toTable()
cacheHelp = {
"name" : "name of cachix binary cache"
}.toTable() // sharedHelp
# setting clCfg.use wasn't working?
dispatchMulti(
[build, help = sharedHelp, usage = usage],
[cache, help = cacheHelp, usage = usage],
[ci, help = ciHelp, usage = usage],
[dry, help = sharedHelp, usage = usage],
[osCmd, help = sharedHelp, usage = osUsage, cmdName = "os"],
[output, help = sharedHelp, usage = usage],
[update, help = updateHelp, usage = usage],
)

View file

@ -0,0 +1 @@
-d:ssl

View file

@ -0,0 +1,54 @@
import std/[logging, os, strformat, strutils]
from std/nativesockets import getHostname
type
OizysContext* = object
flake, host: string
hosts: seq[string]
debug: bool
ci: bool
resetCache: bool
proc initContext*(): OizysContext =
result.hosts = @[getHostname()]
result.flake = "github:daylinmorgan/oizys"
let localDir = getHomeDir() / "oizys"
if localDir.dirExists:
result.flake = localDir
let envVar = getEnv("OIZYS_DIR")
if envVar != "":
result.flake = envVar
result.ci = getEnv("GITHUB_STEP_SUMMARY") != ""
var oc = initContext()
proc checkPath(s: string): string =
## fail if path doesn't exist
if not s.dirExists:
error fmt"flake path: {s} does not exist"
quit()
s
# public api -------------------------------------
proc updateContext*(
host: seq[string],
flake: string,
debug: bool,
resetCache: bool
) =
oc.debug = debug
oc.resetCache = resetCache
if host.len > 0:
oc.hosts = host
if flake != "":
oc.flake =
if flake.startsWith("github") or flake.startsWith("git+"): flake
else: checkPath(flake.normalizedPath().absolutePath())
proc getHosts*(): seq[string] = return oc.hosts
proc getFlake*(): string = return oc.flake
proc isDebug*(): bool = return oc.debug
proc isResetCache*(): bool = return oc.resetCache
proc isCi*(): bool = return oc.ci

View file

@ -0,0 +1,43 @@
import std/[
osproc, strformat,
strutils, streams, logging
]
import ./spin
func addArgs*(cmd: var string, args: seq[string]) =
cmd &= " " & args.join(" ")
func addArg*(cmd: var string, arg: string) =
cmd &= " " & arg
proc runCmd*(cmd: string): int =
debug fmt"running cmd: {cmd}"
execCmd cmd
proc runCmdCapt*(cmd: string): tuple[stdout, stderr: string, exitCode: int] =
let args = cmd.splitWhitespace()
let p = startProcess(args[0], args = args[1..^1], options = {poUsePath})
result = (
readAll p.outputStream,
readAll p.errorStream,
waitForExit p
)
close p
proc runCmdCaptWithSpinner*(cmd: string, msg: string = ""): tuple[output, err: string] =
debug fmt"running command: {cmd}"
withSpinner(msg):
let (output, err, code) = runCmdCapt(cmd)
if code != 0:
stderr.writeLine("stdout\n" & output)
stderr.writeLine("stderr\n" & err)
error fmt"{cmd} had non zero exit"
quit code
return (output, err)
proc quitWithCmd*(cmd: string) =
debug cmd
quit(execCmd cmd)

View file

@ -0,0 +1,39 @@
import std/[httpclient,logging, os, strformat, strutils, json]
var ghToken = getEnv("GITHUB_TOKEN")
#[curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <YOUR-TOKEN>" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/OWNER/REPO/actions/workflows/WORKFLOW_ID/dispatches \
-d '{"ref":"topic-branch","inputs":{"name":"Mona the Octocat","home":"San Francisco, CA"}}'
]#
proc postGhApi(url: string, body: JsonNode) =
if ghToken == "": fatal "GITHUB_TOKEN not set"; quit QuitFailure
let client = newHttpClient()
client.headers = newHttpHeaders({
"Accept" : "application/vnd.github+json",
"Authorization" : fmt"Bearer {ghToken}",
"X-GitHub-Api-Version": "2022-11-28",
})
echo $body
let response = client.post(url, body = $body)
info fmt"Status: {response.status}"
proc createDispatch*(workflowFileName: string, `ref`: string) =
## https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event
var workflow =
if workflowFileName.endsWith(".yml") or workflowFileName.endsWith(".yaml"): workflowFileName
else: workflowFileName & ".yml"
info fmt"creating dispatch event for {workflow}"
postGhApi(
fmt"https://api.github.com/repos/daylinmorgan/oizys/actions/workflows/{workflow}/dispatches",
%*{
"ref": `ref`,
}
)

View file

@ -0,0 +1,21 @@
ld-library-path
builder.pl
profile
system-path
nixos-help
nixos-install
nixos-version
nixos-manual-html
nixos-rebuild
nixos-configuration-reference-manpage
nixos-generate-config
nixos-enter
nixos-container
nixos-build-vms
nixos-wsl-version
nixos-wsl-welcome-message
nixos-wsl-welcome
restic-gdrive
gitea
lock
code

View file

@ -0,0 +1,108 @@
import std/[logging,strutils]
export logging
import bbansi
var
handlers {.threadvar.}: seq[Logger]
#[
Level* = enum ## \
lvlAll, ## All levels active
lvlDebug, ## Debug level and above are active
lvlInfo, ## Info level and above are active
lvlNotice, ## Notice level and above are active
lvlWarn, ## Warn level and above are active
lvlError, ## Error level and above are active
lvlFatal, ## Fatal level and above are active
lvlNone ## No levels active; nothing is logged
]#
type
FancyConsoleLogger* = ref object of Logger
## A logger that writes log messages to the console.
##
## Create a new ``FancyConsoleLogger`` with the `newFancyConsoleLogger proc
## <#newConsoleLogger>`_.
##
useStderr*: bool ## If true, writes to stderr; otherwise, writes to stdout
flushThreshold*: Level ## Only messages that are at or above this
## threshold will be flushed immediately
fmtPrefix: string
fmtSep: string
fmtStrs: array[Level, string]
const defaultFlushThreshold = lvlAll
proc genFmtStr(
fmtPrefix, fmtSep, fmtSuffix, levelBb: string,
level: Level
): string =
var parts: seq[string]
if fmtPrefix != "": parts.add fmtPrefix
parts.add $LevelNames[level].bb(levelBb)
return parts.join(fmtSep) & fmtSuffix
proc newFancyConsoleLogger*(
levelThreshold = lvlAll,
fmtPrefix= "",
fmtSep = "|",
fmtSuffix ="| ",
useStderr = false,
flushThreshold = defaultFlushThreshold,
debugBb: string = "faint",
infoBb: string = "bold",
noticeBb: string = "bold",
warnBb: string = "bold yellow",
errorBb: string = "bold red",
fatalBb: string = "bold red"
): FancyConsoleLogger =
## Creates a new `ColoredConsoleLogger<#ConsoleLogger>`_.
new result
let fmtStrs: array[Level, string] = [
genFmtStr(fmtPrefix,fmtSep, fmtSuffix, "", lvlAll),
genFmtStr(fmtPrefix,fmtSep, fmtSuffix, debugBb, lvlDebug),
genFmtStr(fmtPrefix,fmtSep, fmtSuffix, infobb, lvlInfo),
genFmtStr(fmtPrefix,fmtSep, fmtSuffix, noticeBb, lvlNotice),
genFmtStr(fmtPrefix,fmtSep, fmtSuffix, warnBb, lvlWarn),
genFmtStr(fmtPrefix,fmtSep, fmtSuffix, errorBb, lvlError),
genFmtStr(fmtPrefix,fmtSep, fmtSuffix, fatalBb, lvlFatal),
genFmtStr(fmtPrefix, fmtSep, fmtSuffix, "", lvlNone)
]
result.fmtPrefix = fmtPrefix
result.fmtSep = fmtSep
result.levelThreshold = levelThreshold
result.flushThreshold = flushThreshold
result.useStderr = useStderr
result.fmtStrs = fmtStrs
method log*(logger: FancyConsoleLogger, level: Level, args: varargs[string, `$`]) {.gcsafe.} =
## Logs to the console with the given `FancyConsoleLogger<#ConsoleLogger>`_ only.
##
## This method ignores the list of registered handlers.
##
## Whether the message is logged depends on both the ConsoleLogger's
## ``levelThreshold`` field and the global log filter set using the
## `setLogFilter proc<#setLogFilter,Level>`_.
##
## **Note:** Only error and fatal messages will cause the output buffer
## to be flushed immediately by default. Set ``flushThreshold`` when creating
## the logger to change this.
if level >= logger.levelThreshold:
let ln = substituteLog(logger.fmtStrs[level], level, args)
when defined(js): {.fatal: "handler does note support JS".}
try:
let handle =
if logger.useStderr: stderr
else: stdout
writeLine(handle, ln)
if level >= logger.flushThreshold: flushFile(handle)
except IOError:
discard
proc addHandlers*(handler: Logger) =
handlers.add(handler)

View file

@ -0,0 +1,217 @@
import std/[
algorithm, json,
enumerate, os, osproc, sequtils, strformat,
strutils, sugar, logging, tables
]
import bbansi, jsony
import ./[context, exec]
proc nixCommand(cmd: string): string =
result = "nix"
if isResetCache():
result.addArg "--narinfo-cache-negative-ttl 0"
result.addArg "--log-format multiline"
result.addArg cmd
proc nixosConfigAttrs*(): seq[string] =
for host in getHosts():
result.add fmt"{getFlake()}#nixosConfigurations.{host}.config.system.build.toplevel"
const nixosSubcmds* =
"""switch boot test build dry-build dry-activate edit
repl build-vm build-vm-with-bootloader list-generations""".splitWhitespace()
proc nixosRebuild*(subcmd: string, rest: seq[string] = @[]) =
var cmd = fmt"sudo nixos-rebuild {subcmd} --flake {getFlake()} --log-format multiline"
if getHosts().len > 1:
error "nixos-rebuild only supports one host"
quit QuitFailure
cmd.addArgs rest
quitWithCmd cmd
type
Derivation = object
storePath, hash, name: string
DryRunOutput = object
toBuild: seq[Derivation]
toFetch: seq[Derivation]
func toDerivation(pkg: string): Derivation =
let path = pkg.strip()
let s = path.split("-", 1)
result.storePath = path
result.hash = s[0].rsplit("/")[^1]
result.name = s[^1].replace(".drv","")
func toDerivations(lines: seq[string]): seq[Derivation] =
for pkg in lines:
result.add (toDerivation pkg)
proc cmpDrv(x, y: Derivation): int =
cmp(x.name, y.name)
proc parseDryRunOutput(err: string): DryRunOutput =
let lines = err.strip().splitLines()
let theseLines = collect:
for i, line in enumerate(lines):
if line.startswith("these"): i
case theseLines.len:
of 2:
let (firstIdx, secondIdx) = (theseLines[0], theseLines[1])
result.toBuild = lines[(firstIdx + 1) .. (secondIdx - 1)].toDerivations()
result.toFetch = lines[(secondIdx + 1) .. ^1].toDerivations()
of 1:
let idx = theseLines[0]
let line = lines[idx]
let drvs = lines[idx .. ^1].toDerivations()
if line.contains("built:"):
result.toBuild = drvs
elif line.contains("will be fetched"):
result.toFetch =drvs
else:
fatal "expected on of the lines to contain built or fetched check the output below"
stderr.writeLine err
quit()
of 0:
info "nothing to do";
quit(QuitSuccess)
else:
fatal "unexpected output from nix"
stderr.writeLine err
quit()
result.toBuild.sort(cmpDrv)
result.toFetch.sort(cmpDrv)
proc trunc(s: string, limit: int): string =
if s.len <= limit:
s
else:
s[0..(limit-4)] & "..."
proc display(msg: string, drvs: seq[Derivation]) =
echo fmt"{msg}: [bold cyan]{drvs.len()}[/]".bb
let maxLen = min(max drvs.mapIt(it.name.len), 40)
for drv in drvs:
echo " ", drv.name.trunc(maxLen).alignLeft(maxLen), " ", drv.hash.bb("faint")
proc display(output: DryRunOutput) =
if isDebug():
display("to fetch", output.toFetch)
else:
echo fmt"to fetch: [bold cyan]{output.toFetch.len()}[/]".bb
display("to build", output.toBuild)
proc toBuildNixosConfiguration(): seq[string] =
var cmd = nixCommand("build")
cmd.addArg "--dry-run"
cmd.addArgs nixosConfigAttrs()
let (_, err) = runCmdCaptWithSpinner(cmd, "running dry run build for: " & getHosts().join(" "))
let output = parseDryRunOutput err
return output.toBuild.mapIt(it.storePath)
type
NixDerivation = object
inputDrvs: Table[string, JsonNode]
name: string
proc evaluateDerivations(drvs: seq[string]): Table[string,NixDerivation] =
var cmd = "nix derivation show -r"
cmd.addArgs drvs
let (output, _) =
runCmdCaptWithSpinner(cmd, "evaluating derivations")
output.fromJson(Table[string,NixDerivation])
# TODO: replace asserts in this proc
proc findSystemPaths(drvs: Table[string, NixDerivation]): seq[string] =
let hosts = getHosts()
let systemDrvs = collect(
for k in drvs.keys():
if k.split("-",1)[1].startswith("nixos-system-"): k
)
assert len(hosts) == len(systemDrvs)
for name in systemDrvs:
for drv in drvs[name].inputDrvs.keys():
if drv.endsWith("system-path.drv"):
result.add drv
assert len(hosts) == len(result)
func isIgnored(drv: string): bool =
const ignoredPackages = (slurp "ignored.txt").splitLines()
drv.split("-", 1)[1].replace(".drv","") in ignoredPackages
proc systemPathDrvsToBuild(): seq[string] =
let toBuild = toBuildNixosConfiguration()
let drvs = evaluateDerivations(nixosConfigAttrs())
let systemPaths = findSystemPaths(drvs)
var inputDrvs: seq[string]
for p in systemPaths:
inputDrvs &= drvs[p].inputDrvs.keys().toSeq()
result = collect(
for drv in inputDrvs:
if (drv in toBuild) and (not drv.isIgnored()):
drv & "^*"
)
func splitDrv(drv: string): tuple[name, hash:string] =
let s = drv.split("-", 1)
(s[1].replace(".drv^*",""),s[0].split("/")[^1])
proc writeDervationsToStepSummary(drvs: seq[string]) =
let rows = collect(
for drv in drvs:
let (name,hash) = splitDrv(drv)
fmt"| {name} | {hash} |"
)
let summaryFilePath = getEnv("GITHUB_STEP_SUMMARY")
if summaryFilePath == "":
fatal "no github step summary found"
quit QuitFailure
let output = open(summaryFilePath,fmAppend)
output.writeLine("| derivation | hash |\n|---|---|")
output.writeLine(rows.join("\n"))
proc nixBuild*(minimal: bool, rest: seq[string]) =
var cmd = nixCommand("build")
if minimal:
debug "populating args with derivations not built/cached"
let drvs = systemPathDrvsToBuild()
if drvs.len == 0:
info "nothing to build"
quit "exiting...", QuitSuccess
cmd.addArgs drvs
cmd.addArg "--no-link"
if isCi():
writeDervationsToStepSummary drvs
cmd.addArgs rest
quitWithCmd cmd
proc nixBuildHostDry*(minimal: bool, rest: seq[string]) =
var cmd = nixCommand("build")
if minimal:
debug "populating args with derivations not built/cached"
let drvs = systemPathDrvsToBuild()
if drvs.len == 0:
info "nothing to build"
quit "exiting...", QuitSuccess
cmd.addArgs drvs
cmd.addArg "--no-link"
if isCi():
writeDervationsToStepSummary drvs
else:
cmd.addArgs nixosConfigAttrs()
cmd.addArg "--dry-run"
cmd.addArgs rest
let (_, err) =
runCmdCaptWithSpinner(cmd, "evaluating derivation for: " & getHosts().join(" "))
let output = parseDryRunOutput err
display output

View file

@ -0,0 +1,64 @@
import std/macros
type
OverlayKind = enum
oPre
oPost
OverlayProc = object
node: NimNode
kind: OverlayKind
proc applyOverlay(child: NimNode, overlayProc: OverlayProc) =
let node = overlayProc.node
for p in node.params:
if p.kind == nnkIdentDefs:
child.params.add copyNimTree(p)
case overlayProc.kind:
of oPre:
let startIdx = if child.body[0].kind == nnkCommentStmt: 1 else: 0
for i in countdown(node.body.len()-1, 0):
child.body.insert(startIdx, copyNimTree(node.body[i]))
of oPost:
for stmt in node.body.children():
child.body.add copyNimTree(stmt)
macro overlay*(x: untyped): untyped =
##[
apply pre and post operations to procs:
```nim
overlay:
proc pre(a: bool) =
echo "before"
proc post(c: bool) =
echo "after"
proc mine(b: bool) =
echo "inside mine"
```
would result in:
```nim
proc pre(a: bool; b: bool; c: bool) =
echo "before"
echo "inside mine"
echo "after"
```
]##
result = newStmtList()
var overlays: seq[OverlayProc]
for child in x.children():
case child.kind:
of nnkProcDef:
case ($child.name):
of "pre": overlays.add OverlayProc(node: child, kind: oPre)
of "post": overlays.add OverlayProc(node: child, kind: oPost)
else: result.add child
else: result.add child
if overlays.len == 0:
error "failed to create overlays: didn't find proc pre() or proc post()"
for i, child in result.pairs():
if child.kind == nnkProcDef:
for overlay in overlays:
applyOverlay(child, overlay)

View file

@ -0,0 +1,189 @@
import std/[os, locks, sequtils, terminal]
# https://github.com/molnarmark/colorize
proc reset(): string = "\e[0m"
# foreground colors
proc fgRed*(s: string): string = "\e[31m" & s & reset()
proc fgBlack*(s: string): string = "\e[30m" & s & reset()
proc fgGreen*(s: string): string = "\e[32m" & s & reset()
proc fgYellow*(s: string): string = "\e[33m" & s & reset()
proc fgBlue*(s: string): string = "\e[34m" & s & reset()
proc fgMagenta*(s: string): string = "\e[35m" & s & reset()
proc fgCyan*(s: string): string = "\e[36m" & s & reset()
proc fgLightGray*(s: string): string = "\e[37m" & s & reset()
proc fgDarkGray*(s: string): string = "\e[90m" & s & reset()
proc fgLightRed*(s: string): string = "\e[91m" & s & reset()
proc fgLightGreen*(s: string): string = "\e[92m" & s & reset()
proc fgLightYellow*(s: string): string = "\e[93m" & s & reset()
proc fgLightBlue*(s: string): string = "\e[94m" & s & reset()
proc fgLightMagenta*(s: string): string = "\e[95m" & s & reset()
proc fgLightCyan*(s: string): string = "\e[96m" & s & reset()
proc fgWhite*(s: string): string = "\e[97m" & s & reset()
# background colors
proc bgBlack*(s: string): string = "\e[40m" & s & reset()
proc bgRed*(s: string): string = "\e[41m" & s & reset()
proc bgGreen*(s: string): string = "\e[42m" & s & reset()
proc bgYellow*(s: string): string = "\e[43m" & s & reset()
proc bgBlue*(s: string): string = "\e[44m" & s & reset()
proc bgMagenta*(s: string): string = "\e[45m" & s & reset()
proc bgCyan*(s: string): string = "\e[46m" & s & reset()
proc bgLightGray*(s: string): string = "\e[47m" & s & reset()
proc bgDarkGray*(s: string): string = "\e[100m" & s & reset()
proc bgLightRed*(s: string): string = "\e[101m" & s & reset()
proc bgLightGreen*(s: string): string = "\e[102m" & s & reset()
proc bgLightYellow*(s: string): string = "\e[103m" & s & reset()
proc bgLightBlue*(s: string): string = "\e[104m" & s & reset()
proc bgLightMagenta*(s: string): string = "\e[105m" & s & reset()
proc bgLightCyan*(s: string): string = "\e[106m" & s & reset()
proc bgWhite*(s: string): string = "\e[107m" & s & reset()
# formatting functions
proc bold*(s: string): string = "\e[1m" & s & reset()
proc underline*(s: string): string = "\e[4m" & s & reset()
proc hidden*(s: string): string = "\e[8m" & s & reset()
proc invert*(s: string): string = "\e[7m" & s & reset()
type
SpinnerKind* = enum
Dots
Spinner* = object
interval*: int
frames*: seq[string]
proc makeSpinner*(interval: int, frames: seq[string]): Spinner =
Spinner(interval: interval, frames: frames)
const Spinners*: array[SpinnerKind, Spinner] = [
# Dots
Spinner(interval: 80, frames: @["","","","","","","","","",""]),
]
type
Spinny = ref object
t: Thread[Spinny]
lock: Lock
text: string
running: bool
frames: seq[string]
frame: string
interval: int
customSymbol: bool
EventKind = enum
Stop, StopSuccess, StopError,
SymbolChange, TextChange,
SpinnyEvent = object
kind: EventKind
payload: string
var spinnyChannel: Channel[SpinnyEvent]
proc newSpinny*(text: string, s: Spinner): Spinny =
Spinny(
text: text,
running: true,
frames: s.frames,
customSymbol: false,
interval: s.interval
)
proc newSpinny*(text: string, spinType: SpinnerKind): Spinny =
newSpinny(text, Spinners[spinType])
proc setSymbolColor*(spinny: Spinny, color: proc(x: string): string) =
spinny.frames = mapIt(spinny.frames, color(it))
proc setSymbol*(spinny: Spinny, symbol: string) =
spinnyChannel.send(SpinnyEvent(kind: SymbolChange, payload: symbol))
proc setText*(spinny: Spinny, text: string) =
spinnyChannel.send(SpinnyEvent(kind: TextChange, payload: text))
proc handleEvent(spinny: Spinny, eventData: SpinnyEvent): bool =
result = true
case eventData.kind
of Stop:
result = false
of SymbolChange:
spinny.customSymbol = true
spinny.frame = eventData.payload
of TextChange:
spinny.text = eventData.payload
of StopSuccess:
spinny.customSymbol = true
spinny.frame = "".bold.fgGreen
spinny.text = eventData.payload.bold.fgGreen
of StopError:
spinny.customSymbol = true
spinny.frame = "".bold.fgRed
spinny.text = eventData.payload.bold.fgRed
proc spinnyLoop(spinny: Spinny) {.thread.} =
var frameCounter = 0
while spinny.running:
let data = spinnyChannel.tryRecv()
if data.dataAvailable:
# If we received a Stop event
if not spinny.handleEvent(data.msg):
spinnyChannel.close()
# This is required so we can reopen the same channel more than once
# See https://github.com/nim-lang/Nim/issues/6369
spinnyChannel = default(typeof(spinnyChannel))
# TODO: Do we need spinny.running at all?
spinny.running = false
break
stdout.flushFile()
if not spinny.customSymbol:
spinny.frame = spinny.frames[frameCounter]
withLock spinny.lock:
eraseLine()
stdout.write(spinny.frame & " " & spinny.text)
stdout.flushFile()
sleep(spinny.interval)
if frameCounter >= spinny.frames.len - 1:
frameCounter = 0
else:
frameCounter += 1
proc start*(spinny: Spinny) =
initLock(spinny.lock)
spinnyChannel.open()
createThread(spinny.t, spinnyLoop, spinny)
proc stop(spinny: Spinny, kind: EventKind, payload = "") =
spinnyChannel.send(SpinnyEvent(kind: kind, payload: payload))
spinnyChannel.send(SpinnyEvent(kind: Stop))
joinThread(spinny.t)
eraseLine stdout
flushFile stdout
proc stop*(spinny: Spinny) =
spinny.stop(Stop)
proc success*(spinny: Spinny, msg: string) =
spinny.stop(StopSuccess, msg)
proc error*(spinny: Spinny, msg: string) =
spinny.stop(StopError, msg)
template withSpinner*(msg: string = "", body: untyped): untyped =
var spinner {.inject.} = newSpinny(msg, Dots)
spinner.setSymbolColor(fgBlue)
start spinner
body
stop spinner
template withSpinner*(body: untyped): untyped =
withSpinner("", body)

8
pkgs/oizys-nim/todo.md Normal file
View file

@ -0,0 +1,8 @@
# oizys-nim todo's
- [x] nix commands including dry runs
- [ ] gh api commands
- [ ] ci <- start with the easier one
- [ ] update
<!-- generated with <3 by daylinmorgan/todo -->