diff --git a/cmd/nixbuild/main.go b/cmd/nixbuild/main.go index e0764f6..7f9c753 100644 --- a/cmd/nixbuild/main.go +++ b/cmd/nixbuild/main.go @@ -12,7 +12,7 @@ import ( "slices" "syscall" - "git.gensokyo.uk/yonah/nixbuild" + "gensokyo.uk/nix" "hakurei.app/command" ) @@ -21,7 +21,7 @@ type commandHandlerError string func (c commandHandlerError) Error() string { return string(c) } func main() { - var ctx nixbuild.Context + var ctx nix.Context nixCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() @@ -39,7 +39,7 @@ func main() { if flagVerbose { stderr = os.Stderr } - ctx = nixbuild.New(nixCtx, nil, os.Stdout, stderr) + ctx = nix.New(nixCtx, nil, os.Stdout, stderr) return nil }). @@ -52,7 +52,7 @@ func main() { return commandHandlerError("show requires at least 1 argument") } - if drv, err := nixbuild.DerivationShow(ctx, slices.Values(args)); err != nil { + if drv, err := nix.DerivationShow(ctx, slices.Values(args)); err != nil { return commandHandlerError(fmt.Sprintf("cannot show: %v", err)) } else { log.Printf("got %d derivations:\n%#v", len(drv), drv) @@ -65,12 +65,27 @@ func main() { return commandHandlerError("build requires at least 1 argument") } - if err := nixbuild.Build(ctx, slices.Values(args)); err != nil { + if err := nix.Build(ctx, slices.Values(args)); err != nil { return commandHandlerError(fmt.Sprintf("cannot build: %v", err)) } return nil }) + var ( + flagSignKey string + ) + c.NewCommand("sign", command.UsageInternal, func(args []string) error { + if len(args) < 1 { + return commandHandlerError("sign requires at least 1 argument") + } + + if err := nix.Sign(ctx, flagSignKey, slices.Values(args)); err != nil { + return commandHandlerError(fmt.Sprintf("cannot sign: %v", err)) + } + return nil + }). + Flag(&flagSignKey, "key", command.StringFlag("/var/lib/persist/cache/key"), "File containing the secret signing key") + var ( resolveFlagOut string ) @@ -82,23 +97,23 @@ func main() { log.Printf("evaluating %s", installable) var installables []string - if v, err := nixbuild.EvalInstantiated(ctx, installable); err != nil { + if v, err := nix.EvalInstantiated(ctx, installable); err != nil { return commandHandlerError(fmt.Sprintf("cannot evaluate for instantiated derivations: %v", err)) } else { installables = v } log.Println("building instantiated derivations") - if err := nixbuild.Build(ctx, slices.Values(installables)); err != nil { + if err := nix.Build(ctx, slices.Values(installables)); err != nil { return commandHandlerError(fmt.Sprintf("cannot build: %v", err)) } var collective []string log.Println("collecting store paths") - if derivations, err := nixbuild.DerivationShow(ctx, slices.Values(installables)); err != nil { + if derivations, err := nix.DerivationShow(ctx, slices.Values(installables)); err != nil { return commandHandlerError(fmt.Sprintf("cannot show: %v", err)) } else { - collective = nixbuild.CollectFromDerivations(derivations) + collective = nix.CollectFromDerivations(derivations) } f, err := os.Create(resolveFlagOut) @@ -110,7 +125,7 @@ func main() { return commandHandlerError(fmt.Sprintf("cannot serialise JSON: %v", err)) } } else { - if _, err = nixbuild.WriteStdin(f, slices.Values(collective)); err != nil { + if _, err = nix.WriteStdin(f, slices.Values(collective)); err != nil { return commandHandlerError(fmt.Sprintf("cannot write store path list: %v", err)) } } @@ -126,7 +141,7 @@ func main() { log.Println("initialising evaluator") var collective []string - if eval, err := nixbuild.NewInstantiatedEvaluator(ctx, installable); err != nil { + if eval, err := nix.NewInstantiatedEvaluator(ctx, installable); err != nil { return commandHandlerError(fmt.Sprintf("cannot initialise evaluator: %v", err)) } else { log.Println("collecting paths") @@ -150,7 +165,7 @@ func main() { return err } - if v, err := nixbuild.EvalInstantiated(ctx, installable); err != nil { + if v, err := nix.EvalInstantiated(ctx, installable); err != nil { return commandHandlerError(fmt.Sprintf("cannot evaluate for instantiated derivations: %v", err)) } else if flagJSON { if err = json.NewEncoder(os.Stdout).Encode(v); err != nil { @@ -158,7 +173,7 @@ func main() { } return nil } else { - _, _ = nixbuild.WriteStdin(os.Stdout, slices.Values(v)) + _, _ = nix.WriteStdin(os.Stdout, slices.Values(v)) return nil } }) diff --git a/format.go b/format.go index 4670522..a846d33 100644 --- a/format.go +++ b/format.go @@ -17,6 +17,25 @@ const ( CommandDerivationAdd = "add" // CommandDerivationShow show the contents of a store derivation CommandDerivationShow = "show" + // CommandStore manipulate a Nix store. + CommandStore = "store" + // CommandStoreSign sign store paths with a local key + CommandStoreSign = "sign" + + // FlagAll apply the operation to every store path. + FlagAll = "--all" + // FlagDerivation operate on the store derivation rather than its outputs. + FlagDerivation = "--derivation" + // FlagExpr interpret installables as attribute paths relative to the Nix expression expr. + FlagExpr = "--expr" + // FlagFile interpret installables as attribute paths relative to the Nix expression stored in file. + // If file is the character -, then a Nix expression will be read from standard input. Implies --impure. + FlagFile = "--file" + // FlagRecursive apply operation to closure of the specified paths. + FlagRecursive = "--recursive" + + // FlagKeyFile file containing the secret signing key. + FlagKeyFile = "--key-file" // FlagDryRun show what this command would do without doing it. FlagDryRun = "--dry-run" diff --git a/sign.go b/sign.go new file mode 100644 index 0000000..ec1adf5 --- /dev/null +++ b/sign.go @@ -0,0 +1,23 @@ +package nix + +import ( + "context" + "iter" +) + +// Sign recursively signs installables using the key at keyPath. +func Sign(ctx Context, keyPath string, installables iter.Seq[string]) error { + c, cancel := context.WithCancel(ctx.Unwrap()) + defer cancel() + + cmd := ctx.Nix(c, CommandStore, CommandStoreSign, + FlagPrintBuildLogs, FlagVerbose, + // this is quite useless if not signing recursively + FlagRecursive, + FlagKeyFile, keyPath, + FlagStdin) + + cmd.Stdout, cmd.Stderr = ctx.Streams() + _, err := ctx.WriteStdin(cmd, installables, nil) + return err +} diff --git a/sign_test.go b/sign_test.go new file mode 100644 index 0000000..44f232b --- /dev/null +++ b/sign_test.go @@ -0,0 +1,55 @@ +package nix_test + +import ( + "os" + "slices" + "syscall" + "testing" + + "gensokyo.uk/nix" + "hakurei.app/command" +) + +func init() { + stubCommandInit = append(stubCommandInit, func(c command.Command) { + commandStore := c.New(nix.CommandStore, "emit samples for various `nix store` cases") + + var ( + flagSignPBL bool + flagSignVerbose bool + flagSignRecursive bool + flagSignKeyFile string + flagSignStdin bool + ) + commandStore.NewCommand(nix.CommandStoreSign, "emit samples for various `nix store sign` cases", func(args []string) error { + if !flagSignPBL || !flagSignVerbose || !flagSignRecursive || flagSignKeyFile != "/proc/nonexistent" { + return syscall.ENOSYS + } + + installables, err := nix.ReadStdin(os.Stdin) + if err != nil { + return err + } + if !slices.Equal(installables, instWant["pluiedev pappardelle"]) { + return syscall.EINVAL + } + return nil + }). + Flag(&flagSignPBL, trimFlagName(nix.FlagPrintBuildLogs), command.BoolFlag(false), nix.FlagPrintBuildLogs). + Flag(&flagSignVerbose, trimFlagName(nix.FlagVerbose), command.BoolFlag(false), nix.FlagVerbose). + Flag(&flagSignRecursive, trimFlagName(nix.FlagRecursive), command.BoolFlag(false), nix.FlagRecursive). + Flag(&flagSignKeyFile, trimFlagName(nix.FlagKeyFile), command.StringFlag(""), nix.FlagKeyFile). + Flag(&flagSignStdin, trimFlagName(nix.FlagStdin), command.BoolFlag(false), nix.FlagStdin) + }) +} + +func TestSign(t *testing.T) { + stubNixCommand(t) + if err := nix.Sign( + newStubContext(t.Context(), nil, os.Stdout, os.Stderr), + "/proc/nonexistent", + slices.Values(instWant["pluiedev pappardelle"]), + ); err != nil { + t.Errorf("Sign: error = %v", err) + } +}