Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
2f5e75ae25 | |||
87382aebed | |||
b298c5f572 | |||
4d9d4bcef2 | |||
b6961508e8 | |||
e0278a6d7d | |||
e448541464 |
@ -85,7 +85,7 @@ func TestBuildBadCommand(t *testing.T) {
|
|||||||
wantErr := os.ErrNotExist
|
wantErr := os.ErrNotExist
|
||||||
breakNixCommand(t)
|
breakNixCommand(t)
|
||||||
if err := nix.Build(
|
if err := nix.Build(
|
||||||
nix.New(t.Context(), nil, nil, nil),
|
nix.New(t.Context(), nil, nil, nil, nil),
|
||||||
nil,
|
nil,
|
||||||
); !errors.Is(err, wantErr) {
|
); !errors.Is(err, wantErr) {
|
||||||
t.Errorf("Build: error = %v, want %v", err, wantErr)
|
t.Errorf("Build: error = %v, want %v", err, wantErr)
|
||||||
|
@ -45,11 +45,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var extraArgs []string
|
var (
|
||||||
|
store nix.Store
|
||||||
|
extraArgs []string
|
||||||
|
)
|
||||||
flagStore = strings.TrimSpace(flagStore)
|
flagStore = strings.TrimSpace(flagStore)
|
||||||
if flagStore != string(os.PathSeparator) {
|
if flagStore != string(os.PathSeparator) {
|
||||||
|
store = nix.Local(flagStore)
|
||||||
extraArgs = append(extraArgs,
|
extraArgs = append(extraArgs,
|
||||||
"--store", flagStore,
|
|
||||||
// do not use any binary cache
|
// do not use any binary cache
|
||||||
nix.FlagOption, nix.OptionBuildUseSubstitutes, nix.ValueFalse,
|
nix.FlagOption, nix.OptionBuildUseSubstitutes, nix.ValueFalse,
|
||||||
nix.FlagOption, nix.OptionSubstituters, "",
|
nix.FlagOption, nix.OptionSubstituters, "",
|
||||||
@ -62,7 +65,7 @@ func main() {
|
|||||||
if flagVerbose {
|
if flagVerbose {
|
||||||
stderr = os.Stderr
|
stderr = os.Stderr
|
||||||
}
|
}
|
||||||
ctx = nix.New(nixCtx, extraArgs, os.Stdout, stderr)
|
ctx = nix.New(nixCtx, store, extraArgs, os.Stdout, stderr)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}).
|
}).
|
||||||
@ -111,7 +114,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Println("copying to binary cache...")
|
log.Println("copying to binary cache...")
|
||||||
if err := nix.Copy(ctx, flagCacheKeyPath, &nix.BinaryCache{
|
if err := nix.Copy(ctx, &nix.BinaryCache{
|
||||||
Compression: flagCacheComp,
|
Compression: flagCacheComp,
|
||||||
ParallelCompression: flagCachePComp,
|
ParallelCompression: flagCachePComp,
|
||||||
Bucket: flagCacheBucket,
|
Bucket: flagCacheBucket,
|
||||||
@ -119,6 +122,7 @@ func main() {
|
|||||||
Region: flagCacheRegion,
|
Region: flagCacheRegion,
|
||||||
Scheme: flagCacheScheme,
|
Scheme: flagCacheScheme,
|
||||||
CredentialsPath: flagCacheCredPath,
|
CredentialsPath: flagCacheCredPath,
|
||||||
|
KeyPath: flagCacheKeyPath,
|
||||||
}, slices.Values(collective)); err != nil {
|
}, slices.Values(collective)); err != nil {
|
||||||
return commandHandlerError(fmt.Sprintf("cannot copy: %v", err))
|
return commandHandlerError(fmt.Sprintf("cannot copy: %v", err))
|
||||||
}
|
}
|
||||||
|
43
copy.go
43
copy.go
@ -2,43 +2,12 @@ package nix
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"iter"
|
"iter"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Copy copies installables to the binary cache store.
|
||||||
EnvAwsSharedCredentialsFile = "AWS_SHARED_CREDENTIALS_FILE"
|
func Copy(ctx Context, store Store, installables iter.Seq[string]) error {
|
||||||
)
|
|
||||||
|
|
||||||
// A BinaryCache holds credentials and parameters to a s3 binary cache.
|
|
||||||
type BinaryCache struct {
|
|
||||||
// Compression is the name of the compression algorithm to use. Example: "zstd".
|
|
||||||
Compression string `json:"compression"`
|
|
||||||
// ParallelCompression determines whether parallel compression is enabled.
|
|
||||||
ParallelCompression bool `json:"parallel_compression,omitempty"`
|
|
||||||
|
|
||||||
// Bucket is the s3 bucket name.
|
|
||||||
Bucket string `json:"bucket"`
|
|
||||||
// Endpoint is the s3 endpoint. Example: "s3.example.org".
|
|
||||||
Endpoint string `json:"endpoint,omitempty"`
|
|
||||||
// Region is the s3 region. Example: "ap-northeast-1".
|
|
||||||
Region string `json:"region"`
|
|
||||||
// Scheme is the s3 protocol. Example: "https".
|
|
||||||
Scheme string `json:"scheme"`
|
|
||||||
// CredentialsPath is the path to the s3 shared credentials file.
|
|
||||||
CredentialsPath string `json:"credentials_path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (store *BinaryCache) String() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"s3://%s?compression=%s¶llel-compression=%t®ion=%s&scheme=%s&endpoint=%s",
|
|
||||||
store.Bucket, store.Compression, store.ParallelCompression, store.Region, store.Scheme, store.Endpoint,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy copies installables to the binary cache store, signing all paths using the key at keyPath.
|
|
||||||
func Copy(ctx Context, keyPath string, store *BinaryCache, installables iter.Seq[string]) error {
|
|
||||||
if store == nil {
|
if store == nil {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
@ -47,9 +16,13 @@ func Copy(ctx Context, keyPath string, store *BinaryCache, installables iter.Seq
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cmd := ctx.Nix(c, CommandCopy,
|
cmd := ctx.Nix(c, CommandCopy,
|
||||||
FlagTo, store.String()+"&secret-key="+keyPath,
|
FlagTo, store.String(),
|
||||||
FlagStdin)
|
FlagStdin)
|
||||||
cmd.Env = append(os.Environ(), EnvAwsSharedCredentialsFile+"="+store.CredentialsPath)
|
cmd.Env = 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.Stdout, cmd.Stderr = ctx.Streams()
|
cmd.Stdout, cmd.Stderr = ctx.Streams()
|
||||||
_, err := ctx.WriteStdin(cmd, installables, nil)
|
_, err := ctx.WriteStdin(cmd, installables, nil)
|
||||||
|
56
copy_test.go
56
copy_test.go
@ -16,8 +16,13 @@ func init() {
|
|||||||
var (
|
var (
|
||||||
flagCopyTo string
|
flagCopyTo string
|
||||||
flagCopyStdin bool
|
flagCopyStdin bool
|
||||||
|
flagNoCheckSigs bool
|
||||||
)
|
)
|
||||||
c.NewCommand(nix.CommandCopy, "emit samples for various `nix copy` cases", func(args []string) error {
|
c.NewCommand(nix.CommandCopy, "emit samples for various `nix copy` cases", func(args []string) error {
|
||||||
|
if flagNoCheckSigs && flagCopyStdin && flagCopyTo == "/" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if flagCopyTo != "s3://example?compression=none¶llel-compression=false®ion=us-east-1&scheme=http&endpoint=s3.example.org&secret-key="+nonexistent || !flagCopyStdin {
|
if flagCopyTo != "s3://example?compression=none¶llel-compression=false®ion=us-east-1&scheme=http&endpoint=s3.example.org&secret-key="+nonexistent || !flagCopyStdin {
|
||||||
return syscall.ENOSYS
|
return syscall.ENOSYS
|
||||||
}
|
}
|
||||||
@ -32,50 +37,15 @@ func init() {
|
|||||||
return nil
|
return nil
|
||||||
}).
|
}).
|
||||||
Flag(&flagCopyTo, trimFlagName(nix.FlagTo), command.StringFlag(""), nix.FlagTo).
|
Flag(&flagCopyTo, trimFlagName(nix.FlagTo), command.StringFlag(""), nix.FlagTo).
|
||||||
Flag(&flagCopyStdin, trimFlagName(nix.FlagStdin), command.BoolFlag(false), nix.FlagStdin)
|
Flag(&flagCopyStdin, trimFlagName(nix.FlagStdin), command.BoolFlag(false), nix.FlagStdin).
|
||||||
|
Flag(&flagNoCheckSigs, trimFlagName(nix.FlagNoCheckSigs), command.BoolFlag(false), nix.FlagNoCheckSigs)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBinaryCache(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
store *nix.BinaryCache
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"example", &nix.BinaryCache{
|
|
||||||
Compression: "none",
|
|
||||||
ParallelCompression: false,
|
|
||||||
Bucket: "example",
|
|
||||||
Endpoint: "s3.example.org",
|
|
||||||
Region: "us-east-1",
|
|
||||||
Scheme: "http",
|
|
||||||
CredentialsPath: "/dev/null",
|
|
||||||
}, "s3://example?compression=none¶llel-compression=false®ion=us-east-1&scheme=http&endpoint=s3.example.org"},
|
|
||||||
|
|
||||||
{"gensokyo", &nix.BinaryCache{
|
|
||||||
Compression: "zstd",
|
|
||||||
ParallelCompression: true,
|
|
||||||
Bucket: "nix-cache",
|
|
||||||
Endpoint: "s3.gensokyo.uk",
|
|
||||||
Region: "ap-northeast-1",
|
|
||||||
Scheme: "https",
|
|
||||||
CredentialsPath: "/var/lib/persist/cache/s3",
|
|
||||||
}, "s3://nix-cache?compression=zstd¶llel-compression=true®ion=ap-northeast-1&scheme=https&endpoint=s3.gensokyo.uk"},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
if got := tc.store.String(); got != tc.want {
|
|
||||||
t.Errorf("String: %q, want %q", got, tc.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopy(t *testing.T) {
|
func TestCopy(t *testing.T) {
|
||||||
stubNixCommand(t)
|
stubNixCommand(t)
|
||||||
if err := nix.Copy(
|
if err := nix.Copy(
|
||||||
newStubContext(t.Context(), nil, os.Stdout, os.Stderr),
|
newStubContext(t.Context(), nil, os.Stdout, os.Stderr),
|
||||||
nonexistent,
|
|
||||||
&nix.BinaryCache{
|
&nix.BinaryCache{
|
||||||
Compression: "none",
|
Compression: "none",
|
||||||
ParallelCompression: false,
|
ParallelCompression: false,
|
||||||
@ -84,6 +54,7 @@ func TestCopy(t *testing.T) {
|
|||||||
Region: "us-east-1",
|
Region: "us-east-1",
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
CredentialsPath: "/dev/null",
|
CredentialsPath: "/dev/null",
|
||||||
|
KeyPath: nonexistent,
|
||||||
},
|
},
|
||||||
slices.Values(instWant["pluiedev pappardelle"]),
|
slices.Values(instWant["pluiedev pappardelle"]),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -93,11 +64,20 @@ func TestCopy(t *testing.T) {
|
|||||||
t.Run("nil store", func(t *testing.T) {
|
t.Run("nil store", func(t *testing.T) {
|
||||||
if err := nix.Copy(
|
if err := nix.Copy(
|
||||||
newStubContext(t.Context(), nil, os.Stdout, os.Stderr),
|
newStubContext(t.Context(), nil, os.Stdout, os.Stderr),
|
||||||
nonexistent,
|
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
); !errors.Is(err, os.ErrInvalid) {
|
); !errors.Is(err, os.ErrInvalid) {
|
||||||
t.Errorf("Copy: error = %v, want %v", err, os.ErrInvalid)
|
t.Errorf("Copy: error = %v, want %v", err, os.ErrInvalid)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("chroot store", func(t *testing.T) {
|
||||||
|
if err := nix.Copy(
|
||||||
|
newStubContext(t.Context(), nil, os.Stdout, os.Stderr),
|
||||||
|
nix.Local(os.PathSeparator),
|
||||||
|
slices.Values([]string{"/nix/store"}),
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("Copy: error = %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
1
default.nix
Normal file
1
default.nix
Normal file
@ -0,0 +1 @@
|
|||||||
|
with import <nixpkgs> { }; pkgsStatic.callPackage ./package.nix { }
|
15
exec.go
15
exec.go
@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultWaitDelay = 15 * time.Second
|
defaultWaitDelay = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// Nix is the name of the nix program.
|
// Nix is the name of the nix program.
|
||||||
@ -19,6 +19,7 @@ var Nix = "nix"
|
|||||||
|
|
||||||
type nix struct {
|
type nix struct {
|
||||||
name string
|
name string
|
||||||
|
store Store
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
extra []string
|
extra []string
|
||||||
|
|
||||||
@ -40,12 +41,17 @@ A non-nil stderr implies verbose.
|
|||||||
|
|
||||||
Streams will not be connected for commands outputting JSON.
|
Streams will not be connected for commands outputting JSON.
|
||||||
*/
|
*/
|
||||||
func New(ctx context.Context, extraArgs []string, stdout, stderr io.Writer) Context {
|
func New(ctx context.Context, store Store, extraArgs []string, stdout, stderr io.Writer) Context {
|
||||||
|
extra := []string{ExtraExperimentalFeatures, ExperimentalFeaturesFlakes}
|
||||||
|
if store != nil {
|
||||||
|
extra = append(extraArgs, FlagStore, store.String())
|
||||||
|
}
|
||||||
return &nix{
|
return &nix{
|
||||||
name: Nix,
|
name: Nix,
|
||||||
|
store: store,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
// since flakes are supposedly experimental
|
// since flakes are supposedly experimental
|
||||||
extra: append(extraArgs, ExtraExperimentalFeatures, ExperimentalFeaturesFlakes),
|
extra: append(extraArgs, extra...),
|
||||||
|
|
||||||
stdout: stdout,
|
stdout: stdout,
|
||||||
stderr: stderr,
|
stderr: stderr,
|
||||||
@ -56,6 +62,9 @@ func (n *nix) Nix(ctx context.Context, arg ...string) *exec.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 {
|
||||||
|
cmd.Env = append(cmd.Env, n.store.Environ()...)
|
||||||
|
}
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ func stubNixCommand(t *testing.T) {
|
|||||||
|
|
||||||
// newStubContext creates a context for use with the nix command stub.
|
// newStubContext creates a context for use with the nix command stub.
|
||||||
func newStubContext(ctx context.Context, extraArgs []string, stdout, stderr io.Writer) nix.Context {
|
func newStubContext(ctx context.Context, extraArgs []string, stdout, stderr io.Writer) nix.Context {
|
||||||
return nix.New(ctx, append(stubExtraArgs, extraArgs...), stdout, stderr)
|
return nix.New(ctx, nil, append(stubExtraArgs, extraArgs...), stdout, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
type stubContextCommand struct {
|
type stubContextCommand struct {
|
||||||
@ -107,7 +107,7 @@ func TestNixStub(t *testing.T) {
|
|||||||
Flag(&flagExtraExperimentalFeatures, trimFlagName(nix.ExtraExperimentalFeatures), command.StringFlag(""),
|
Flag(&flagExtraExperimentalFeatures, trimFlagName(nix.ExtraExperimentalFeatures), command.StringFlag(""),
|
||||||
fmt.Sprintf("expects exactly %q", nix.ExperimentalFeaturesFlakes))
|
fmt.Sprintf("expects exactly %q", nix.ExperimentalFeaturesFlakes))
|
||||||
|
|
||||||
c.Command("true", command.UsageInternal, func([]string) error { return nil })
|
c.Command(nix.ValueTrue, command.UsageInternal, func([]string) error { return nil })
|
||||||
|
|
||||||
for _, f := range stubCommandInit {
|
for _, f := range stubCommandInit {
|
||||||
f(c)
|
f(c)
|
50
exec_test.go
50
exec_test.go
@ -1,6 +1,7 @@
|
|||||||
package nix_test
|
package nix_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -12,8 +13,36 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNixWriteStdin(t *testing.T) {
|
func TestNixWriteStdin(t *testing.T) {
|
||||||
|
t.Run("store", func(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,
|
||||||
|
}, nil, nil, nil)
|
||||||
|
cmd := ctx.Nix(t.Context(), nix.FlagVersion)
|
||||||
|
|
||||||
|
wantArgs := []string{
|
||||||
|
nix.Nix,
|
||||||
|
nix.FlagStore,
|
||||||
|
"s3://example?compression=none¶llel-compression=false®ion=us-east-1&scheme=http&endpoint=s3.example.org&secret-key=/proc/nonexistent",
|
||||||
|
nix.FlagVersion}
|
||||||
|
if !slices.Equal(cmd.Args, wantArgs) {
|
||||||
|
t.Errorf("Args = %#v, want %#v", cmd.Args, wantArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantEnv := []string{nix.EnvAwsSharedCredentialsFile + "=/dev/null"}
|
||||||
|
if !slices.Equal(cmd.Env, wantEnv) {
|
||||||
|
t.Errorf("Env = %#v, want %#v", cmd.Env, wantEnv)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("already set", func(t *testing.T) {
|
t.Run("already set", func(t *testing.T) {
|
||||||
ctx := nix.New(t.Context(), nil, os.Stdout, os.Stderr)
|
ctx := nix.New(t.Context(), nil, nil, os.Stdout, os.Stderr)
|
||||||
cmd := exec.CommandContext(t.Context(), nonexistent)
|
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 {
|
||||||
@ -33,4 +62,23 @@ func TestNixWriteStdin(t *testing.T) {
|
|||||||
t.Fatalf("WriteStdinCommand: error = %v, want %v", err, syscall.ENOSYS)
|
t.Fatalf("WriteStdinCommand: error = %v, want %v", err, syscall.ENOSYS)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("exit before cancel", func(t *testing.T) {
|
||||||
|
stubNixCommand(t)
|
||||||
|
ctx := newStubContext(t.Context(), nil, os.Stdout, os.Stderr)
|
||||||
|
c, cancel := context.WithCancel(t.Context())
|
||||||
|
defer cancel()
|
||||||
|
cmd := ctx.Nix(c, "true")
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
t.Fatalf("Start: error = %v", err)
|
||||||
|
}
|
||||||
|
// Cancel is skipped after exec.Cmd.Wait completes
|
||||||
|
if _, err := cmd.Process.Wait(); err != nil {
|
||||||
|
t.Fatalf("Wait: error = %v", err)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
if cmd.Err != nil {
|
||||||
|
t.Fatalf("Err = %v", cmd.Err)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,9 @@ const (
|
|||||||
// FlagVersion show version information.
|
// FlagVersion show version information.
|
||||||
FlagVersion = "--version"
|
FlagVersion = "--version"
|
||||||
|
|
||||||
|
// FlagStore is a loosely documented flag for specifying the store url to operate on.
|
||||||
|
FlagStore = "--store"
|
||||||
|
|
||||||
// FlagKeepGoing keep going in case of failed builds, to the greatest extent possible.
|
// FlagKeepGoing keep going in case of failed builds, to the greatest extent possible.
|
||||||
// That is, if building an input of some derivation fails, Nix will still build the other inputs,
|
// That is, if building an input of some derivation fails, Nix will still build the other inputs,
|
||||||
// but not the derivation itself.
|
// but not the derivation itself.
|
||||||
|
2
go.mod
2
go.mod
@ -2,4 +2,4 @@ module gensokyo.uk/nix
|
|||||||
|
|
||||||
go 1.24.4
|
go 1.24.4
|
||||||
|
|
||||||
require hakurei.app v0.1.1
|
require hakurei.app v0.1.2
|
||||||
|
4
go.sum
4
go.sum
@ -1,2 +1,2 @@
|
|||||||
hakurei.app v0.1.1 h1:b1ooWWIdvvBRj0BCOmK2wLfVs3nvYbxEHtQX5DP6bos=
|
hakurei.app v0.1.2 h1:jJLAThl15C1+4N8Ss95pk+FMtqb6m+foUTrhhwefMFg=
|
||||||
hakurei.app v0.1.1/go.mod h1:bWcF0vCO+ZOtZ2zK7L3e08sNb0kIkv1CBcdWTZtu1Gs=
|
hakurei.app v0.1.2/go.mod h1:bWcF0vCO+ZOtZ2zK7L3e08sNb0kIkv1CBcdWTZtu1Gs=
|
||||||
|
@ -107,7 +107,7 @@ func TestInstantiatedEvaluatorBadCommand(t *testing.T) {
|
|||||||
breakNixCommand(t)
|
breakNixCommand(t)
|
||||||
|
|
||||||
if _, err := nix.EvalInstantiated(
|
if _, err := nix.EvalInstantiated(
|
||||||
nix.New(t.Context(), nil, os.Stdout, os.Stderr),
|
nix.New(t.Context(), nil, nil, os.Stdout, os.Stderr),
|
||||||
"",
|
"",
|
||||||
); !errors.Is(err, wantErr) {
|
); !errors.Is(err, wantErr) {
|
||||||
t.Errorf("EvalInstantiated: error = %v, want %v", err, wantErr)
|
t.Errorf("EvalInstantiated: error = %v, want %v", err, wantErr)
|
||||||
|
24
package.nix
Normal file
24
package.nix
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
buildGoModule,
|
||||||
|
pkg-config,
|
||||||
|
}:
|
||||||
|
buildGoModule {
|
||||||
|
pname = "nix-tool";
|
||||||
|
version = "0.1.4";
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
vendorHash = "sha256-z07S4eulIujnFR5Sn2tpg8gxl+lh0zKcLQwPBg8gKsI=";
|
||||||
|
|
||||||
|
ldflags =
|
||||||
|
[ "-s -w" ]
|
||||||
|
++ lib.optionals stdenv.hostPlatform.isStatic [
|
||||||
|
"-linkmode external"
|
||||||
|
"-extldflags \"-static\""
|
||||||
|
];
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkg-config
|
||||||
|
];
|
||||||
|
}
|
56
store.go
Normal file
56
store.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package nix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Store interface {
|
||||||
|
// Environ returns extra environment variables specified by Store.
|
||||||
|
Environ() []string
|
||||||
|
|
||||||
|
fmt.Stringer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local points to a local filesystem path containing a nix store.
|
||||||
|
type Local string
|
||||||
|
|
||||||
|
func (Local) Environ() []string { return nil }
|
||||||
|
func (store Local) String() string { return string(store) }
|
||||||
|
|
||||||
|
const (
|
||||||
|
EnvAwsSharedCredentialsFile = "AWS_SHARED_CREDENTIALS_FILE"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A BinaryCache holds credentials and parameters to a s3 binary cache.
|
||||||
|
type BinaryCache struct {
|
||||||
|
// Compression is the name of the compression algorithm to use. Example: "zstd".
|
||||||
|
Compression string `json:"compression"`
|
||||||
|
// ParallelCompression determines whether parallel compression is enabled.
|
||||||
|
ParallelCompression bool `json:"parallel_compression,omitempty"`
|
||||||
|
|
||||||
|
// Bucket is the s3 bucket name.
|
||||||
|
Bucket string `json:"bucket"`
|
||||||
|
// Endpoint is the s3 endpoint. Example: "s3.example.org".
|
||||||
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
|
// Region is the s3 region. Example: "ap-northeast-1".
|
||||||
|
Region string `json:"region"`
|
||||||
|
// Scheme is the s3 protocol. Example: "https".
|
||||||
|
Scheme string `json:"scheme"`
|
||||||
|
// CredentialsPath is the path to the s3 shared credentials file.
|
||||||
|
CredentialsPath string `json:"credentials_path"`
|
||||||
|
|
||||||
|
// KeyPath is the path to the nix secret key for signing all newly copied paths.
|
||||||
|
KeyPath string `json:"key_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *BinaryCache) Environ() []string {
|
||||||
|
return []string{EnvAwsSharedCredentialsFile + "=" + strings.TrimSpace(store.CredentialsPath)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *BinaryCache) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"s3://%s?compression=%s¶llel-compression=%t®ion=%s&scheme=%s&endpoint=%s&secret-key=%s",
|
||||||
|
store.Bucket, store.Compression, store.ParallelCompression, store.Region, store.Scheme, store.Endpoint, store.KeyPath,
|
||||||
|
)
|
||||||
|
}
|
57
store_test.go
Normal file
57
store_test.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package nix_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gensokyo.uk/nix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLocal(t *testing.T) {
|
||||||
|
if got := nix.Local("/").String(); got != "/" {
|
||||||
|
t.Errorf("String: %v, want %v", got, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := nix.Local("").Environ(); got != nil {
|
||||||
|
t.Errorf("Environ: %v, want %v", got, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinaryCache(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
store *nix.BinaryCache
|
||||||
|
want string
|
||||||
|
wantEnv []string
|
||||||
|
}{
|
||||||
|
{"example", &nix.BinaryCache{
|
||||||
|
Compression: "none",
|
||||||
|
ParallelCompression: false,
|
||||||
|
Bucket: "example",
|
||||||
|
Endpoint: "s3.example.org",
|
||||||
|
Region: "us-east-1",
|
||||||
|
Scheme: "http",
|
||||||
|
CredentialsPath: "/dev/null",
|
||||||
|
KeyPath: nonexistent,
|
||||||
|
}, "s3://example?compression=none¶llel-compression=false®ion=us-east-1&scheme=http&endpoint=s3.example.org&secret-key=/proc/nonexistent",
|
||||||
|
[]string{nix.EnvAwsSharedCredentialsFile + "=/dev/null"}},
|
||||||
|
|
||||||
|
{"gensokyo", &nix.BinaryCache{
|
||||||
|
Compression: "zstd",
|
||||||
|
ParallelCompression: true,
|
||||||
|
Bucket: "nix-cache",
|
||||||
|
Endpoint: "s3.gensokyo.uk",
|
||||||
|
Region: "ap-northeast-1",
|
||||||
|
Scheme: "https",
|
||||||
|
CredentialsPath: "/var/lib/persist/cache/s3",
|
||||||
|
KeyPath: "/var/lib/persist/cache/key",
|
||||||
|
}, "s3://nix-cache?compression=zstd¶llel-compression=true®ion=ap-northeast-1&scheme=https&endpoint=s3.gensokyo.uk&secret-key=/var/lib/persist/cache/key",
|
||||||
|
[]string{nix.EnvAwsSharedCredentialsFile + "=/var/lib/persist/cache/s3"}},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if got := tc.store.String(); got != tc.want {
|
||||||
|
t.Errorf("String: %q, want %q", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user