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