Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions internal/boxcli/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/samber/lo"
"github.com/spf13/cobra"

"go.jetify.com/devbox/internal/boxcli/midcobra"
"go.jetify.com/devbox/internal/boxcli/usererr"
"go.jetify.com/devbox/internal/devbox"
"go.jetify.com/devbox/internal/devbox/devopt"
Expand Down Expand Up @@ -48,11 +49,18 @@ func generateCmd() *cobra.Command {
flags := &generateCmdFlags{}

command := &cobra.Command{
Use: "generate",
Aliases: []string{"gen"},
Short: "Generate supporting files for your project",
Args: cobra.MaximumNArgs(0),
PersistentPreRunE: ensureNixInstalled,
Use: "generate",
Aliases: []string{"gen"},
Short: "Generate supporting files for your project",
Args: cobra.MaximumNArgs(0),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// direnv runs `generate direnv --print-envrc` on every reload. It
// only prints a fixed template, so it doesn't need nix.
if isPrintEnvrc(cmd) {
return nil
}
return ensureNixInstalled(cmd, args)
},
}
command.AddCommand(genAliasCmd())
command.AddCommand(devcontainerCmd())
Expand Down Expand Up @@ -294,6 +302,12 @@ func runGenerateCmd(cmd *cobra.Command, flags *generateCmdFlags) error {
return nil
}

// isPrintEnvrc reports whether cmd is `generate direnv --print-envrc`.
func isPrintEnvrc(cmd *cobra.Command) bool {
printEnvrc, err := cmd.Flags().GetBool("print-envrc")
return err == nil && printEnvrc
}

func runGenerateDirenvCmd(cmd *cobra.Command, flags *generateCmdFlags) error {
// --print-envrc is used within the .envrc file and therefore doesn't make sense to also
// use it with --envrc-dir, which specifies a directory where the .envrc file should be generated.
Expand All @@ -305,6 +319,8 @@ func runGenerateDirenvCmd(cmd *cobra.Command, flags *generateCmdFlags) error {
}

if flags.printEnvrcContent {
// direnv runs this on every reload, so don't record it.
midcobra.SkipTelemetry(cmd)
return devbox.PrintEnvrcContent(
cmd.OutOrStdout(), devopt.EnvFlags(flags.envFlag), flags.config.path,
)
Expand Down
30 changes: 29 additions & 1 deletion internal/boxcli/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@

package boxcli

import "testing"
import (
"bytes"
"strings"
"testing"

"go.jetify.com/devbox/internal/nix"
)

func TestGenerateSubcommandsAcceptConfigFlags(t *testing.T) {
for _, sub := range generateCmd().Commands() {
Expand All @@ -14,3 +20,25 @@ func TestGenerateSubcommandsAcceptConfigFlags(t *testing.T) {
})
}
}

// TestGenerateDirenvPrintEnvrcSkipsNixCheck ensures that `generate direnv
// --print-envrc`, which direnv runs on every reload, doesn't check for nix.
func TestGenerateDirenvPrintEnvrcSkipsNixCheck(t *testing.T) {
if nix.Ensured() {
t.Skip("an earlier test already ensured nix is installed")
}

root := RootCmd()
stdout := &bytes.Buffer{}
root.SetOut(stdout)
root.SetArgs([]string{"generate", "direnv", "--print-envrc"})
if err := root.Execute(); err != nil {
t.Fatal(err)
}
if !strings.Contains(stdout.String(), "devbox shellenv") {
t.Errorf("got output %q, want an .envrc that calls devbox shellenv", stdout)
}
if nix.Ensured() {
t.Error("generate direnv --print-envrc checked that nix is installed")
}
}
11 changes: 11 additions & 0 deletions internal/boxcli/midcobra/midcobra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,14 @@ func TestExecuteParsesPersistentFlagsAfterUnknownFlags(t *testing.T) {
t.Error("Execute() with an unknown root flag = 0, want non-zero")
}
}

func TestSkipTelemetry(t *testing.T) {
cmd := &cobra.Command{Use: "test"}
if telemetrySkipped(cmd) {
t.Error("telemetry skipped before calling SkipTelemetry")
}
SkipTelemetry(cmd)
if !telemetrySkipped(cmd) {
t.Error("telemetry not skipped after calling SkipTelemetry")
}
}
20 changes: 20 additions & 0 deletions internal/boxcli/midcobra/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ type telemetryMiddleware struct{}
// telemetryMiddleware implements interface Middleware (compile-time check)
var _ Middleware = (*telemetryMiddleware)(nil)

// skipTelemetryAnnotation is set on a command by SkipTelemetry.
const skipTelemetryAnnotation = "devbox.skip-telemetry"

// SkipTelemetry stops the telemetry middleware from recording the current run
// of cmd. Use it for commands that run so often in the background (e.g. on
// every direnv reload) that recording them isn't useful.
func SkipTelemetry(cmd *cobra.Command) {
if cmd.Annotations == nil {
cmd.Annotations = map[string]string{}
}
cmd.Annotations[skipTelemetryAnnotation] = "true"
}

func telemetrySkipped(cmd *cobra.Command) bool {
return cmd.Annotations[skipTelemetryAnnotation] != ""
}

func (m *telemetryMiddleware) preRun(cmd *cobra.Command, args []string) {
telemetry.Start()
}
Expand All @@ -51,6 +68,9 @@ func (m *telemetryMiddleware) postRun(cmd *cobra.Command, args []string, runErr
// Ignore invalid commands/flags.
return
}
if telemetrySkipped(subcmd) {
return
}
meta.Command = subcmd.CommandPath()
meta.CommandFlags = flags

Expand Down
2 changes: 1 addition & 1 deletion specs/perf-improvements.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ every successful command. Fix: record the last upload time in the state dir and
only spawn the uploader when it was more than N minutes ago, or when the
buffered event count exceeds a threshold.

### 5. Make `generate direnv --print-envrc` cheap
### 5. Make `generate direnv --print-envrc` cheap (done in #3022)

**Win:** every direnv reload.
**Effort:** ~15–30 LOC.
Expand Down
Loading