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
20 changes: 8 additions & 12 deletions cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ var checkCmd = &cobra.Command{
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// Create check pipeline
pipeline := pipelines.NewCheckPipeline()

// Create output function
outputFunc := func(output string) {
fmt.Fprintln(cmd.OutOrStdout(), output)
Expand All @@ -38,9 +35,10 @@ var checkCmd = &cobra.Command{
ctx := context.WithValue(cmd.Context(), "operation", "tools")
ctx = context.WithValue(ctx, "output", outputFunc)

// Initialize the pipeline
if err := pipeline.Initialize(injector, ctx); err != nil {
return fmt.Errorf("Error initializing: %w", err)
// Set up the check pipeline
pipeline, err := pipelines.WithPipeline(injector, ctx, "checkPipeline")
if err != nil {
return fmt.Errorf("failed to set up check pipeline: %w", err)
}

// Execute the pipeline
Expand All @@ -61,9 +59,6 @@ var checkNodeHealthCmd = &cobra.Command{
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// Create check pipeline
pipeline := pipelines.NewCheckPipeline()

// Require nodes to be specified
if len(nodeHealthNodes) == 0 {
return fmt.Errorf("No nodes specified. Use --nodes flag to specify nodes to check")
Expand All @@ -86,9 +81,10 @@ var checkNodeHealthCmd = &cobra.Command{
ctx = context.WithValue(ctx, "version", nodeHealthVersion)
ctx = context.WithValue(ctx, "output", outputFunc)

// Initialize the pipeline
if err := pipeline.Initialize(injector, ctx); err != nil {
return fmt.Errorf("Error initializing: %w", err)
// Set up the check pipeline
pipeline, err := pipelines.WithPipeline(injector, ctx, "checkPipeline")
if err != nil {
return fmt.Errorf("failed to set up check pipeline: %w", err)
}

// Execute the pipeline
Expand Down
20 changes: 8 additions & 12 deletions cmd/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ var getContextCmd = &cobra.Command{
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// Create context pipeline
pipeline := pipelines.NewContextPipeline()

// Create output function
outputFunc := func(output string) {
fmt.Fprintln(cmd.OutOrStdout(), output)
Expand All @@ -31,9 +28,10 @@ var getContextCmd = &cobra.Command{
ctx := context.WithValue(cmd.Context(), "operation", "get")
ctx = context.WithValue(ctx, "output", outputFunc)

// Initialize the pipeline
if err := pipeline.Initialize(injector, ctx); err != nil {
return fmt.Errorf("Error initializing: %w", err)
// Set up the context pipeline
pipeline, err := pipelines.WithPipeline(injector, ctx, "contextPipeline")
if err != nil {
return fmt.Errorf("failed to set up context pipeline: %w", err)
}

// Execute the pipeline
Expand All @@ -55,9 +53,6 @@ var setContextCmd = &cobra.Command{
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// Create context pipeline
pipeline := pipelines.NewContextPipeline()

// Create output function
outputFunc := func(output string) {
fmt.Fprintln(cmd.OutOrStdout(), output)
Expand All @@ -68,9 +63,10 @@ var setContextCmd = &cobra.Command{
ctx = context.WithValue(ctx, "contextName", args[0])
ctx = context.WithValue(ctx, "output", outputFunc)

// Initialize the pipeline
if err := pipeline.Initialize(injector, ctx); err != nil {
return fmt.Errorf("Error initializing: %w", err)
// Set up the context pipeline
pipeline, err := pipelines.WithPipeline(injector, ctx, "contextPipeline")
if err != nil {
return fmt.Errorf("failed to set up context pipeline: %w", err)
}

// Execute the pipeline
Expand Down
10 changes: 4 additions & 6 deletions cmd/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ var envCmd = &cobra.Command{
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// Create env pipeline
pipeline := pipelines.NewEnvPipeline()

// Get flags
hook, _ := cmd.Flags().GetBool("hook")
decrypt, _ := cmd.Flags().GetBool("decrypt")
Expand All @@ -37,9 +34,10 @@ var envCmd = &cobra.Command{
ctx = context.WithValue(ctx, "hook", true)
}

// Initialize the pipeline with appropriate configuration
if err := pipeline.Initialize(injector, ctx); err != nil {
return fmt.Errorf("Error initializing: %w", err)
// 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 pipeline
Expand Down
24 changes: 6 additions & 18 deletions cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,9 @@ var execCmd = &cobra.Command{
injector := cmd.Context().Value(injectorKey).(di.Injector)

// First, run the env pipeline in quiet mode to set up environment variables
var envPipeline pipelines.Pipeline
if existing := injector.Resolve("envPipeline"); existing != nil {
envPipeline = existing.(pipelines.Pipeline)
} else {
envPipeline = pipelines.NewEnvPipeline()
if err := envPipeline.Initialize(injector, cmd.Context()); err != nil {
return fmt.Errorf("failed to initialize env pipeline: %w", err)
}
injector.Register("envPipeline", envPipeline)
envPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "envPipeline")
if err != nil {
return fmt.Errorf("failed to set up env pipeline: %w", err)
}

// Execute env pipeline in quiet mode (inject environment variables without printing)
Expand All @@ -44,15 +38,9 @@ var execCmd = &cobra.Command{
}

// Then, run the exec pipeline to execute the command
var execPipeline pipelines.Pipeline
if existing := injector.Resolve("execPipeline"); existing != nil {
execPipeline = existing.(pipelines.Pipeline)
} else {
execPipeline = pipelines.NewExecPipeline()
if err := execPipeline.Initialize(injector, cmd.Context()); err != nil {
return fmt.Errorf("failed to initialize exec pipeline: %w", err)
}
injector.Register("execPipeline", execPipeline)
execPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "execPipeline")
if err != nil {
return fmt.Errorf("failed to set up exec pipeline: %w", err)
}

// Create execution context with command and arguments
Expand Down
3 changes: 2 additions & 1 deletion cmd/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func TestExecCmd(t *testing.T) {
Use: "exec -- [command]",
Short: "Execute a shell command with environment variables",
Long: "Execute a shell command with environment variables set for the application.",
Args: cobra.MinimumNArgs(1),
SilenceUsage: true,
RunE: execCmd.RunE,
}
Expand Down Expand Up @@ -79,7 +80,7 @@ func TestExecCmd(t *testing.T) {
t.Error("Expected error, got nil")
}

expectedError := "no command provided"
expectedError := "requires at least 1 arg(s), only received 0"
if err.Error() != expectedError {
t.Errorf("Expected error %q, got %q", expectedError, err.Error())
}
Expand Down
19 changes: 7 additions & 12 deletions cmd/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,23 @@ var hookCmd = &cobra.Command{
Short: "Prints out shell hook information per platform (zsh,bash,fish,tcsh,powershell).",
Long: "Prints out shell hook information for each platform (zsh,bash,fish,tcsh,powershell).",
SilenceUsage: true,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("No shell name provided")
}

// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// Create hook pipeline
pipeline := pipelines.NewHookPipeline()

// Initialize the pipeline
if err := pipeline.Initialize(injector, cmd.Context()); err != nil {
return fmt.Errorf("Error initializing: %w", err)
}

// Create execution context with shell type
ctx := context.WithValue(cmd.Context(), "shellType", args[0])
if verbose {
ctx = context.WithValue(ctx, "verbose", true)
}

// Set up the hook pipeline
pipeline, err := pipelines.WithPipeline(injector, ctx, "hookPipeline")
if err != nil {
return fmt.Errorf("failed to set up hook pipeline: %w", err)
}

// Execute the pipeline
if err := pipeline.Execute(ctx); err != nil {
return fmt.Errorf("Error executing hook pipeline: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion cmd/hook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestHookCmd(t *testing.T) {
}

// And error should contain usage message
expectedError := "No shell name provided"
expectedError := "accepts 1 arg(s), received 0"
if err.Error() != expectedError {
t.Errorf("Expected error %q, got %q", expectedError, err.Error())
}
Expand Down
33 changes: 17 additions & 16 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,24 @@ var initCmd = &cobra.Command{
Long: "Initialize the application environment with the specified context configuration",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
var injector di.Injector
if contextInjector := cmd.Context().Value(injectorKey); contextInjector != nil {
injector = contextInjector.(di.Injector)
} else {
injector = di.NewInjector()
// 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
envPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "envPipeline")
if err != nil {
return fmt.Errorf("failed to set up env pipeline: %w", err)
}
envCtx := context.WithValue(cmd.Context(), "quiet", true)
envCtx = context.WithValue(envCtx, "decrypt", true)
if err := envPipeline.Execute(envCtx); err != nil {
return fmt.Errorf("failed to set up environment: %w", err)
}

// Check if init pipeline exists in injector, create if not
var pipeline pipelines.Pipeline
if existing := injector.Resolve("initPipeline"); existing != nil {
pipeline = existing.(pipelines.Pipeline)
} else {
pipeline = pipelines.NewInitPipeline()
if err := pipeline.Initialize(injector, cmd.Context()); err != nil {
return fmt.Errorf("failed to initialize pipeline: %w", err)
}
injector.Register("initPipeline", pipeline)
// Set up the init pipeline
initPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "initPipeline")
if err != nil {
return fmt.Errorf("failed to set up init pipeline: %w", err)
}

ctx := cmd.Context()
Expand Down Expand Up @@ -142,7 +143,7 @@ var initCmd = &cobra.Command{
}
}

return pipeline.Execute(ctx)
return initPipeline.Execute(ctx)
},
}

Expand Down
16 changes: 11 additions & 5 deletions cmd/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,17 @@ func setupInitTest(t *testing.T, opts ...*SetupOptions) *InitMocks {
baseMocks.Shell.AddCurrentDirToTrustedFileFunc = func() error { return nil }
baseMocks.Shell.WriteResetTokenFunc = func() (string, error) { return "test-token", nil }

// Register mock pipeline in injector (following exec_test.go pattern)
mockPipeline := pipelines.NewMockBasePipeline()
mockPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil }
mockPipeline.ExecuteFunc = func(ctx context.Context) error { return nil }
baseMocks.Injector.Register("initPipeline", mockPipeline)
// Register mock env pipeline in injector (needed since init now 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)

// Register mock init pipeline in injector (following exec_test.go pattern)
mockInitPipeline := pipelines.NewMockBasePipeline()
mockInitPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil }
mockInitPipeline.ExecuteFunc = func(ctx context.Context) error { return nil }
baseMocks.Injector.Register("initPipeline", mockInitPipeline)

return &InitMocks{
Injector: baseMocks.Injector,
Expand Down
44 changes: 44 additions & 0 deletions pkg/pipelines/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,50 @@ type Pipeline interface {
Execute(ctx context.Context) error
}

// PipelineConstructor defines a function that creates a new pipeline instance
type PipelineConstructor func() Pipeline

// =============================================================================
// Pipeline Factory
// =============================================================================

// pipelineConstructors maps pipeline names to their constructor functions
var pipelineConstructors = map[string]PipelineConstructor{
"envPipeline": func() Pipeline { return NewEnvPipeline() },
"initPipeline": func() Pipeline { return NewInitPipeline() },
"execPipeline": func() Pipeline { return NewExecPipeline() },
"contextPipeline": func() Pipeline { return NewContextPipeline() },
"hookPipeline": func() Pipeline { return NewHookPipeline() },
"checkPipeline": func() Pipeline { return NewCheckPipeline() },
}

// WithPipeline resolves or creates a pipeline instance from the DI container by name.
// If the pipeline already exists in the injector, it is returned directly. Otherwise, a new instance is constructed,
// initialized with the provided injector and context, registered in the DI container, and then returned.
// Returns an error if the pipeline name is unknown or initialization fails.
func WithPipeline(injector di.Injector, ctx context.Context, pipelineName string) (Pipeline, error) {
if existing := injector.Resolve(pipelineName); existing != nil {
if pipeline, ok := existing.(Pipeline); ok {
return pipeline, nil
}
}

constructor, exists := pipelineConstructors[pipelineName]
if !exists {
return nil, fmt.Errorf("unknown pipeline: %s", pipelineName)
}

pipeline := constructor()

if err := pipeline.Initialize(injector, ctx); err != nil {
return nil, fmt.Errorf("failed to initialize %s: %w", pipelineName, err)
}

injector.Register(pipelineName, pipeline)

return pipeline, nil
}

// BasePipeline provides common pipeline functionality including config loading
// Specific pipelines should embed this and add their own dependencies
type BasePipeline struct {
Expand Down
Loading
Loading