diff --git a/context.go b/context.go index 7aaa2de..404a529 100644 --- a/context.go +++ b/context.go @@ -25,10 +25,14 @@ type Cmd interface { type Context interface { // Streams returns the stdout and stderr writers held by this [Context]. Streams() (stdout, stderr io.Writer) + // Extra returns a copy of extra arguments held by this [Context]. + Extra() []string + // StoreEnv returns extra [Store] environment variables. + StoreEnv() []string // 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 calls [WriteStdin] for [Cmd]. The function f points to is called if [WriteStdin] succeeds. WriteStdin(cmd Cmd, installables iter.Seq[string], f func() error) (int, error) // Unwrap returns the stored [context.Context] diff --git a/exec.go b/exec.go index 9867c52..91644a5 100644 --- a/exec.go +++ b/exec.go @@ -11,7 +11,7 @@ import ( ) const ( - defaultWaitDelay = 30 * time.Second + DefaultWaitDelay = 30 * time.Second ) // Nix is the name of the nix program. @@ -28,6 +28,13 @@ type nix struct { func (n *nix) Unwrap() context.Context { return n.ctx } func (n *nix) Streams() (stdout, stderr io.Writer) { return n.stdout, n.stderr } +func (n *nix) Extra() (v []string) { v = make([]string, len(n.extra)); copy(v, n.extra); return } +func (n *nix) StoreEnv() (v []string) { + if n.store == nil { + return nil + } + return n.store.Environ() +} const ( ExtraExperimentalFeatures = "--extra-experimental-features" @@ -42,6 +49,7 @@ A non-nil stderr implies verbose. Streams will not be connected for commands outputting JSON. */ func New(ctx context.Context, store Store, extraArgs []string, stdout, stderr io.Writer) Context { + // since flakes are supposedly experimental extra := []string{ExtraExperimentalFeatures, ExperimentalFeaturesFlakes} if store != nil { extra = append(extraArgs, FlagStore, store.String()) @@ -50,7 +58,6 @@ func New(ctx context.Context, store Store, extraArgs []string, stdout, stderr io name: Nix, store: store, ctx: ctx, - // since flakes are supposedly experimental extra: append(extraArgs, extra...), stdout: stdout, @@ -70,7 +77,7 @@ 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 + cmd.WaitDelay = DefaultWaitDelay if n.store != nil { cmd.Env = append(cmd.Env, n.store.Environ()...) } diff --git a/exec_test.go b/exec_test.go index c600789..3b9e55b 100644 --- a/exec_test.go +++ b/exec_test.go @@ -5,13 +5,62 @@ import ( "errors" "os" "os/exec" + "reflect" "slices" "syscall" "testing" + "unsafe" "gensokyo.uk/nix" ) +func TestContext(t *testing.T) { + ctx := nix.New(t.Context(), &nix.BinaryCache{ + Compression: "none", + ParallelCompression: false, + Bucket: "example", + Endpoint: "s3.example.org", + Region: "us-east-1", + Scheme: "http", + CredentialsPath: "/dev/null", + KeyPath: nonexistent, + }, []string{ + nix.FlagOption, nix.OptionBuildUseSubstitutes, nix.ValueFalse, + nix.FlagOption, nix.OptionSubstituters, "", + nix.FlagOption, nix.OptionTrustedSubstituters, "", + nix.FlagOption, nix.OptionTrustedPublicKeys, "", + }, nil, nil) + + extraVal := reflect.ValueOf(ctx).Elem().FieldByName("extra") + wantExtra := reflect.NewAt(extraVal.Type(), unsafe.Pointer(extraVal.UnsafeAddr())).Elem().Interface().([]string) + + t.Run("extra", func(t *testing.T) { + got := ctx.Extra() + if !slices.Equal(got, wantExtra) { + t.Errorf("Extra: %#v, want %#v", got, wantExtra) + } + got[0] = "\x00" + if slices.Equal(got, wantExtra) { + t.Errorf("Extra did not return a copy") + } + }) + + t.Run("store env", func(t *testing.T) { + t.Run("nil", func(t *testing.T) { + want := nix.New(t.Context(), nil, nil, nil, nil).StoreEnv() + if want != nil { + t.Errorf("StoreEnv: %#v", want) + } + }) + + want := []string{"AWS_SHARED_CREDENTIALS_FILE=/dev/null"} + got := ctx.StoreEnv() + if !slices.Equal(got, want) { + t.Errorf("StoreEnv: %#v, want %#v", got, want) + } + }) +} + func TestNixWriteStdin(t *testing.T) { t.Run("store", func(t *testing.T) { ctx := nix.New(t.Context(), &nix.BinaryCache{