From 270298c73d30d58606cf85ad920f83002379924e Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 20 Jul 2025 01:33:10 +0900 Subject: [PATCH] cmd/nixbuild: implement cache command The tool this entire library is created for. --- cmd/nixbuild/format.go | 22 ++++++++-- cmd/nixbuild/main.go | 90 ++++++++++++++++++++++++++++++----------- cmd/nixbuild/resolve.go | 36 +++++++++++++++++ 3 files changed, 122 insertions(+), 26 deletions(-) create mode 100644 cmd/nixbuild/resolve.go diff --git a/cmd/nixbuild/format.go b/cmd/nixbuild/format.go index 19697f2..615b623 100644 --- a/cmd/nixbuild/format.go +++ b/cmd/nixbuild/format.go @@ -5,7 +5,7 @@ import ( "os" "strings" - "git.gensokyo.uk/yonah/nixbuild" + "gensokyo.uk/nix" ) func formatInstallable(name string, installable *string, flagNixOS bool, args []string) error { @@ -21,16 +21,32 @@ func formatInstallable(name string, installable *string, flagNixOS bool, args [] fields := strings.SplitN(*installable, "#", 2) switch len(fields) { case 2: - *installable = nixbuild.NixOSInstallable(fields[0], fields[1]) + *installable = nix.InstallableNixOS(fields[0], fields[1]) return nil case 1: hostname, err := os.Hostname() if err != nil { return commandHandlerError(fmt.Sprintf("cannot get hostname: %v", err)) } - *installable = nixbuild.NixOSInstallable(fields[0], hostname) + *installable = nix.InstallableNixOS(fields[0], hostname) return nil default: return commandHandlerError("unexpected installable") } } + +func replaceFromEnviron(v map[string]*string) error { + for key, p := range v { + if *p != "env" { + continue + } + + if s, ok := os.LookupEnv(key); !ok { + return commandHandlerError(key + " not set") + } else { + *p = s + } + } + + return nil +} diff --git a/cmd/nixbuild/main.go b/cmd/nixbuild/main.go index 7f9c753..e465236 100644 --- a/cmd/nixbuild/main.go +++ b/cmd/nixbuild/main.go @@ -47,6 +47,68 @@ func main() { Flag(&flagVerbose, "v", command.BoolFlag(false), "Connect nix stderr"). Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable") + // this is most likely what you are here for + var ( + flagCacheKeyPath string + + flagCacheComp string + flagCachePComp bool + flagCacheBucket string + flagCacheEndpoint string + flagCacheRegion string + flagCacheScheme string + flagCacheCredPath string + ) + c.NewCommand("cache", "Build an installable and upload it to a binary cache", func(args []string) error { + if err := replaceFromEnviron(map[string]*string{ + "NIX_TOOL_CACHE_KEY": &flagCacheKeyPath, + "NIX_TOOL_CACHE_COMPRESSION": &flagCacheComp, + "NIX_TOOL_CACHE_BUCKET": &flagCacheBucket, + "NIX_TOOL_CACHE_ENDPOINT": &flagCacheEndpoint, + "NIX_TOOL_CACHE_REGION": &flagCacheRegion, + "NIX_TOOL_CACHE_SCHEME": &flagCacheScheme, + "NIX_TOOL_CACHE_CRED": &flagCacheCredPath, + }); err != nil { + return err + } + + var ( + installable string + collective []string + ) + if err := buildAndResolve(ctx, "cache", &installable, &collective, flagNixOS, args); err != nil { + return err + } + + log.Println("signing collected paths") + if err := nix.Sign(ctx, flagCacheKeyPath, slices.Values(collective)); err != nil { + return commandHandlerError(fmt.Sprintf("cannot sign: %v", err)) + } + + log.Println("copying to binary cache") + if err := nix.Copy(ctx, flagCacheKeyPath, &nix.BinaryCache{ + Compression: flagCacheComp, + ParallelCompression: flagCachePComp, + Bucket: flagCacheBucket, + Endpoint: flagCacheEndpoint, + Region: flagCacheRegion, + Scheme: flagCacheScheme, + CredentialsPath: flagCacheCredPath, + }, slices.Values(collective)); err != nil { + return commandHandlerError(fmt.Sprintf("cannot copy: %v", err)) + } + + return nil + }). + Flag(&flagCacheKeyPath, "secret-key", command.StringFlag("env"), "Path to the secret key used to sign the binary cache"). + Flag(&flagCacheComp, "compression", command.StringFlag("zstd"), "Name of the compression algorithm to use"). + Flag(&flagCachePComp, "parallel-compression", command.BoolFlag(true), "Whether parallel compression is enabled"). + Flag(&flagCacheBucket, "bucket", command.StringFlag("env"), "S3 bucket name"). + Flag(&flagCacheEndpoint, "endpoint", command.StringFlag("env"), "S3 endpoint"). + Flag(&flagCacheRegion, "region", command.StringFlag("env"), "S3 region"). + Flag(&flagCacheScheme, "scheme", command.StringFlag("https"), "S3 scheme"). + Flag(&flagCacheCredPath, "credentials-path", command.StringFlag("env"), "Path to the s3 shared credentials file") + c.Command("show", command.UsageInternal, func(args []string) error { if len(args) < 1 { return commandHandlerError("show requires at least 1 argument") @@ -90,32 +152,14 @@ func main() { resolveFlagOut string ) c.Command("resolve", "Resolve possible build-time dependencies", func(args []string) error { - var installable string - if err := formatInstallable("resolve", &installable, flagNixOS, args); err != nil { + var ( + installable string + collective []string + ) + if err := buildAndResolve(ctx, "resolve", &installable, &collective, flagNixOS, args); err != nil { return err } - log.Printf("evaluating %s", installable) - var installables []string - 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 := 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 := nix.DerivationShow(ctx, slices.Values(installables)); err != nil { - return commandHandlerError(fmt.Sprintf("cannot show: %v", err)) - } else { - collective = nix.CollectFromDerivations(derivations) - } - f, err := os.Create(resolveFlagOut) if err != nil { return commandHandlerError(fmt.Sprintf("cannot create store paths file: %v", err)) diff --git a/cmd/nixbuild/resolve.go b/cmd/nixbuild/resolve.go new file mode 100644 index 0000000..7414df1 --- /dev/null +++ b/cmd/nixbuild/resolve.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "log" + "slices" + + "gensokyo.uk/nix" +) + +func buildAndResolve(ctx nix.Context, name string, installable *string, collective *[]string, flagNixOS bool, args []string) error { + if err := formatInstallable(name, installable, flagNixOS, args); err != nil { + return err + } + + log.Printf("evaluating %s", *installable) + var installables []string + if v, err := nix.EvalInstantiated(ctx, *installable); err != nil { + return commandHandlerError(fmt.Sprintf("cannot evaluate: %v", err)) + } else { + installables = v + } + + log.Println("building instantiated derivations") + if err := nix.Build(ctx, slices.Values(installables)); err != nil { + return commandHandlerError(fmt.Sprintf("cannot build: %v", err)) + } + + log.Println("collecting store paths") + if derivations, err := nix.DerivationShow(ctx, slices.Values(installables)); err != nil { + return commandHandlerError(fmt.Sprintf("cannot get derivations: %v", err)) + } else { + *collective = nix.CollectFromDerivations(derivations) + return nil + } +}