Add instantiate and narinfo

This commit is contained in:
Emery Hemingway 2023-05-30 23:27:11 +01:00
parent 4e3e77171c
commit 5247d1f329
5 changed files with 87 additions and 14 deletions

View File

@ -2,13 +2,15 @@
An actor for interacting with the [Nix](https://nixos.org/) daemon via the [Syndicated Actor Model](https://syndicate-lang.org/).
*This is only a proof-of-concept and is not useful in any meaningful way.*
*This is only a proof-of-concept and is not yet useful.*
## Example configuration
```
? <nixspace ?nixspace> $nixspace [
? <realise "/nix/store/sv1yikjpf7q8b9w4xszb2ipg0cgcq1xv-imv-4.4.0.drv" ?outputs> [ ]
? <instantiate "let pkgs = import <nixpkgs> {}; in pkgs.hello" { } ?drv> [
? <realise $drv ?outputs> [ ]
]
? <eval "3 * 4" {} _> []
? <eval "builtins.getEnv \"PATH\"" {impure: ""} _> []

View File

@ -1,7 +1,14 @@
version 1 .
Build = <nix-build @input string @output any> .
Realise = <realise @drv string @outputs [string ...]> .
Instantiate = <instantiate @expr string @options Dict @result any> .
Eval = <eval @expr string @options {symbol: any ...:...} @result any> .
Narinfo = <narinfo @path string @info Dict> .
Dict = {symbol: any ...:...} .

View File

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[asyncdispatch, json, osproc, strutils, tables]
import std/[asyncdispatch, httpclient, json, osproc, parseutils, strutils, tables]
import preserves, preserves/jsonhooks
import syndicate
from syndicate/protocols/dataspace import Observe
@ -10,9 +10,48 @@ import ./nix_actor/[main, store]
type
Value = Preserve[void]
Options = Table[Symbol, Value]
Observe = dataspace.Observe[Ref]
proc parseArgs(args: var seq[string]; opts: Dict) =
for sym, val in opts:
add(args, "--" & $sym)
if not val.isString "":
var js: JsonNode
if fromPreserve(js, val): add(args, $js)
else: stderr.writeLine "invalid option --", sym, " ", val
proc parseNarinfo(info: var Dict; text: string) =
var
key, val: string
off: int
while off < len(text):
off = off + parseUntil(text, key, ':', off) + 1
off = off + skipWhitespace(text, off)
off = off + parseUntil(text, val, '\n', off) + 1
if key != "" and val != "":
if allCharsInSet(val, Digits):
info[Symbol key] = val.parsePreserves
else:
info[Symbol key] = val.toPreserve
proc narinfo(turn: var Turn; ds: Ref; path: string) =
let
client = newAsyncHttpClient()
url = "https://cache.nixos.org/" & path & ".narinfo"
futGet = get(client, url)
stderr.writeLine "fetching ", url
addCallback(futGet, turn) do (turn: var Turn):
let resp = read(futGet)
if code(resp) != Http200:
close(client)
else:
let futBody = body(resp)
addCallback(futBody, turn) do (turn: var Turn):
close(client)
var narinfo = Narinfo(path: path)
parseNarinfo(narinfo.info, read(futBody))
discard publish(turn, ds, narinfo)
proc build(spec: string): Build =
var execOutput = execProcess("nix", args = ["build", "--json", "--no-link", spec], options = {poUsePath})
var js = parseJson(execOutput)
@ -22,15 +61,18 @@ proc realise(realise: Realise): seq[string] =
var execlines = execProcess("nix-store", args = ["--realize", realise.drv], options = {poUsePath})
split(strip(execlines), '\n')
proc instantiate(instantiate: Instantiate): Value =
const cmd = "nix-instantiate"
var args = @["--expr", instantiate.expr]
parseArgs(args, instantiate.options)
var execOutput = strip execProcess(cmd, args = args, options = {poUsePath})
execOutput.toPreserve
proc eval(eval: Eval): Value =
var args = @["eval", "--json", "--expr", eval.expr]
for sym, val in eval.options:
add(args, "--" & $sym)
if not val.isString "":
var js: JsonNode
if fromPreserve(js, val): add(args, $js)
else: stderr.writeLine "invalid option ", sym, " ", val
var execOutput = strip execProcess("nix", args = args, options = {poUsePath})
const cmd = "nix"
var args = @["eval", "--expr", eval.expr]
parseArgs(args, eval.options)
var execOutput = strip execProcess(cmd, args = args, options = {poUsePath})
if execOutput != "":
var js = parseJson(execOutput)
result = js.toPreserve
@ -47,6 +89,14 @@ proc bootNixFacet(ds: Ref; turn: var Turn): Facet =
ass.outputs = realise(ass)
discard publish(turn, ds, ass)
during(turn, ds, ?Observe(pattern: !Instantiate) ?? {0: grabLit(), 1: grabDict()}) do (e: string, o: Value):
var ass = Instantiate(expr: e)
if not fromPreserve(ass.options, unpackLiterals(o)):
stderr.writeLine "invalid options ", o
else:
ass.result = instantiate(ass)
discard publish(turn, ds, ass)
during(turn, ds, ?Observe(pattern: !Eval) ?? {0: grabLit(), 1: grabDict()}) do (e: string, o: Value):
var ass = Eval(expr: e)
if not fromPreserve(ass.options, unpackLiterals(o)):
@ -55,6 +105,9 @@ proc bootNixFacet(ds: Ref; turn: var Turn): Facet =
ass.result = eval(ass)
discard publish(turn, ds, ass)
during(turn, ds, ?Observe(pattern: !Narinfo) ?? {0: grabLit()}) do (path: string):
narinfo(turn, ds, path)
type Args {.preservesDictionary.} = object
dataspace: Ref

1
src/nix_actor.nim.cfg Normal file
View File

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

View File

@ -12,12 +12,22 @@ type
`drv`*: string
`outputs`*: seq[string]
Narinfo* {.preservesRecord: "narinfo".} = object
`path`*: string
`info`*: Dict
Dict* = Table[Symbol, Preserve[void]]
Build* {.preservesRecord: "nix-build".} = object
`input`*: string
`output`*: Preserve[void]
proc `$`*(x: Eval | Realise | Build): string =
Instantiate* {.preservesRecord: "instantiate".} = object
`expr`*: string
`options`*: Dict
`result`*: Preserve[void]
proc `$`*(x: Eval | Realise | Narinfo | Dict | Build | Instantiate): string =
`$`(toPreserve(x))
proc encode*(x: Eval | Realise | Build): seq[byte] =
proc encode*(x: Eval | Realise | Narinfo | Dict | Build | Instantiate): seq[byte] =
encode(toPreserve(x))