context: interface command type
This should allow caller to override the method to run in a container.
This commit is contained in:
parent
6911583918
commit
a3427ce7dd
10
build.go
10
build.go
@ -14,14 +14,14 @@ func Build(ctx Context, installables iter.Seq[string]) error {
|
||||
cmd := ctx.Nix(c, CommandBuild,
|
||||
FlagKeepGoing, FlagNoLink, FlagStdin)
|
||||
if stdout != nil {
|
||||
cmd.Stdout = stdout
|
||||
cmd.Args = append(cmd.Args, FlagPrintBuildLogs)
|
||||
cmd.Stdout(stdout)
|
||||
cmd.AppendArgs(FlagPrintBuildLogs)
|
||||
} else {
|
||||
cmd.Args = append(cmd.Args, FlagQuiet)
|
||||
cmd.AppendArgs(FlagQuiet)
|
||||
}
|
||||
if stderr != nil {
|
||||
cmd.Stderr = stderr
|
||||
cmd.Args = append(cmd.Args, FlagVerbose)
|
||||
cmd.Stderr(stderr)
|
||||
cmd.AppendArgs(FlagVerbose)
|
||||
}
|
||||
|
||||
_, err := ctx.WriteStdin(cmd, installables, nil)
|
||||
|
29
context.go
29
context.go
@ -4,19 +4,40 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"iter"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// Cmd represents an external command or container being prepared to run.
|
||||
type Cmd interface {
|
||||
Start() error
|
||||
Wait() error
|
||||
StdinPipe() (io.WriteCloser, error)
|
||||
StdoutPipe() (io.ReadCloser, error)
|
||||
StderrPipe() (io.ReadCloser, error)
|
||||
|
||||
Stdin(io.Reader)
|
||||
Stdout(io.Writer)
|
||||
Stderr(io.Writer)
|
||||
AppendArgs(a ...string)
|
||||
ReplaceEnv(env []string)
|
||||
}
|
||||
|
||||
// Context holds configuration and environment information for interacting with nix.
|
||||
type Context interface {
|
||||
// Streams returns the stdout and stderr writers held by this [Context].
|
||||
Streams() (stdout, stderr io.Writer)
|
||||
|
||||
// Nix returns the [exec.Cmd] struct to execute a nix command.
|
||||
Nix(ctx context.Context, arg ...string) *exec.Cmd
|
||||
// Nix returns an implementation of [Cmd] for running a nix command.
|
||||
Nix(ctx context.Context, arg ...string) Cmd
|
||||
// WriteStdin calls [WriteStdin] for [exec.Cmd]. The function f points to is called if [WriteStdin] succeeds.
|
||||
WriteStdin(cmd *exec.Cmd, installables iter.Seq[string], f func() error) (int, error)
|
||||
WriteStdin(cmd Cmd, installables iter.Seq[string], f func() error) (int, error)
|
||||
|
||||
// Unwrap returns the stored [context.Context]
|
||||
Unwrap() context.Context
|
||||
}
|
||||
|
||||
// setStreams sets the stdout and stderr streams of [Cmd] according to [Context].
|
||||
func setStreams(ctx Context, cmd Cmd) {
|
||||
stdout, stderr := ctx.Streams()
|
||||
cmd.Stdout(stdout)
|
||||
cmd.Stderr(stderr)
|
||||
}
|
||||
|
8
copy.go
8
copy.go
@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Copy copies installables to the binary cache store.
|
||||
// Copy copies installables to [Store].
|
||||
func Copy(ctx Context, store Store, installables iter.Seq[string]) error {
|
||||
if store == nil {
|
||||
return os.ErrInvalid
|
||||
@ -18,13 +18,13 @@ func Copy(ctx Context, store Store, installables iter.Seq[string]) error {
|
||||
cmd := ctx.Nix(c, CommandCopy,
|
||||
FlagTo, store.String(),
|
||||
FlagStdin)
|
||||
cmd.Env = append(os.Environ(), store.Environ()...)
|
||||
cmd.ReplaceEnv(append(os.Environ(), store.Environ()...))
|
||||
if _, ok := store.(Local); ok {
|
||||
// this is required for chroot stores, but does not seem to have any effect on binary cache
|
||||
cmd.Args = append(cmd.Args, FlagNoCheckSigs)
|
||||
cmd.AppendArgs(FlagNoCheckSigs)
|
||||
}
|
||||
|
||||
cmd.Stdout, cmd.Stderr = ctx.Streams()
|
||||
setStreams(ctx, cmd)
|
||||
_, err := ctx.WriteStdin(cmd, installables, nil)
|
||||
return err
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func DerivationShow(ctx Context, installables iter.Seq[string]) (DerivationMap,
|
||||
decoder = json.NewDecoder(r)
|
||||
}
|
||||
if stderr != nil {
|
||||
cmd.Stderr = stderr
|
||||
cmd.Stderr(stderr)
|
||||
}
|
||||
|
||||
var v DerivationMap
|
||||
|
15
exec.go
15
exec.go
@ -58,17 +58,26 @@ func New(ctx context.Context, store Store, extraArgs []string, stdout, stderr io
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nix) Nix(ctx context.Context, arg ...string) *exec.Cmd {
|
||||
// ExecCmd wraps [exec.Cmd] and implements extra [Cmd] methods.
|
||||
type ExecCmd struct{ *exec.Cmd }
|
||||
|
||||
func (cmd ExecCmd) Stdin(r io.Reader) { cmd.Cmd.Stdin = r }
|
||||
func (cmd ExecCmd) Stdout(w io.Writer) { cmd.Cmd.Stdout = w }
|
||||
func (cmd ExecCmd) Stderr(w io.Writer) { cmd.Cmd.Stderr = w }
|
||||
func (cmd ExecCmd) AppendArgs(a ...string) { cmd.Cmd.Args = append(cmd.Cmd.Args, a...) }
|
||||
func (cmd ExecCmd) ReplaceEnv(env []string) { cmd.Cmd.Env = env }
|
||||
|
||||
func (n *nix) Nix(ctx context.Context, arg ...string) Cmd {
|
||||
cmd := exec.CommandContext(ctx, n.name, append(n.extra, arg...)...)
|
||||
cmd.Cancel = func() error { return cmd.Process.Signal(os.Interrupt) }
|
||||
cmd.WaitDelay = defaultWaitDelay
|
||||
if n.store != nil {
|
||||
cmd.Env = append(cmd.Env, n.store.Environ()...)
|
||||
}
|
||||
return cmd
|
||||
return ExecCmd{cmd}
|
||||
}
|
||||
|
||||
func (n *nix) WriteStdin(cmd *exec.Cmd, installables iter.Seq[string], f func() error) (int, error) {
|
||||
func (n *nix) WriteStdin(cmd Cmd, installables iter.Seq[string], f func() error) (int, error) {
|
||||
w, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -63,10 +63,10 @@ type stubContextCommand struct {
|
||||
nix.Context
|
||||
}
|
||||
|
||||
func (s *stubContextCommand) Nix(ctx context.Context, arg ...string) *exec.Cmd {
|
||||
cmd := s.Context.Nix(ctx, arg...)
|
||||
func (s *stubContextCommand) Nix(ctx context.Context, arg ...string) nix.Cmd {
|
||||
cmd := s.Context.Nix(ctx, arg...).(nix.ExecCmd)
|
||||
if s.f != nil {
|
||||
s.f(cmd)
|
||||
s.f(cmd.Cmd)
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ func TestNixWriteStdin(t *testing.T) {
|
||||
CredentialsPath: "/dev/null",
|
||||
KeyPath: nonexistent,
|
||||
}, nil, nil, nil)
|
||||
cmd := ctx.Nix(t.Context(), nix.FlagVersion)
|
||||
cmd := ctx.Nix(t.Context(), nix.FlagVersion).(nix.ExecCmd)
|
||||
|
||||
wantArgs := []string{
|
||||
nix.Nix,
|
||||
@ -43,8 +43,8 @@ func TestNixWriteStdin(t *testing.T) {
|
||||
|
||||
t.Run("already set", func(t *testing.T) {
|
||||
ctx := nix.New(t.Context(), nil, nil, os.Stdout, os.Stderr)
|
||||
cmd := exec.CommandContext(t.Context(), nonexistent)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd := nix.ExecCmd{Cmd: exec.CommandContext(t.Context(), nonexistent)}
|
||||
cmd.Stdin(os.Stdin)
|
||||
if _, err := ctx.WriteStdin(cmd, nil, nil); err == nil {
|
||||
t.Fatal("WriteStdinCommand unexpectedly succeeded")
|
||||
}
|
||||
@ -68,7 +68,7 @@ func TestNixWriteStdin(t *testing.T) {
|
||||
ctx := newStubContext(t.Context(), nil, os.Stdout, os.Stderr)
|
||||
c, cancel := context.WithCancel(t.Context())
|
||||
defer cancel()
|
||||
cmd := ctx.Nix(c, "true")
|
||||
cmd := ctx.Nix(c, "true").(nix.ExecCmd)
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("Start: error = %v", err)
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"iter"
|
||||
"os/exec"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
@ -153,7 +152,7 @@ func (d *InstantiatedDecoder) Decode() ([]string, error) {
|
||||
// are significantly more complete than `nix-store -qR` and there does not appear to be a better way.
|
||||
type InstantiatedEvaluator struct {
|
||||
// underlying nix program
|
||||
cmd *exec.Cmd
|
||||
cmd Cmd
|
||||
// populated by Close
|
||||
waitErr error
|
||||
// synchronises access to waitErr
|
||||
@ -184,7 +183,7 @@ func NewInstantiatedEvaluator(ctx Context, installable string) (*InstantiatedEva
|
||||
}
|
||||
|
||||
stdout, stderr := ctx.Streams()
|
||||
e.cmd.Stdout = stdout
|
||||
e.cmd.Stdout(stdout)
|
||||
|
||||
// verbose output ends up on stderr in the current nix implementation
|
||||
if r, err := e.cmd.StderrPipe(); err != nil {
|
||||
|
14
package.nix
14
package.nix
@ -3,21 +3,29 @@
|
||||
stdenv,
|
||||
buildGoModule,
|
||||
pkg-config,
|
||||
libffi,
|
||||
libseccomp,
|
||||
}:
|
||||
buildGoModule {
|
||||
pname = "nix-tool";
|
||||
version = "0.1.4";
|
||||
src = ./.;
|
||||
|
||||
vendorHash = "sha256-lK9+fI8/GR2GY6X899HnoP28FuQnklYbzaiGqIkus8c=";
|
||||
vendorHash = "sha256-AUSqbsXvJvkOE0BjO6XnPTDD2NkOHY2XUbo7jZtYmd4=";
|
||||
|
||||
ldflags =
|
||||
[ "-s -w" ]
|
||||
ldflags = [
|
||||
"-s -w"
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isStatic [
|
||||
"-linkmode external"
|
||||
"-extldflags \"-static\""
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
libffi
|
||||
libseccomp
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkg-config
|
||||
];
|
||||
|
Loading…
x
Reference in New Issue
Block a user