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
19 changes: 19 additions & 0 deletions cmd/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"bytes"
"context"
"os"
"strings"
"testing"
Expand All @@ -13,6 +14,10 @@ func checkContains(str, substr string) bool {
}

func TestCheckCmd(t *testing.T) {
t.Cleanup(func() {
rootCmd.SetContext(context.Background())
})

setup := func(t *testing.T, withConfig bool) (*bytes.Buffer, *bytes.Buffer) {
t.Helper()

Expand Down Expand Up @@ -57,6 +62,9 @@ func TestCheckCmd(t *testing.T) {
// Given a directory with proper configuration
stdout, stderr := setup(t, true)

// Reset context for fresh test
rootCmd.SetContext(context.Background())

// When executing the command
err := Execute()

Expand All @@ -79,6 +87,9 @@ func TestCheckCmd(t *testing.T) {
// Given a directory with no configuration
_, _ = setup(t, false)

// Reset context for fresh test
rootCmd.SetContext(context.Background())

// When executing the command
err := Execute()

Expand All @@ -96,6 +107,11 @@ func TestCheckCmd(t *testing.T) {
}

func TestCheckNodeHealthCmd(t *testing.T) {
// Cleanup: reset rootCmd context after all subtests complete
t.Cleanup(func() {
rootCmd.SetContext(context.Background())
})

setup := func(t *testing.T, withConfig bool) (*bytes.Buffer, *bytes.Buffer) {
t.Helper()

Expand Down Expand Up @@ -213,6 +229,9 @@ func TestCheckNodeHealthCmd(t *testing.T) {
// Given a directory with no configuration
_, _ = setup(t, false)

// Reset context for fresh test
rootCmd.SetContext(context.Background())

// Setup command args
rootCmd.SetArgs([]string{"check", "node-health", "--nodes", "10.0.0.1"})

Expand Down
2 changes: 1 addition & 1 deletion cmd/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ var setContextCmd = &cobra.Command{
if err := runtime.NewRuntime(deps).
LoadShell().
LoadConfig().
SetContext(args[0]).
WriteResetToken().
SetContext(args[0]).
Do(); err != nil {
return fmt.Errorf("Error setting context: %w", err)
}
Expand Down
25 changes: 16 additions & 9 deletions cmd/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/pipelines"
"github.com/windsorcli/cli/pkg/runtime"
)

var (
Expand All @@ -25,21 +26,27 @@ var downCmd = &cobra.Command{
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// First, run the env pipeline in quiet mode to set up environment variables
var envPipeline pipelines.Pipeline
envPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "envPipeline")
if err != nil {
return fmt.Errorf("failed to set up env pipeline: %w", err)
// First, set up environment variables using runtime
deps := &runtime.Dependencies{
Injector: injector,
}
envCtx := context.WithValue(cmd.Context(), "quiet", true)
envCtx = context.WithValue(envCtx, "decrypt", true)
if err := envPipeline.Execute(envCtx); err != nil {
if err := runtime.NewRuntime(deps).
LoadShell().
CheckTrustedDirectory().
LoadConfig().
LoadSecretsProviders().
LoadEnvVars(runtime.EnvVarsOptions{
Decrypt: true,
Verbose: verbose,
}).
ExecutePostEnvHook(verbose).
Do(); err != nil {
return fmt.Errorf("failed to set up environment: %w", err)
}

// Then, run the init pipeline to initialize the environment
var initPipeline pipelines.Pipeline
initPipeline, err = pipelines.WithPipeline(injector, cmd.Context(), "initPipeline")
initPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "initPipeline")
if err != nil {
return fmt.Errorf("failed to set up init pipeline: %w", err)
}
Expand Down
106 changes: 7 additions & 99 deletions cmd/down_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"context"
"fmt"
"os"
"strings"
"testing"

Expand Down Expand Up @@ -34,20 +33,10 @@ type DownMocks struct {
func setupDownMocks(t *testing.T, opts ...*SetupOptions) *DownMocks {
t.Helper()

// Set up temporary directory and change to it
tmpDir := t.TempDir()
oldDir, _ := os.Getwd()
os.Chdir(tmpDir)
t.Cleanup(func() { os.Chdir(oldDir) })

// Get base mocks
// Get base mocks (includes trusted directory setup)
baseMocks := setupMocks(t, opts...)

// Register mock env pipeline in injector (needed since down runs env pipeline first)
mockEnvPipeline := pipelines.NewMockBasePipeline()
mockEnvPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil }
mockEnvPipeline.ExecuteFunc = func(ctx context.Context) error { return nil }
baseMocks.Injector.Register("envPipeline", mockEnvPipeline)
// Note: env pipeline is no longer used - environment setup is handled by runtime

// Register mock init pipeline in injector (needed since down runs init pipeline second)
mockInitPipeline := pipelines.NewMockBasePipeline()
Expand Down Expand Up @@ -109,51 +98,9 @@ func TestDownCmd(t *testing.T) {
}
})

t.Run("ErrorSettingUpEnvPipeline", func(t *testing.T) {
// Given a down command with failing env pipeline setup
mocks := setupDownMocks(t)
// Remove env pipeline from injector to cause failure
mocks.Injector.Register("envPipeline", nil)
cmd := createTestDownCmd()

// When executing the command
ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector)
cmd.SetContext(ctx)
err := cmd.Execute()

// Then an error should be returned
if err == nil {
t.Error("Expected error, got nil")
}
if !strings.Contains(err.Error(), "failed to set up environment") {
t.Errorf("Expected error about env pipeline setup, got: %v", err)
}
})

t.Run("ErrorExecutingEnvPipeline", func(t *testing.T) {
// Given a down command with failing env pipeline execution
mocks := setupDownMocks(t)
mockEnvPipeline := pipelines.NewMockBasePipeline()
mockEnvPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil }
mockEnvPipeline.ExecuteFunc = func(ctx context.Context) error {
return fmt.Errorf("env pipeline execution failed")
}
mocks.Injector.Register("envPipeline", mockEnvPipeline)
cmd := createTestDownCmd()
// Note: ErrorSettingUpEnvironment test removed - runtime is self-healing and creates missing dependencies

// When executing the command
ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector)
cmd.SetContext(ctx)
err := cmd.Execute()

// Then an error should be returned
if err == nil {
t.Error("Expected error, got nil")
}
if !strings.Contains(err.Error(), "failed to set up environment") {
t.Errorf("Expected error about environment setup, got: %v", err)
}
})
// Note: ErrorExecutingEnvPipeline test removed - env pipeline no longer used

t.Run("ErrorExecutingInitPipeline", func(t *testing.T) {
// Given a down command with failing init pipeline execution
Expand Down Expand Up @@ -244,40 +191,7 @@ func TestDownCmd(t *testing.T) {
}
})

t.Run("EnvPipelineContextFlags", func(t *testing.T) {
// Given a down command with mocks
mocks := setupDownMocks(t)
var envExecutedContext context.Context
mockEnvPipeline := pipelines.NewMockBasePipeline()
mockEnvPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil }
mockEnvPipeline.ExecuteFunc = func(ctx context.Context) error {
envExecutedContext = ctx
return nil
}
mocks.Injector.Register("envPipeline", mockEnvPipeline)
cmd := createTestDownCmd()

// When executing the command
ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector)
cmd.SetContext(ctx)
err := cmd.Execute()

// Then no error should be returned
if err != nil {
t.Errorf("Expected no error, got %v", err)
}

// And env pipeline should be executed with quiet and decrypt flags
if envExecutedContext == nil {
t.Fatal("Expected context to be passed to env pipeline")
}
if quietValue := envExecutedContext.Value("quiet"); quietValue != true {
t.Errorf("Expected quiet flag to be true, got %v", quietValue)
}
if decryptValue := envExecutedContext.Value("decrypt"); decryptValue != true {
t.Errorf("Expected decrypt flag to be true, got %v", decryptValue)
}
})
// Note: EnvPipelineContextFlags test removed - env pipeline no longer used

t.Run("PipelineOrchestrationOrder", func(t *testing.T) {
// Given a down command with mocks
Expand All @@ -286,13 +200,7 @@ func TestDownCmd(t *testing.T) {
// And pipelines that track execution order
executionOrder := []string{}

mockEnvPipeline := pipelines.NewMockBasePipeline()
mockEnvPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil }
mockEnvPipeline.ExecuteFunc = func(ctx context.Context) error {
executionOrder = append(executionOrder, "env")
return nil
}
mocks.Injector.Register("envPipeline", mockEnvPipeline)
// Note: env pipeline no longer used - environment setup handled by runtime

mockInitPipeline := pipelines.NewMockBasePipeline()
mockInitPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil }
Expand Down Expand Up @@ -321,7 +229,7 @@ func TestDownCmd(t *testing.T) {
t.Errorf("Expected no error, got %v", err)
}

expectedOrder := []string{"env", "init", "down"}
expectedOrder := []string{"init", "down"}
if len(executionOrder) != len(expectedOrder) {
t.Errorf("Expected %d pipelines to execute, got %d", len(expectedOrder), len(executionOrder))
}
Expand Down
54 changes: 36 additions & 18 deletions cmd/env.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package cmd

import (
"context"
"fmt"
"os"

"github.com/spf13/cobra"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/pipelines"
"github.com/windsorcli/cli/pkg/runtime"
)

var envCmd = &cobra.Command{
Expand All @@ -16,12 +15,10 @@ var envCmd = &cobra.Command{
Long: "Output commands to set environment variables for the application.",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// Get flags
hook, _ := cmd.Flags().GetBool("hook")
decrypt, _ := cmd.Flags().GetBool("decrypt")
verbose, _ := cmd.Flags().GetBool("verbose")

// Set NO_CACHE=true unless --hook is specified or NO_CACHE is already set
if !hook && os.Getenv("NO_CACHE") == "" {
Expand All @@ -30,24 +27,45 @@ var envCmd = &cobra.Command{
}
}

// Create execution context with flags
ctx := cmd.Context()
if decrypt {
ctx = context.WithValue(ctx, "decrypt", true)
// Create dependencies with injector from command context
deps := &runtime.Dependencies{
Injector: cmd.Context().Value(injectorKey).(di.Injector),
}
if hook {
ctx = context.WithValue(ctx, "hook", true)

// Create output function for environment variables and aliases
outputFunc := func(output string) {
fmt.Fprint(cmd.OutOrStdout(), output)
}

// Set up the env pipeline
pipeline, err := pipelines.WithPipeline(injector, ctx, "envPipeline")
if err != nil {
return fmt.Errorf("failed to set up env pipeline: %w", err)
// Execute the complete workflow
rt := runtime.NewRuntime(deps).
LoadShell().
CheckTrustedDirectory().
HandleSessionReset().
LoadConfig().
LoadSecretsProviders().
LoadEnvVars(runtime.EnvVarsOptions{
Decrypt: decrypt,
Verbose: verbose,
}).
PrintEnvVars(runtime.EnvVarsOptions{
Verbose: verbose,
Export: hook,
OutputFunc: outputFunc,
})

// Only print aliases in hook mode
if hook {
rt = rt.PrintAliases(outputFunc)
}

// Execute the pipeline
if err := pipeline.Execute(ctx); err != nil {
return fmt.Errorf("Error executing env pipeline: %w", err)
if err := rt.ExecutePostEnvHook(verbose).Do(); err != nil {
if hook {
// In hook mode, return success even if there are errors
// This prevents shell initialization failures from breaking the environment
return nil
}
return fmt.Errorf("Error executing environment workflow: %w", err)
}

return nil
Expand Down
Loading
Loading