diff --git a/.vscode/launch.json b/.vscode/launch.json index 3b534b9c9..e3b001061 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -25,7 +25,7 @@ "request": "launch", "mode": "auto", "program": "${workspaceFolder}/cmd/windsor/main.go", - "args": ["init", "local"] + "args": ["init", "local"], }, { "name": "Windsor Up", diff --git a/cmd/build_id_test.go b/cmd/build_id_test.go index 4e472100e..1e77dc4d6 100644 --- a/cmd/build_id_test.go +++ b/cmd/build_id_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) func TestBuildIDCmd(t *testing.T) { diff --git a/cmd/bundle_test.go b/cmd/bundle_test.go index 6626986bf..59968f601 100644 --- a/cmd/bundle_test.go +++ b/cmd/bundle_test.go @@ -9,12 +9,12 @@ import ( "testing" "github.com/spf13/cobra" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/cmd/down_test.go b/cmd/down_test.go index 331aa6063..6017a55b9 100644 --- a/cmd/down_test.go +++ b/cmd/down_test.go @@ -8,10 +8,10 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/pipelines" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/cmd/init.go b/cmd/init.go index 8759e7ab5..001ba1b26 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/spf13/cobra" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/pipelines" "github.com/windsorcli/cli/pkg/runtime" diff --git a/cmd/init_test.go b/cmd/init_test.go index 0b80270f6..d30d2515b 100644 --- a/cmd/init_test.go +++ b/cmd/init_test.go @@ -9,11 +9,11 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/pipelines" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/cmd/push_test.go b/cmd/push_test.go index 68a531bb4..98b939393 100644 --- a/cmd/push_test.go +++ b/cmd/push_test.go @@ -9,12 +9,12 @@ import ( "testing" "github.com/spf13/cobra" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/cmd/root_test.go b/cmd/root_test.go index e0c093c04..b5306a8cc 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -13,13 +13,13 @@ import ( "testing" "github.com/spf13/cobra" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/envvars" + envvars "github.com/windsorcli/cli/pkg/context/env" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" blueprintpkg "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/secrets" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/secrets" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/cmd/up_test.go b/cmd/up_test.go index 44088bd66..20545d111 100644 --- a/cmd/up_test.go +++ b/cmd/up_test.go @@ -9,10 +9,10 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/pipelines" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/config/config_handler.go b/pkg/context/config/config_handler.go similarity index 93% rename from pkg/config/config_handler.go rename to pkg/context/config/config_handler.go index a56f71413..9301e71dd 100644 --- a/pkg/config/config_handler.go +++ b/pkg/context/config/config_handler.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/windsorcli/cli/api/v1alpha1" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" ) // The ConfigHandler is a core component that manages configuration state and context across the application. @@ -61,6 +61,7 @@ type configHandler struct { shims *Shims schemaValidator *SchemaValidator data map[string]any + defaultConfig *v1alpha1.Context } // ============================================================================= @@ -323,7 +324,30 @@ func (c *configHandler) SaveConfig(overwrite ...bool) error { } if !contextExists || shouldOverwrite { - contextStruct := c.mapToContext(staticFields) + var contextStruct *v1alpha1.Context + if !contextExists && c.defaultConfig != nil { + defaultCopy := c.defaultConfig.DeepCopy() + if len(staticFields) > 0 { + overlayStruct := c.mapToContext(staticFields) + if overlayStruct != nil { + defaultCopy.Merge(overlayStruct) + } + } + contextStruct = defaultCopy + } else { + mergedStaticFields := staticFields + if c.schemaValidator != nil && c.schemaValidator.Schema != nil { + defaults, err := c.schemaValidator.GetSchemaDefaults() + if err == nil && defaults != nil { + defaultsStatic, _ := c.separateStaticAndDynamicFields(defaults) + mergedStaticFields = c.deepMerge(defaultsStatic, staticFields) + } + } + contextStruct = c.mapToContext(mergedStaticFields) + } + if contextStruct == nil { + contextStruct = &v1alpha1.Context{} + } data, err := c.shims.YamlMarshal(contextStruct) if err != nil { return fmt.Errorf("error marshalling context config: %w", err) @@ -353,6 +377,8 @@ func (c *configHandler) SaveConfig(overwrite ...bool) error { // SetDefault sets the default context configuration in the config handler's internal data. // It marshals the given v1alpha1.Context struct to a map and merges it into the handler's data. // This method is typically used during initialization when context files do not yet exist. +// The original default config is stored so it can be used when saving a new config file to ensure +// all default values are preserved even if they were empty/nil (which would be omitted by omitempty tags). func (c *configHandler) SetDefault(context v1alpha1.Context) error { contextData, err := c.shims.YamlMarshal(context) if err != nil { @@ -365,6 +391,10 @@ func (c *configHandler) SetDefault(context v1alpha1.Context) error { } c.data = c.deepMerge(c.data, contextMap) + + contextCopy := context + c.defaultConfig = &contextCopy + return nil } @@ -735,6 +765,8 @@ func (c *configHandler) LoadSchemaFromBytes(schemaContent []byte) error { // The result provides all configuration values, with schema defaults filled in for missing keys, ensuring // downstream consumers (such as blueprint processing) always receive a complete set of config values. // If the schema validator or schema is unavailable, only the currently loaded data is returned. +// It also ensures that cluster.controlplanes.nodes and cluster.workers.nodes are initialized as empty maps +// even though they are not serialized to YAML, so template expressions can safely evaluate them. func (c *configHandler) GetContextValues() (map[string]any, error) { result := make(map[string]any) if c.schemaValidator != nil && c.schemaValidator.Schema != nil { @@ -744,6 +776,43 @@ func (c *configHandler) GetContextValues() (map[string]any, error) { } } result = c.deepMerge(result, c.data) + + clusterVal, ok := result["cluster"].(map[string]any) + if !ok { + if _, exists := result["cluster"]; !exists || result["cluster"] == nil { + result["cluster"] = map[string]any{ + "controlplanes": map[string]any{ + "nodes": make(map[string]any), + }, + "workers": map[string]any{ + "nodes": make(map[string]any), + }, + } + return result, nil + } + return result, nil + } + + if controlplanesVal, ok := clusterVal["controlplanes"].(map[string]any); ok { + if _, exists := controlplanesVal["nodes"]; !exists { + controlplanesVal["nodes"] = make(map[string]any) + } + } else { + clusterVal["controlplanes"] = map[string]any{ + "nodes": make(map[string]any), + } + } + + if workersVal, ok := clusterVal["workers"].(map[string]any); ok { + if _, exists := workersVal["nodes"]; !exists { + workersVal["nodes"] = make(map[string]any) + } + } else { + clusterVal["workers"] = map[string]any{ + "nodes": make(map[string]any), + } + } + return result, nil } diff --git a/pkg/config/config_handler_private_test.go b/pkg/context/config/config_handler_private_test.go similarity index 99% rename from pkg/config/config_handler_private_test.go rename to pkg/context/config/config_handler_private_test.go index 734eed548..125f607c2 100644 --- a/pkg/config/config_handler_private_test.go +++ b/pkg/context/config/config_handler_private_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/config/config_handler_public_test.go b/pkg/context/config/config_handler_public_test.go similarity index 99% rename from pkg/config/config_handler_public_test.go rename to pkg/context/config/config_handler_public_test.go index 454a07a44..99e2d7233 100644 --- a/pkg/config/config_handler_public_test.go +++ b/pkg/context/config/config_handler_public_test.go @@ -7,7 +7,7 @@ import ( "github.com/windsorcli/cli/api/v1alpha1" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/config/defaults.go b/pkg/context/config/defaults.go similarity index 100% rename from pkg/config/defaults.go rename to pkg/context/config/defaults.go diff --git a/pkg/config/defaults_test.go b/pkg/context/config/defaults_test.go similarity index 100% rename from pkg/config/defaults_test.go rename to pkg/context/config/defaults_test.go diff --git a/pkg/config/mock_config_handler.go b/pkg/context/config/mock_config_handler.go similarity index 100% rename from pkg/config/mock_config_handler.go rename to pkg/context/config/mock_config_handler.go diff --git a/pkg/config/mock_config_handler_test.go b/pkg/context/config/mock_config_handler_test.go similarity index 100% rename from pkg/config/mock_config_handler_test.go rename to pkg/context/config/mock_config_handler_test.go diff --git a/pkg/config/schema_validator.go b/pkg/context/config/schema_validator.go similarity index 99% rename from pkg/config/schema_validator.go rename to pkg/context/config/schema_validator.go index 291a4630a..d86ae51b9 100644 --- a/pkg/config/schema_validator.go +++ b/pkg/context/config/schema_validator.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/goccy/go-yaml" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The SchemaValidator is a JSON Schema validation component for Windsor blueprints. diff --git a/pkg/config/schema_validator_test.go b/pkg/context/config/schema_validator_test.go similarity index 99% rename from pkg/config/schema_validator_test.go rename to pkg/context/config/schema_validator_test.go index 78f539e64..5ed9cfe61 100644 --- a/pkg/config/schema_validator_test.go +++ b/pkg/context/config/schema_validator_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The SchemaValidatorTest is a comprehensive test suite for the SchemaValidator component. diff --git a/pkg/config/shims.go b/pkg/context/config/shims.go similarity index 100% rename from pkg/config/shims.go rename to pkg/context/config/shims.go diff --git a/pkg/context/context.go b/pkg/context/context.go new file mode 100644 index 000000000..d64dc0ce7 --- /dev/null +++ b/pkg/context/context.go @@ -0,0 +1,364 @@ +package context + +import ( + "fmt" + "maps" + + "github.com/windsorcli/cli/pkg/context/config" + envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/secrets" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/context/tools" + "github.com/windsorcli/cli/pkg/di" +) + +// ExecutionContext holds common execution values and core dependencies used across the Windsor CLI. +// These fields are set during various initialization steps rather than computed on-demand. +// Includes secret providers for Sops and 1Password, enabling access to secrets across all contexts. +// Also includes environment printers, tools manager, and environment variable/alias storage. +type ExecutionContext struct { + // ContextName is the current context name + ContextName string + + // ProjectRoot is the project root directory path + ProjectRoot string + + // ConfigRoot is the config root directory (/contexts/) + ConfigRoot string + + // TemplateRoot is the template directory (/contexts/_template) + TemplateRoot string + + // Injector is the dependency injector + Injector di.Injector + + // Core dependencies + ConfigHandler config.ConfigHandler + Shell shell.Shell + + // SecretsProviders contains providers for Sops and 1Password secrets management + SecretsProviders struct { + Sops secrets.SecretsProvider + Onepassword secrets.SecretsProvider + } + + // EnvPrinters contains environment printers for various providers and tools + EnvPrinters struct { + AwsEnv envvars.EnvPrinter + AzureEnv envvars.EnvPrinter + DockerEnv envvars.EnvPrinter + KubeEnv envvars.EnvPrinter + TalosEnv envvars.EnvPrinter + TerraformEnv envvars.EnvPrinter + WindsorEnv envvars.EnvPrinter + } + + // ToolsManager manages tool installation and configuration + ToolsManager tools.ToolsManager + + // envVars stores collected environment variables + envVars map[string]string + + // aliases stores collected shell aliases + aliases map[string]string +} + +// ============================================================================= +// Constructor +// ============================================================================= + +// NewContext creates a new ExecutionContext with ConfigHandler and Shell initialized if not already present. +// This is the base constructor that ensures core dependencies are available. +// If ConfigHandler is nil, it creates one using the Injector and initializes it. +// If Shell is nil, it creates one using the Injector and initializes it. +// Both are registered in the Injector for use by other components. +// The context also initializes envVars and aliases maps. +// Returns the ExecutionContext with initialized dependencies or an error if initialization fails. +func NewContext(ctx *ExecutionContext) (*ExecutionContext, error) { + if ctx == nil { + return nil, fmt.Errorf("execution context is required") + } + if ctx.Injector == nil { + return nil, fmt.Errorf("injector is required") + } + injector := ctx.Injector + + if ctx.ConfigHandler == nil { + if existing := injector.Resolve("configHandler"); existing != nil { + if configHandler, ok := existing.(config.ConfigHandler); ok { + ctx.ConfigHandler = configHandler + } else { + ctx.ConfigHandler = config.NewConfigHandler(injector) + injector.Register("configHandler", ctx.ConfigHandler) + } + } else { + ctx.ConfigHandler = config.NewConfigHandler(injector) + injector.Register("configHandler", ctx.ConfigHandler) + } + + if err := ctx.ConfigHandler.Initialize(); err != nil { + return nil, fmt.Errorf("failed to initialize config handler: %w", err) + } + } + + if ctx.Shell == nil { + if existing := injector.Resolve("shell"); existing != nil { + if shellInstance, ok := existing.(shell.Shell); ok { + ctx.Shell = shellInstance + } else { + shellInstance := shell.NewDefaultShell(injector) + ctx.Shell = shellInstance + injector.Register("shell", shellInstance) + } + } else { + shellInstance := shell.NewDefaultShell(injector) + ctx.Shell = shellInstance + injector.Register("shell", shellInstance) + } + + if err := ctx.Shell.Initialize(); err != nil { + return nil, fmt.Errorf("failed to initialize shell: %w", err) + } + } + + if ctx.envVars == nil { + ctx.envVars = make(map[string]string) + } + if ctx.aliases == nil { + ctx.aliases = make(map[string]string) + } + + return ctx, nil +} + +// ============================================================================= +// Environment Public Methods +// ============================================================================= + +// LoadEnvironment loads environment variables and aliases from all configured environment printers. +// It initializes all necessary components, optionally loads secrets if requested, and aggregates +// all environment variables and aliases into the ExecutionContext instance. Returns an error if any required +// dependency is missing or if any step fails. This method expects the ConfigHandler to be set before invocation. +func (ctx *ExecutionContext) LoadEnvironment(decrypt bool) error { + if ctx.ConfigHandler == nil { + return fmt.Errorf("config handler not loaded") + } + + ctx.initializeEnvPrinters() + ctx.initializeToolsManager() + ctx.initializeSecretsProviders() + + if err := ctx.initializeComponents(); err != nil { + return fmt.Errorf("failed to initialize environment components: %w", err) + } + + if decrypt { + if err := ctx.loadSecrets(); err != nil { + return fmt.Errorf("failed to load secrets: %w", err) + } + } + + allEnvVars := make(map[string]string) + allAliases := make(map[string]string) + + for _, printer := range ctx.getAllEnvPrinters() { + if printer != nil { + envVars, err := printer.GetEnvVars() + if err != nil { + return fmt.Errorf("error getting environment variables: %w", err) + } + maps.Copy(allEnvVars, envVars) + + aliases, err := printer.GetAlias() + if err != nil { + return fmt.Errorf("error getting aliases: %w", err) + } + maps.Copy(allAliases, aliases) + } + } + + ctx.envVars = allEnvVars + ctx.aliases = allAliases + + return nil +} + +// PrintEnvVars returns all collected environment variables in key=value format. +// If no environment variables are loaded, returns an empty string. +func (ctx *ExecutionContext) PrintEnvVars() string { + if len(ctx.envVars) > 0 { + return ctx.Shell.RenderEnvVars(ctx.envVars, false) + } + return "" +} + +// PrintEnvVarsExport returns all collected environment variables in export key=value format. +// If no environment variables are loaded, returns an empty string. +func (ctx *ExecutionContext) PrintEnvVarsExport() string { + if len(ctx.envVars) > 0 { + return ctx.Shell.RenderEnvVars(ctx.envVars, true) + } + return "" +} + +// PrintAliases returns all collected aliases using the shell's RenderAliases method. +// If no aliases are loaded, returns an empty string. +func (ctx *ExecutionContext) PrintAliases() string { + if len(ctx.aliases) > 0 { + return ctx.Shell.RenderAliases(ctx.aliases) + } + return "" +} + +// ExecutePostEnvHooks executes post-environment hooks for all environment printers. +func (ctx *ExecutionContext) ExecutePostEnvHooks() error { + var firstError error + + for _, printer := range ctx.getAllEnvPrinters() { + if printer != nil { + if err := printer.PostEnvHook(); err != nil && firstError == nil { + firstError = err + } + } + } + + if firstError != nil { + return fmt.Errorf("failed to execute post env hooks: %w", firstError) + } + + return nil +} + +// GetEnvVars returns a copy of the collected environment variables. +func (ctx *ExecutionContext) GetEnvVars() map[string]string { + result := make(map[string]string) + maps.Copy(result, ctx.envVars) + return result +} + +// GetAliases returns a copy of the collected aliases. +func (ctx *ExecutionContext) GetAliases() map[string]string { + result := make(map[string]string) + maps.Copy(result, ctx.aliases) + return result +} + +// ============================================================================= +// Environment Private Methods +// ============================================================================= + +// initializeEnvPrinters initializes environment printers based on configuration settings. +// It creates and registers the appropriate environment printers with the dependency injector +// based on the current configuration state. +func (ctx *ExecutionContext) initializeEnvPrinters() { + if ctx.EnvPrinters.AwsEnv == nil && ctx.ConfigHandler.GetBool("aws.enabled", false) { + ctx.EnvPrinters.AwsEnv = envvars.NewAwsEnvPrinter(ctx.Injector) + ctx.Injector.Register("awsEnv", ctx.EnvPrinters.AwsEnv) + } + if ctx.EnvPrinters.AzureEnv == nil && ctx.ConfigHandler.GetBool("azure.enabled", false) { + ctx.EnvPrinters.AzureEnv = envvars.NewAzureEnvPrinter(ctx.Injector) + ctx.Injector.Register("azureEnv", ctx.EnvPrinters.AzureEnv) + } + if ctx.EnvPrinters.DockerEnv == nil && ctx.ConfigHandler.GetBool("docker.enabled", false) { + ctx.EnvPrinters.DockerEnv = envvars.NewDockerEnvPrinter(ctx.Injector) + ctx.Injector.Register("dockerEnv", ctx.EnvPrinters.DockerEnv) + } + if ctx.EnvPrinters.KubeEnv == nil && ctx.ConfigHandler.GetBool("cluster.enabled", false) { + ctx.EnvPrinters.KubeEnv = envvars.NewKubeEnvPrinter(ctx.Injector) + ctx.Injector.Register("kubeEnv", ctx.EnvPrinters.KubeEnv) + } + if ctx.EnvPrinters.TalosEnv == nil && + (ctx.ConfigHandler.GetString("cluster.driver", "") == "talos" || + ctx.ConfigHandler.GetString("cluster.driver", "") == "omni") { + ctx.EnvPrinters.TalosEnv = envvars.NewTalosEnvPrinter(ctx.Injector) + ctx.Injector.Register("talosEnv", ctx.EnvPrinters.TalosEnv) + } + if ctx.EnvPrinters.TerraformEnv == nil && ctx.ConfigHandler.GetBool("terraform.enabled", false) { + ctx.EnvPrinters.TerraformEnv = envvars.NewTerraformEnvPrinter(ctx.Injector) + ctx.Injector.Register("terraformEnv", ctx.EnvPrinters.TerraformEnv) + } + if ctx.EnvPrinters.WindsorEnv == nil { + ctx.EnvPrinters.WindsorEnv = envvars.NewWindsorEnvPrinter(ctx.Injector) + ctx.Injector.Register("windsorEnv", ctx.EnvPrinters.WindsorEnv) + } +} + +// initializeToolsManager initializes the tools manager if not already set. +// It creates a new ToolsManager instance and registers it with the dependency injector. +func (ctx *ExecutionContext) initializeToolsManager() { + if ctx.ToolsManager == nil { + ctx.ToolsManager = tools.NewToolsManager(ctx.Injector) + ctx.Injector.Register("toolsManager", ctx.ToolsManager) + } +} + +// initializeSecretsProviders initializes and registers secrets providers with the dependency injector +// based on current configuration settings. The method sets up the SOPS provider if enabled with the +// context's config root path, and sets up the 1Password provider if enabled, using a mock in test +// scenarios. Providers are only initialized if not already present on the context. +func (ctx *ExecutionContext) initializeSecretsProviders() { + if ctx.SecretsProviders.Sops == nil && ctx.ConfigHandler.GetBool("secrets.sops.enabled", false) { + configPath := ctx.ConfigRoot + ctx.SecretsProviders.Sops = secrets.NewSopsSecretsProvider(configPath, ctx.Injector) + ctx.Injector.Register("sopsSecretsProvider", ctx.SecretsProviders.Sops) + } + + if ctx.SecretsProviders.Onepassword == nil && ctx.ConfigHandler.GetBool("secrets.onepassword.enabled", false) { + ctx.SecretsProviders.Onepassword = secrets.NewMockSecretsProvider(ctx.Injector) + ctx.Injector.Register("onepasswordSecretsProvider", ctx.SecretsProviders.Onepassword) + } +} + +// getAllEnvPrinters returns all environment printers in a consistent order. +// This ensures that environment variables are processed in a predictable sequence +// with WindsorEnv being processed last to take precedence. +func (ctx *ExecutionContext) getAllEnvPrinters() []envvars.EnvPrinter { + return []envvars.EnvPrinter{ + ctx.EnvPrinters.AwsEnv, + ctx.EnvPrinters.AzureEnv, + ctx.EnvPrinters.DockerEnv, + ctx.EnvPrinters.KubeEnv, + ctx.EnvPrinters.TalosEnv, + ctx.EnvPrinters.TerraformEnv, + ctx.EnvPrinters.WindsorEnv, + } +} + +// initializeComponents initializes all environment-related components required after setup. +// This includes initializing the tools manager (if present) and all configured environment printers. +// Each component's Initialize method is called if the component is non-nil. +// Returns an error if any initialization fails, otherwise returns nil. +func (ctx *ExecutionContext) initializeComponents() error { + if ctx.ToolsManager != nil { + if err := ctx.ToolsManager.Initialize(); err != nil { + return fmt.Errorf("failed to initialize tools manager: %w", err) + } + } + for _, printer := range ctx.getAllEnvPrinters() { + if printer != nil { + if err := printer.Initialize(); err != nil { + return fmt.Errorf("failed to initialize environment printer: %w", err) + } + } + } + return nil +} + +// loadSecrets loads secrets from configured secrets providers. +// It attempts to load secrets from both SOPS and 1Password providers if they are available. +func (ctx *ExecutionContext) loadSecrets() error { + providers := []secrets.SecretsProvider{ + ctx.SecretsProviders.Sops, + ctx.SecretsProviders.Onepassword, + } + + for _, provider := range providers { + if provider != nil { + if err := provider.LoadSecrets(); err != nil { + return fmt.Errorf("failed to load secrets: %w", err) + } + } + } + + return nil +} diff --git a/pkg/environment/environment_test.go b/pkg/context/context_test.go similarity index 72% rename from pkg/environment/environment_test.go rename to pkg/context/context_test.go index 39678d5d7..5e4e2f281 100644 --- a/pkg/environment/environment_test.go +++ b/pkg/context/context_test.go @@ -1,22 +1,21 @@ -package environment +package context import ( "errors" "strings" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" + "github.com/windsorcli/cli/pkg/context/secrets" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/secrets" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/types" ) // ============================================================================= // Test Setup // ============================================================================= -// setupEnvironmentMocks creates mock components for testing the Environment +// setupEnvironmentMocks creates mock components for testing the ExecutionContext func setupEnvironmentMocks(t *testing.T) *Mocks { t.Helper() @@ -80,7 +79,7 @@ func setupEnvironmentMocks(t *testing.T) *Mocks { injector.Register("contextName", "test-context") // Create execution context - execCtx := &types.ExecutionContext{ + execCtx := &ExecutionContext{ ContextName: "test-context", ProjectRoot: "/test/project", ConfigRoot: "/test/project/contexts/test-context", @@ -90,58 +89,58 @@ func setupEnvironmentMocks(t *testing.T) *Mocks { Shell: shell, } - // Create environment execution context - envCtx := &EnvironmentExecutionContext{ - ExecutionContext: *execCtx, + ctx, err := NewContext(execCtx) + if err != nil { + t.Fatalf("Failed to create context: %v", err) } return &Mocks{ - Injector: injector, - ConfigHandler: configHandler, - Shell: shell, - EnvironmentExecutionContext: envCtx, + Injector: injector, + ConfigHandler: configHandler, + Shell: shell, + ExecutionContext: ctx, } } // Mocks contains all the mock dependencies for testing type Mocks struct { - Injector di.Injector - ConfigHandler config.ConfigHandler - Shell shell.Shell - EnvironmentExecutionContext *EnvironmentExecutionContext + Injector di.Injector + ConfigHandler config.ConfigHandler + Shell shell.Shell + ExecutionContext *ExecutionContext } // ============================================================================= // Test Constructor // ============================================================================= -func TestNewEnvironment(t *testing.T) { - t.Run("CreatesEnvironmentWithDependencies", func(t *testing.T) { +func TestNewContext(t *testing.T) { + t.Run("CreatesContextWithDependencies", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext - if env == nil { - t.Fatal("Expected environment to be created") + if ctx == nil { + t.Fatal("Expected context to be created") } - if env.Injector != mocks.Injector { + if ctx.Injector != mocks.Injector { t.Error("Expected injector to be set") } - if env.Shell != mocks.Shell { + if ctx.Shell != mocks.Shell { t.Error("Expected shell to be set") } - if env.ConfigHandler != mocks.ConfigHandler { + if ctx.ConfigHandler != mocks.ConfigHandler { t.Error("Expected config handler to be set") } - if env.envVars == nil { + if ctx.envVars == nil { t.Error("Expected envVars map to be initialized") } - if env.aliases == nil { + if ctx.aliases == nil { t.Error("Expected aliases map to be initialized") } }) @@ -151,37 +150,37 @@ func TestNewEnvironment(t *testing.T) { // Test LoadEnvironment // ============================================================================= -func TestEnvironment_LoadEnvironment(t *testing.T) { +func TestExecutionContext_LoadEnvironment(t *testing.T) { t.Run("LoadsEnvironmentSuccessfully", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext - err := env.LoadEnvironment(false) + err := ctx.LoadEnvironment(false) - // The environment should load successfully with the default WindsorEnv printer + // The context should load successfully with the default WindsorEnv printer if err != nil { t.Fatalf("Expected no error, got: %v", err) } // Check that the WindsorEnv printer was initialized - if env.EnvPrinters.WindsorEnv == nil { + if ctx.EnvPrinters.WindsorEnv == nil { t.Error("Expected WindsorEnv printer to be initialized") } // Check that environment variables were loaded - if len(env.envVars) == 0 { + if len(ctx.envVars) == 0 { t.Error("Expected environment variables to be loaded") } }) t.Run("HandlesConfigHandlerNotLoaded", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set config handler to nil to test error handling - env.ConfigHandler = nil + ctx.ConfigHandler = nil - err := env.LoadEnvironment(false) + err := ctx.LoadEnvironment(false) if err == nil { t.Error("Expected error when config handler is not loaded") @@ -190,19 +189,19 @@ func TestEnvironment_LoadEnvironment(t *testing.T) { t.Run("HandlesEnvPrinterInitializationError", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext - // The WindsorEnv printer should be initialized by default - if env.EnvPrinters.WindsorEnv == nil { - t.Error("Expected WindsorEnv printer to be initialized") - } - - err := env.LoadEnvironment(false) + err := ctx.LoadEnvironment(false) // This should not error since the default WindsorEnv printer has working initialization if err != nil { t.Fatalf("Expected no error, got: %v", err) } + + // The WindsorEnv printer should be initialized after LoadEnvironment + if ctx.EnvPrinters.WindsorEnv == nil { + t.Error("Expected WindsorEnv printer to be initialized") + } }) } @@ -210,18 +209,18 @@ func TestEnvironment_LoadEnvironment(t *testing.T) { // Test PrintEnvVars // ============================================================================= -func TestEnvironment_PrintEnvVars(t *testing.T) { +func TestExecutionContext_PrintEnvVars(t *testing.T) { t.Run("PrintsEnvironmentVariables", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up environment variables - env.envVars = map[string]string{ + ctx.envVars = map[string]string{ "TEST_VAR1": "value1", "TEST_VAR2": "value2", } - output := env.PrintEnvVarsExport() + output := ctx.PrintEnvVarsExport() if output == "" { t.Error("Expected output to be generated") @@ -230,9 +229,9 @@ func TestEnvironment_PrintEnvVars(t *testing.T) { t.Run("HandlesEmptyEnvironmentVariables", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext - output := env.PrintEnvVars() + output := ctx.PrintEnvVars() if output != "" { t.Error("Expected no output for empty environment variables") @@ -245,18 +244,18 @@ func TestEnvironment_PrintEnvVars(t *testing.T) { // Test PrintAliases // ============================================================================= -func TestEnvironment_PrintAliases(t *testing.T) { +func TestExecutionContext_PrintAliases(t *testing.T) { t.Run("PrintsAliases", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up aliases - env.aliases = map[string]string{ + ctx.aliases = map[string]string{ "test1": "echo test1", "test2": "echo test2", } - output := env.PrintAliases() + output := ctx.PrintAliases() if output == "" { t.Error("Expected output to be generated") @@ -265,9 +264,9 @@ func TestEnvironment_PrintAliases(t *testing.T) { t.Run("HandlesEmptyAliases", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext - output := env.PrintAliases() + output := ctx.PrintAliases() if output != "" { t.Error("Expected no output for empty aliases") @@ -280,17 +279,20 @@ func TestEnvironment_PrintAliases(t *testing.T) { // Test ExecutePostEnvHooks // ============================================================================= -func TestEnvironment_ExecutePostEnvHooks(t *testing.T) { +func TestExecutionContext_ExecutePostEnvHooks(t *testing.T) { t.Run("ExecutesPostEnvHooksSuccessfully", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext + + // Initialize env printers first + ctx.initializeEnvPrinters() - // The WindsorEnv printer should be initialized by default - if env.EnvPrinters.WindsorEnv == nil { + // The WindsorEnv printer should be initialized after initializeEnvPrinters + if ctx.EnvPrinters.WindsorEnv == nil { t.Error("Expected WindsorEnv printer to be initialized") } - err := env.ExecutePostEnvHooks() + err := ctx.ExecutePostEnvHooks() if err != nil { t.Fatalf("Expected no error, got: %v", err) @@ -301,15 +303,18 @@ func TestEnvironment_ExecutePostEnvHooks(t *testing.T) { t.Run("HandlesPostEnvHookError", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext - // The WindsorEnv printer should be initialized by default - if env.EnvPrinters.WindsorEnv == nil { + // Initialize env printers first + ctx.initializeEnvPrinters() + + // The WindsorEnv printer should be initialized after initializeEnvPrinters + if ctx.EnvPrinters.WindsorEnv == nil { t.Error("Expected WindsorEnv printer to be initialized") } // Test that post env hooks work with the default printer - err := env.ExecutePostEnvHooks() + err := ctx.ExecutePostEnvHooks() // This should not error since the default WindsorEnv printer has a working PostEnvHook if err != nil { @@ -319,15 +324,18 @@ func TestEnvironment_ExecutePostEnvHooks(t *testing.T) { t.Run("IgnoresPostEnvHookErrorWhenNotVerbose", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext + + // Initialize env printers first + ctx.initializeEnvPrinters() - // The WindsorEnv printer should be initialized by default - if env.EnvPrinters.WindsorEnv == nil { + // The WindsorEnv printer should be initialized after initializeEnvPrinters + if ctx.EnvPrinters.WindsorEnv == nil { t.Error("Expected WindsorEnv printer to be initialized") } // Test that post env hooks work with the default printer - err := env.ExecutePostEnvHooks() + err := ctx.ExecutePostEnvHooks() // This should not error since the default WindsorEnv printer has a working PostEnvHook if err != nil { @@ -340,18 +348,18 @@ func TestEnvironment_ExecutePostEnvHooks(t *testing.T) { // Test Getter Methods // ============================================================================= -func TestEnvironment_GetEnvVars(t *testing.T) { +func TestExecutionContext_GetEnvVars(t *testing.T) { t.Run("ReturnsCopyOfEnvVars", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext original := map[string]string{ "TEST_VAR1": "value1", "TEST_VAR2": "value2", } - env.envVars = original + ctx.envVars = original - copy := env.GetEnvVars() + copy := ctx.GetEnvVars() if len(copy) != len(original) { t.Error("Expected copy to have same length as original") @@ -361,24 +369,24 @@ func TestEnvironment_GetEnvVars(t *testing.T) { copy["NEW_VAR"] = "new_value" // Original should be unchanged - if len(env.envVars) != len(original) { + if len(ctx.envVars) != len(original) { t.Error("Expected original to be unchanged") } }) } -func TestEnvironment_GetAliases(t *testing.T) { +func TestExecutionContext_GetAliases(t *testing.T) { t.Run("ReturnsCopyOfAliases", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext original := map[string]string{ "test1": "echo test1", "test2": "echo test2", } - env.aliases = original + ctx.aliases = original - copy := env.GetAliases() + copy := ctx.GetAliases() if len(copy) != len(original) { t.Error("Expected copy to have same length as original") @@ -388,7 +396,7 @@ func TestEnvironment_GetAliases(t *testing.T) { copy["new"] = "echo new" // Original should be unchanged - if len(env.aliases) != len(original) { + if len(ctx.aliases) != len(original) { t.Error("Expected original to be unchanged") } }) @@ -398,19 +406,19 @@ func TestEnvironment_GetAliases(t *testing.T) { // Test Private Methods // ============================================================================= -func TestEnvironment_loadSecrets(t *testing.T) { +func TestExecutionContext_loadSecrets(t *testing.T) { t.Run("LoadsSecretsSuccessfully", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up mock secrets providers mockSopsProvider := secrets.NewMockSecretsProvider(mocks.Injector) mockOnepasswordProvider := secrets.NewMockSecretsProvider(mocks.Injector) - env.SecretsProviders.Sops = mockSopsProvider - env.SecretsProviders.Onepassword = mockOnepasswordProvider + ctx.SecretsProviders.Sops = mockSopsProvider + ctx.SecretsProviders.Onepassword = mockOnepasswordProvider - err := env.loadSecrets() + err := ctx.loadSecrets() if err != nil { t.Fatalf("Expected no error, got: %v", err) } @@ -418,7 +426,7 @@ func TestEnvironment_loadSecrets(t *testing.T) { t.Run("HandlesSecretsProviderError", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up mock secrets provider that returns an error mockProvider := secrets.NewMockSecretsProvider(mocks.Injector) @@ -426,9 +434,9 @@ func TestEnvironment_loadSecrets(t *testing.T) { return errors.New("secrets load failed") } - env.SecretsProviders.Sops = mockProvider + ctx.SecretsProviders.Sops = mockProvider - err := env.loadSecrets() + err := ctx.loadSecrets() if err == nil { t.Fatal("Expected error, got nil") } @@ -440,13 +448,13 @@ func TestEnvironment_loadSecrets(t *testing.T) { t.Run("HandlesNilProviders", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Leave providers as nil - env.SecretsProviders.Sops = nil - env.SecretsProviders.Onepassword = nil + ctx.SecretsProviders.Sops = nil + ctx.SecretsProviders.Onepassword = nil - err := env.loadSecrets() + err := ctx.loadSecrets() if err != nil { t.Fatalf("Expected no error with nil providers, got: %v", err) } @@ -454,24 +462,24 @@ func TestEnvironment_loadSecrets(t *testing.T) { t.Run("HandlesMixedProviders", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up one provider that works and one that's nil mockProvider := secrets.NewMockSecretsProvider(mocks.Injector) - env.SecretsProviders.Sops = mockProvider - env.SecretsProviders.Onepassword = nil + ctx.SecretsProviders.Sops = mockProvider + ctx.SecretsProviders.Onepassword = nil - err := env.loadSecrets() + err := ctx.loadSecrets() if err != nil { t.Fatalf("Expected no error with mixed providers, got: %v", err) } }) } -func TestEnvironment_initializeSecretsProviders(t *testing.T) { +func TestExecutionContext_initializeSecretsProviders(t *testing.T) { t.Run("InitializesSopsProviderWhenEnabled", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Enable SOPS in config mockConfigHandler := mocks.ConfigHandler.(*config.MockConfigHandler) @@ -482,9 +490,9 @@ func TestEnvironment_initializeSecretsProviders(t *testing.T) { return false } - env.initializeSecretsProviders() + ctx.initializeSecretsProviders() - if env.SecretsProviders.Sops == nil { + if ctx.SecretsProviders.Sops == nil { t.Error("Expected SOPS provider to be initialized") } @@ -496,7 +504,7 @@ func TestEnvironment_initializeSecretsProviders(t *testing.T) { t.Run("InitializesOnepasswordProviderWhenEnabled", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Enable 1Password in config mockConfigHandler := mocks.ConfigHandler.(*config.MockConfigHandler) @@ -507,9 +515,9 @@ func TestEnvironment_initializeSecretsProviders(t *testing.T) { return false } - env.initializeSecretsProviders() + ctx.initializeSecretsProviders() - if env.SecretsProviders.Onepassword == nil { + if ctx.SecretsProviders.Onepassword == nil { t.Error("Expected 1Password provider to be initialized") } @@ -521,7 +529,7 @@ func TestEnvironment_initializeSecretsProviders(t *testing.T) { t.Run("SkipsProvidersWhenDisabled", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Disable both providers mockConfigHandler := mocks.ConfigHandler.(*config.MockConfigHandler) @@ -529,24 +537,24 @@ func TestEnvironment_initializeSecretsProviders(t *testing.T) { return false } - env.initializeSecretsProviders() + ctx.initializeSecretsProviders() - if env.SecretsProviders.Sops != nil { + if ctx.SecretsProviders.Sops != nil { t.Error("Expected SOPS provider to be nil when disabled") } - if env.SecretsProviders.Onepassword != nil { + if ctx.SecretsProviders.Onepassword != nil { t.Error("Expected 1Password provider to be nil when disabled") } }) t.Run("DoesNotOverrideExistingProviders", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Pre-set a provider existingProvider := secrets.NewMockSecretsProvider(mocks.Injector) - env.SecretsProviders.Sops = existingProvider + ctx.SecretsProviders.Sops = existingProvider // Enable SOPS in config mockConfigHandler := mocks.ConfigHandler.(*config.MockConfigHandler) @@ -557,28 +565,28 @@ func TestEnvironment_initializeSecretsProviders(t *testing.T) { return false } - env.initializeSecretsProviders() + ctx.initializeSecretsProviders() // Should still be the same provider - if env.SecretsProviders.Sops != existingProvider { + if ctx.SecretsProviders.Sops != existingProvider { t.Error("Expected existing provider to be preserved") } }) } -func TestEnvironment_LoadEnvironment_WithSecrets(t *testing.T) { +func TestExecutionContext_LoadEnvironment_WithSecrets(t *testing.T) { t.Run("LoadsEnvironmentWithSecretsSuccessfully", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up mock secrets providers mockSopsProvider := secrets.NewMockSecretsProvider(mocks.Injector) mockOnepasswordProvider := secrets.NewMockSecretsProvider(mocks.Injector) - env.SecretsProviders.Sops = mockSopsProvider - env.SecretsProviders.Onepassword = mockOnepasswordProvider + ctx.SecretsProviders.Sops = mockSopsProvider + ctx.SecretsProviders.Onepassword = mockOnepasswordProvider - err := env.LoadEnvironment(true) // Enable secrets loading + err := ctx.LoadEnvironment(true) // Enable secrets loading if err != nil { t.Fatalf("Expected no error, got: %v", err) } @@ -586,7 +594,7 @@ func TestEnvironment_LoadEnvironment_WithSecrets(t *testing.T) { t.Run("HandlesSecretsLoadError", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up mock secrets provider that returns an error mockProvider := secrets.NewMockSecretsProvider(mocks.Injector) @@ -594,9 +602,9 @@ func TestEnvironment_LoadEnvironment_WithSecrets(t *testing.T) { return errors.New("secrets load failed") } - env.SecretsProviders.Sops = mockProvider + ctx.SecretsProviders.Sops = mockProvider - err := env.LoadEnvironment(true) // Enable secrets loading + err := ctx.LoadEnvironment(true) // Enable secrets loading if err == nil { t.Fatal("Expected error, got nil") } @@ -607,14 +615,14 @@ func TestEnvironment_LoadEnvironment_WithSecrets(t *testing.T) { }) } -func TestEnvironment_PrintEnvVars_EdgeCases(t *testing.T) { +func TestExecutionContext_PrintEnvVars_EdgeCases(t *testing.T) { t.Run("HandlesNilShell", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) - env.Shell = nil + ctx := mocks.ExecutionContext + ctx.Shell = nil // This should not panic - result := env.PrintEnvVars() + result := ctx.PrintEnvVars() if result != "" { t.Errorf("Expected empty string with nil shell, got: %s", result) } @@ -622,24 +630,24 @@ func TestEnvironment_PrintEnvVars_EdgeCases(t *testing.T) { t.Run("HandlesEmptyEnvVars", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) - env.envVars = make(map[string]string) + ctx := mocks.ExecutionContext + ctx.envVars = make(map[string]string) - result := env.PrintEnvVars() + result := ctx.PrintEnvVars() if result != "" { t.Errorf("Expected empty string with empty env vars, got: %s", result) } }) } -func TestEnvironment_PrintEnvVarsExport_EdgeCases(t *testing.T) { +func TestExecutionContext_PrintEnvVarsExport_EdgeCases(t *testing.T) { t.Run("HandlesNilShell", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) - env.Shell = nil + ctx := mocks.ExecutionContext + ctx.Shell = nil // This should not panic - result := env.PrintEnvVarsExport() + result := ctx.PrintEnvVarsExport() if result != "" { t.Errorf("Expected empty string with nil shell, got: %s", result) } @@ -647,24 +655,24 @@ func TestEnvironment_PrintEnvVarsExport_EdgeCases(t *testing.T) { t.Run("HandlesEmptyEnvVars", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) - env.envVars = make(map[string]string) + ctx := mocks.ExecutionContext + ctx.envVars = make(map[string]string) - result := env.PrintEnvVarsExport() + result := ctx.PrintEnvVarsExport() if result != "" { t.Errorf("Expected empty string with empty env vars, got: %s", result) } }) } -func TestEnvironment_PrintAliases_EdgeCases(t *testing.T) { +func TestExecutionContext_PrintAliases_EdgeCases(t *testing.T) { t.Run("HandlesNilShell", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) - env.Shell = nil + ctx := mocks.ExecutionContext + ctx.Shell = nil // This should not panic - result := env.PrintAliases() + result := ctx.PrintAliases() if result != "" { t.Errorf("Expected empty string with nil shell, got: %s", result) } @@ -672,29 +680,29 @@ func TestEnvironment_PrintAliases_EdgeCases(t *testing.T) { t.Run("HandlesEmptyAliases", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) - env.aliases = make(map[string]string) + ctx := mocks.ExecutionContext + ctx.aliases = make(map[string]string) - result := env.PrintAliases() + result := ctx.PrintAliases() if result != "" { t.Errorf("Expected empty string with empty aliases, got: %s", result) } }) } -func TestEnvironment_initializeComponents_EdgeCases(t *testing.T) { +func TestExecutionContext_initializeComponents_EdgeCases(t *testing.T) { t.Run("HandlesToolsManagerInitializationError", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up a mock tools manager that returns an error mockToolsManager := &MockToolsManager{} mockToolsManager.InitializeFunc = func() error { return errors.New("tools manager init failed") } - env.ToolsManager = mockToolsManager + ctx.ToolsManager = mockToolsManager - err := env.initializeComponents() + err := ctx.initializeComponents() if err == nil { t.Fatal("Expected error, got nil") } @@ -706,16 +714,16 @@ func TestEnvironment_initializeComponents_EdgeCases(t *testing.T) { t.Run("HandlesEnvPrinterInitializationError", func(t *testing.T) { mocks := setupEnvironmentMocks(t) - env := NewEnvironment(mocks.EnvironmentExecutionContext) + ctx := mocks.ExecutionContext // Set up a mock env printer that returns an error mockPrinter := &MockEnvPrinter{} mockPrinter.InitializeFunc = func() error { return errors.New("env printer init failed") } - env.EnvPrinters.WindsorEnv = mockPrinter + ctx.EnvPrinters.WindsorEnv = mockPrinter - err := env.initializeComponents() + err := ctx.initializeComponents() if err == nil { t.Fatal("Expected error, got nil") } diff --git a/pkg/environment/envvars/aws_env.go b/pkg/context/env/aws_env.go similarity index 100% rename from pkg/environment/envvars/aws_env.go rename to pkg/context/env/aws_env.go diff --git a/pkg/environment/envvars/aws_env_test.go b/pkg/context/env/aws_env_test.go similarity index 99% rename from pkg/environment/envvars/aws_env_test.go rename to pkg/context/env/aws_env_test.go index 72ee0a94b..23a81ab89 100644 --- a/pkg/environment/envvars/aws_env_test.go +++ b/pkg/context/env/aws_env_test.go @@ -10,7 +10,7 @@ import ( "github.com/goccy/go-yaml" "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" ) // ============================================================================= diff --git a/pkg/environment/envvars/azure_env.go b/pkg/context/env/azure_env.go similarity index 100% rename from pkg/environment/envvars/azure_env.go rename to pkg/context/env/azure_env.go diff --git a/pkg/environment/envvars/azure_env_test.go b/pkg/context/env/azure_env_test.go similarity index 98% rename from pkg/environment/envvars/azure_env_test.go rename to pkg/context/env/azure_env_test.go index 762ed96ac..59d0c785a 100644 --- a/pkg/environment/envvars/azure_env_test.go +++ b/pkg/context/env/azure_env_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" ) // ============================================================================= diff --git a/pkg/environment/envvars/docker_env.go b/pkg/context/env/docker_env.go similarity index 100% rename from pkg/environment/envvars/docker_env.go rename to pkg/context/env/docker_env.go diff --git a/pkg/environment/envvars/docker_env_test.go b/pkg/context/env/docker_env_test.go similarity index 99% rename from pkg/environment/envvars/docker_env_test.go rename to pkg/context/env/docker_env_test.go index adbe6996f..d17ce4733 100644 --- a/pkg/environment/envvars/docker_env_test.go +++ b/pkg/context/env/docker_env_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/environment/envvars/env.go b/pkg/context/env/env.go similarity index 97% rename from pkg/environment/envvars/env.go rename to pkg/context/env/env.go index 86833cba1..6da3d3729 100644 --- a/pkg/environment/envvars/env.go +++ b/pkg/context/env/env.go @@ -9,9 +9,9 @@ import ( "fmt" "slices" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/environment/envvars/env_test.go b/pkg/context/env/env_test.go similarity index 99% rename from pkg/environment/envvars/env_test.go rename to pkg/context/env/env_test.go index 8be0a5d1f..8f5efda0e 100644 --- a/pkg/environment/envvars/env_test.go +++ b/pkg/context/env/env_test.go @@ -5,9 +5,9 @@ import ( "reflect" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/environment/envvars/kube_env.go b/pkg/context/env/kube_env.go similarity index 100% rename from pkg/environment/envvars/kube_env.go rename to pkg/context/env/kube_env.go diff --git a/pkg/environment/envvars/kube_env_test.go b/pkg/context/env/kube_env_test.go similarity index 99% rename from pkg/environment/envvars/kube_env_test.go rename to pkg/context/env/kube_env_test.go index 8c2f34fe5..2c0e54c4e 100644 --- a/pkg/environment/envvars/kube_env_test.go +++ b/pkg/context/env/kube_env_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/pkg/environment/envvars/mock_env.go b/pkg/context/env/mock_env.go similarity index 100% rename from pkg/environment/envvars/mock_env.go rename to pkg/context/env/mock_env.go diff --git a/pkg/environment/envvars/mock_env_test.go b/pkg/context/env/mock_env_test.go similarity index 100% rename from pkg/environment/envvars/mock_env_test.go rename to pkg/context/env/mock_env_test.go diff --git a/pkg/environment/envvars/shims.go b/pkg/context/env/shims.go similarity index 100% rename from pkg/environment/envvars/shims.go rename to pkg/context/env/shims.go diff --git a/pkg/environment/envvars/shims_test.go b/pkg/context/env/shims_test.go similarity index 100% rename from pkg/environment/envvars/shims_test.go rename to pkg/context/env/shims_test.go diff --git a/pkg/environment/envvars/talos_env.go b/pkg/context/env/talos_env.go similarity index 100% rename from pkg/environment/envvars/talos_env.go rename to pkg/context/env/talos_env.go diff --git a/pkg/environment/envvars/talos_env_test.go b/pkg/context/env/talos_env_test.go similarity index 99% rename from pkg/environment/envvars/talos_env_test.go rename to pkg/context/env/talos_env_test.go index 328fde89f..e469f1919 100644 --- a/pkg/environment/envvars/talos_env_test.go +++ b/pkg/context/env/talos_env_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" ) // ============================================================================= diff --git a/pkg/environment/envvars/terraform_env.go b/pkg/context/env/terraform_env.go similarity index 100% rename from pkg/environment/envvars/terraform_env.go rename to pkg/context/env/terraform_env.go diff --git a/pkg/environment/envvars/terraform_env_test.go b/pkg/context/env/terraform_env_test.go similarity index 99% rename from pkg/environment/envvars/terraform_env_test.go rename to pkg/context/env/terraform_env_test.go index b4ff426d8..37624897a 100644 --- a/pkg/environment/envvars/terraform_env_test.go +++ b/pkg/context/env/terraform_env_test.go @@ -10,7 +10,7 @@ import ( "testing" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/resources/blueprint" ) diff --git a/pkg/environment/envvars/windsor_env.go b/pkg/context/env/windsor_env.go similarity index 99% rename from pkg/environment/envvars/windsor_env.go rename to pkg/context/env/windsor_env.go index 54ed7f68c..6c07702eb 100644 --- a/pkg/environment/envvars/windsor_env.go +++ b/pkg/context/env/windsor_env.go @@ -12,8 +12,8 @@ import ( "regexp" "strings" + "github.com/windsorcli/cli/pkg/context/secrets" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/secrets" ) // ============================================================================= diff --git a/pkg/environment/envvars/windsor_env_test.go b/pkg/context/env/windsor_env_test.go similarity index 99% rename from pkg/environment/envvars/windsor_env_test.go rename to pkg/context/env/windsor_env_test.go index 298e126bc..bbe4fd472 100644 --- a/pkg/environment/envvars/windsor_env_test.go +++ b/pkg/context/env/windsor_env_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/secrets" + "github.com/windsorcli/cli/pkg/context/secrets" ) // ============================================================================= diff --git a/pkg/secrets/mock_secrets_provider.go b/pkg/context/secrets/mock_secrets_provider.go similarity index 100% rename from pkg/secrets/mock_secrets_provider.go rename to pkg/context/secrets/mock_secrets_provider.go diff --git a/pkg/secrets/mock_secrets_provider_test.go b/pkg/context/secrets/mock_secrets_provider_test.go similarity index 100% rename from pkg/secrets/mock_secrets_provider_test.go rename to pkg/context/secrets/mock_secrets_provider_test.go diff --git a/pkg/secrets/op_cli_secrets_provider.go b/pkg/context/secrets/op_cli_secrets_provider.go similarity index 100% rename from pkg/secrets/op_cli_secrets_provider.go rename to pkg/context/secrets/op_cli_secrets_provider.go diff --git a/pkg/secrets/op_cli_secrets_provider_test.go b/pkg/context/secrets/op_cli_secrets_provider_test.go similarity index 100% rename from pkg/secrets/op_cli_secrets_provider_test.go rename to pkg/context/secrets/op_cli_secrets_provider_test.go diff --git a/pkg/secrets/op_sdk_secrets_provider.go b/pkg/context/secrets/op_sdk_secrets_provider.go similarity index 100% rename from pkg/secrets/op_sdk_secrets_provider.go rename to pkg/context/secrets/op_sdk_secrets_provider.go diff --git a/pkg/secrets/op_sdk_secrets_provider_test.go b/pkg/context/secrets/op_sdk_secrets_provider_test.go similarity index 100% rename from pkg/secrets/op_sdk_secrets_provider_test.go rename to pkg/context/secrets/op_sdk_secrets_provider_test.go diff --git a/pkg/secrets/secrets_provider.go b/pkg/context/secrets/secrets_provider.go similarity index 99% rename from pkg/secrets/secrets_provider.go rename to pkg/context/secrets/secrets_provider.go index 0ee812a46..b9dc5aaec 100644 --- a/pkg/secrets/secrets_provider.go +++ b/pkg/context/secrets/secrets_provider.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The SecretsProvider is a core interface for secrets management diff --git a/pkg/secrets/secrets_provider_test.go b/pkg/context/secrets/secrets_provider_test.go similarity index 99% rename from pkg/secrets/secrets_provider_test.go rename to pkg/context/secrets/secrets_provider_test.go index 74e0a1112..c8d82cba5 100644 --- a/pkg/secrets/secrets_provider_test.go +++ b/pkg/context/secrets/secrets_provider_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The SecretsProviderTest is a test suite for the SecretsProvider interface diff --git a/pkg/secrets/shims.go b/pkg/context/secrets/shims.go similarity index 100% rename from pkg/secrets/shims.go rename to pkg/context/secrets/shims.go diff --git a/pkg/secrets/shims_test.go b/pkg/context/secrets/shims_test.go similarity index 100% rename from pkg/secrets/shims_test.go rename to pkg/context/secrets/shims_test.go diff --git a/pkg/secrets/sops_secrets_provider.go b/pkg/context/secrets/sops_secrets_provider.go similarity index 100% rename from pkg/secrets/sops_secrets_provider.go rename to pkg/context/secrets/sops_secrets_provider.go diff --git a/pkg/secrets/sops_secrets_provider_test.go b/pkg/context/secrets/sops_secrets_provider_test.go similarity index 100% rename from pkg/secrets/sops_secrets_provider_test.go rename to pkg/context/secrets/sops_secrets_provider_test.go diff --git a/pkg/shell/hooks.go b/pkg/context/shell/hooks.go similarity index 100% rename from pkg/shell/hooks.go rename to pkg/context/shell/hooks.go diff --git a/pkg/shell/mock_shell.go b/pkg/context/shell/mock_shell.go similarity index 100% rename from pkg/shell/mock_shell.go rename to pkg/context/shell/mock_shell.go diff --git a/pkg/shell/mock_shell_test.go b/pkg/context/shell/mock_shell_test.go similarity index 100% rename from pkg/shell/mock_shell_test.go rename to pkg/context/shell/mock_shell_test.go diff --git a/pkg/shell/scrubbing_writer.go b/pkg/context/shell/scrubbing_writer.go similarity index 100% rename from pkg/shell/scrubbing_writer.go rename to pkg/context/shell/scrubbing_writer.go diff --git a/pkg/shell/secure_shell.go b/pkg/context/shell/secure_shell.go similarity index 98% rename from pkg/shell/secure_shell.go rename to pkg/context/shell/secure_shell.go index bbd73e638..c92997d2b 100644 --- a/pkg/shell/secure_shell.go +++ b/pkg/context/shell/secure_shell.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell/ssh" + "github.com/windsorcli/cli/pkg/context/shell/ssh" ) // The SecureShell is a secure implementation of the Shell interface using SSH. diff --git a/pkg/shell/secure_shell_test.go b/pkg/context/shell/secure_shell_test.go similarity index 99% rename from pkg/shell/secure_shell_test.go rename to pkg/context/shell/secure_shell_test.go index 69fd74bdd..f7cde86dc 100644 --- a/pkg/shell/secure_shell_test.go +++ b/pkg/context/shell/secure_shell_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell/ssh" + "github.com/windsorcli/cli/pkg/context/shell/ssh" ) // The SecureShellTest is a test suite for the SecureShell implementation. diff --git a/pkg/shell/shell.go b/pkg/context/shell/shell.go similarity index 100% rename from pkg/shell/shell.go rename to pkg/context/shell/shell.go diff --git a/pkg/shell/shell_test.go b/pkg/context/shell/shell_test.go similarity index 100% rename from pkg/shell/shell_test.go rename to pkg/context/shell/shell_test.go diff --git a/pkg/shell/shims.go b/pkg/context/shell/shims.go similarity index 100% rename from pkg/shell/shims.go rename to pkg/context/shell/shims.go diff --git a/pkg/shell/shims_test.go b/pkg/context/shell/shims_test.go similarity index 100% rename from pkg/shell/shims_test.go rename to pkg/context/shell/shims_test.go diff --git a/pkg/shell/ssh/client.go b/pkg/context/shell/ssh/client.go similarity index 100% rename from pkg/shell/ssh/client.go rename to pkg/context/shell/ssh/client.go diff --git a/pkg/shell/ssh/client_test.go b/pkg/context/shell/ssh/client_test.go similarity index 100% rename from pkg/shell/ssh/client_test.go rename to pkg/context/shell/ssh/client_test.go diff --git a/pkg/shell/ssh/mock_client.go b/pkg/context/shell/ssh/mock_client.go similarity index 100% rename from pkg/shell/ssh/mock_client.go rename to pkg/context/shell/ssh/mock_client.go diff --git a/pkg/shell/ssh/mock_client_test.go b/pkg/context/shell/ssh/mock_client_test.go similarity index 100% rename from pkg/shell/ssh/mock_client_test.go rename to pkg/context/shell/ssh/mock_client_test.go diff --git a/pkg/shell/ssh/real_client.go b/pkg/context/shell/ssh/real_client.go similarity index 100% rename from pkg/shell/ssh/real_client.go rename to pkg/context/shell/ssh/real_client.go diff --git a/pkg/shell/ssh/shims.go b/pkg/context/shell/ssh/shims.go similarity index 100% rename from pkg/shell/ssh/shims.go rename to pkg/context/shell/ssh/shims.go diff --git a/pkg/shell/unix_shell.go b/pkg/context/shell/unix_shell.go similarity index 100% rename from pkg/shell/unix_shell.go rename to pkg/context/shell/unix_shell.go diff --git a/pkg/shell/unix_shell_test.go b/pkg/context/shell/unix_shell_test.go similarity index 100% rename from pkg/shell/unix_shell_test.go rename to pkg/context/shell/unix_shell_test.go diff --git a/pkg/shell/windows_shell.go b/pkg/context/shell/windows_shell.go similarity index 100% rename from pkg/shell/windows_shell.go rename to pkg/context/shell/windows_shell.go diff --git a/pkg/shell/windows_shell_test.go b/pkg/context/shell/windows_shell_test.go similarity index 100% rename from pkg/shell/windows_shell_test.go rename to pkg/context/shell/windows_shell_test.go diff --git a/pkg/environment/tools/mock_tools_manager.go b/pkg/context/tools/mock_tools_manager.go similarity index 100% rename from pkg/environment/tools/mock_tools_manager.go rename to pkg/context/tools/mock_tools_manager.go diff --git a/pkg/environment/tools/mock_tools_manager_test.go b/pkg/context/tools/mock_tools_manager_test.go similarity index 100% rename from pkg/environment/tools/mock_tools_manager_test.go rename to pkg/context/tools/mock_tools_manager_test.go diff --git a/pkg/environment/tools/shims.go b/pkg/context/tools/shims.go similarity index 100% rename from pkg/environment/tools/shims.go rename to pkg/context/tools/shims.go diff --git a/pkg/environment/tools/tools_manager.go b/pkg/context/tools/tools_manager.go similarity index 98% rename from pkg/environment/tools/tools_manager.go rename to pkg/context/tools/tools_manager.go index 3eb7e78ce..89479484a 100644 --- a/pkg/environment/tools/tools_manager.go +++ b/pkg/context/tools/tools_manager.go @@ -10,11 +10,11 @@ import ( "time" "github.com/briandowns/spinner" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" - sh "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" + sh "github.com/windsorcli/cli/pkg/context/shell" ) // The ToolsManager is a core component that manages development tools and dependencies diff --git a/pkg/environment/tools/tools_manager_test.go b/pkg/context/tools/tools_manager_test.go similarity index 99% rename from pkg/environment/tools/tools_manager_test.go rename to pkg/context/tools/tools_manager_test.go index 2370765aa..621c8bacf 100644 --- a/pkg/environment/tools/tools_manager_test.go +++ b/pkg/context/tools/tools_manager_test.go @@ -7,10 +7,10 @@ import ( "strings" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" - sh "github.com/windsorcli/cli/pkg/shell" + sh "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/environment/environment.go b/pkg/environment/environment.go deleted file mode 100644 index ba47fbe57..000000000 --- a/pkg/environment/environment.go +++ /dev/null @@ -1,296 +0,0 @@ -// The Environment package provides a top-level interface for environment management functionality. -// It consolidates environment variable and alias management from the runtime and environment packages, -// providing a unified API for loading, printing, and managing environment state across the Windsor CLI. -// The Environment acts as the primary interface for all environment-related operations, -// coordinating between different environment printers and providing consistent behavior. - -package environment - -import ( - "fmt" - "maps" - - "github.com/windsorcli/cli/pkg/environment/envvars" - "github.com/windsorcli/cli/pkg/environment/tools" - "github.com/windsorcli/cli/pkg/secrets" - "github.com/windsorcli/cli/pkg/types" -) - -// ============================================================================= -// Types -// ============================================================================= - -// EnvironmentExecutionContext holds the execution context for environment operations. -// It embeds the base ExecutionContext and includes all environment-specific dependencies. -type EnvironmentExecutionContext struct { - types.ExecutionContext - - EnvPrinters struct { - AwsEnv envvars.EnvPrinter - AzureEnv envvars.EnvPrinter - DockerEnv envvars.EnvPrinter - KubeEnv envvars.EnvPrinter - TalosEnv envvars.EnvPrinter - TerraformEnv envvars.EnvPrinter - WindsorEnv envvars.EnvPrinter - } - ToolsManager tools.ToolsManager -} - -// Environment manages environment variables and aliases across the Windsor CLI. -// It provides a unified interface for loading, printing, and managing environment state -// by coordinating between different environment printers and handling secrets decryption. -type Environment struct { - *EnvironmentExecutionContext - envVars map[string]string - aliases map[string]string -} - -// ============================================================================= -// Constructor -// ============================================================================= - -// NewEnvironment creates a new Environment instance with the given execution context. -// The constructor sets up all environment printers, the tools manager, and secrets providers based on current configuration. -// It returns an Environment object that will initialize components when LoadEnvironment is called. -func NewEnvironment(ctx *EnvironmentExecutionContext) *Environment { - env := &Environment{ - EnvironmentExecutionContext: ctx, - envVars: make(map[string]string), - aliases: make(map[string]string), - } - - env.initializeEnvPrinters() - env.initializeToolsManager() - env.initializeSecretsProviders() - - return env -} - -// ============================================================================= -// Public Methods -// ============================================================================= - -// LoadEnvironment loads environment variables and aliases from all configured environment printers. -// It initializes all necessary components, optionally loads secrets if requested, and aggregates -// all environment variables and aliases into the Environment instance. Returns an error if any required -// dependency is missing or if any step fails. This method expects the ConfigHandler to be set before invocation. -func (e *Environment) LoadEnvironment(decrypt bool) error { - if e.ConfigHandler == nil { - return fmt.Errorf("config handler not loaded") - } - - if err := e.initializeComponents(); err != nil { - return fmt.Errorf("failed to initialize environment components: %w", err) - } - - if decrypt { - if err := e.loadSecrets(); err != nil { - return fmt.Errorf("failed to load secrets: %w", err) - } - } - - allEnvVars := make(map[string]string) - allAliases := make(map[string]string) - - for _, printer := range e.getAllEnvPrinters() { - if printer != nil { - envVars, err := printer.GetEnvVars() - if err != nil { - return fmt.Errorf("error getting environment variables: %w", err) - } - maps.Copy(allEnvVars, envVars) - - aliases, err := printer.GetAlias() - if err != nil { - return fmt.Errorf("error getting aliases: %w", err) - } - maps.Copy(allAliases, aliases) - } - } - - e.envVars = allEnvVars - e.aliases = allAliases - - return nil -} - -// PrintEnvVars returns all collected environment variables in key=value format. -// If no environment variables are loaded, returns an empty string. -func (e *Environment) PrintEnvVars() string { - if len(e.envVars) > 0 { - return e.Shell.RenderEnvVars(e.envVars, false) - } - return "" -} - -// PrintEnvVarsExport returns all collected environment variables in export key=value format. -// If no environment variables are loaded, returns an empty string. -func (e *Environment) PrintEnvVarsExport() string { - if len(e.envVars) > 0 { - return e.Shell.RenderEnvVars(e.envVars, true) - } - return "" -} - -// PrintAliases returns all collected aliases using the shell's RenderAliases method. -// If no aliases are loaded, returns an empty string. -func (e *Environment) PrintAliases() string { - if len(e.aliases) > 0 { - return e.Shell.RenderAliases(e.aliases) - } - return "" -} - -// ExecutePostEnvHooks executes post-environment hooks for all environment printers. -func (e *Environment) ExecutePostEnvHooks() error { - var firstError error - - for _, printer := range e.getAllEnvPrinters() { - if printer != nil { - if err := printer.PostEnvHook(); err != nil && firstError == nil { - firstError = err - } - } - } - - if firstError != nil { - return fmt.Errorf("failed to execute post env hooks: %w", firstError) - } - - return nil -} - -// GetEnvVars returns a copy of the collected environment variables. -func (e *Environment) GetEnvVars() map[string]string { - result := make(map[string]string) - maps.Copy(result, e.envVars) - return result -} - -// GetAliases returns a copy of the collected aliases. -func (e *Environment) GetAliases() map[string]string { - result := make(map[string]string) - maps.Copy(result, e.aliases) - return result -} - -// ============================================================================= -// Private Methods -// ============================================================================= - -// initializeEnvPrinters initializes environment printers based on configuration settings. -// It creates and registers the appropriate environment printers with the dependency injector -// based on the current configuration state. -func (e *Environment) initializeEnvPrinters() { - if e.EnvPrinters.AwsEnv == nil && e.ConfigHandler.GetBool("aws.enabled", false) { - e.EnvPrinters.AwsEnv = envvars.NewAwsEnvPrinter(e.Injector) - e.Injector.Register("awsEnv", e.EnvPrinters.AwsEnv) - } - if e.EnvPrinters.AzureEnv == nil && e.ConfigHandler.GetBool("azure.enabled", false) { - e.EnvPrinters.AzureEnv = envvars.NewAzureEnvPrinter(e.Injector) - e.Injector.Register("azureEnv", e.EnvPrinters.AzureEnv) - } - if e.EnvPrinters.DockerEnv == nil && e.ConfigHandler.GetBool("docker.enabled", false) { - e.EnvPrinters.DockerEnv = envvars.NewDockerEnvPrinter(e.Injector) - e.Injector.Register("dockerEnv", e.EnvPrinters.DockerEnv) - } - if e.EnvPrinters.KubeEnv == nil && e.ConfigHandler.GetBool("cluster.enabled", false) { - e.EnvPrinters.KubeEnv = envvars.NewKubeEnvPrinter(e.Injector) - e.Injector.Register("kubeEnv", e.EnvPrinters.KubeEnv) - } - if e.EnvPrinters.TalosEnv == nil && - (e.ConfigHandler.GetString("cluster.driver", "") == "talos" || - e.ConfigHandler.GetString("cluster.driver", "") == "omni") { - e.EnvPrinters.TalosEnv = envvars.NewTalosEnvPrinter(e.Injector) - e.Injector.Register("talosEnv", e.EnvPrinters.TalosEnv) - } - if e.EnvPrinters.TerraformEnv == nil && e.ConfigHandler.GetBool("terraform.enabled", false) { - e.EnvPrinters.TerraformEnv = envvars.NewTerraformEnvPrinter(e.Injector) - e.Injector.Register("terraformEnv", e.EnvPrinters.TerraformEnv) - } - if e.EnvPrinters.WindsorEnv == nil { - e.EnvPrinters.WindsorEnv = envvars.NewWindsorEnvPrinter(e.Injector) - e.Injector.Register("windsorEnv", e.EnvPrinters.WindsorEnv) - } -} - -// initializeToolsManager initializes the tools manager if not already set. -// It creates a new ToolsManager instance and registers it with the dependency injector. -func (e *Environment) initializeToolsManager() { - if e.ToolsManager == nil { - e.ToolsManager = tools.NewToolsManager(e.Injector) - e.Injector.Register("toolsManager", e.ToolsManager) - } -} - -// initializeSecretsProviders initializes and registers secrets providers with the dependency injector -// based on current configuration settings. The method sets up the SOPS provider if enabled with the -// environment's config root path, and sets up the 1Password provider if enabled, using a mock in test -// scenarios. Providers are only initialized if not already present on the environment. -func (e *Environment) initializeSecretsProviders() { - if e.SecretsProviders.Sops == nil && e.ConfigHandler.GetBool("secrets.sops.enabled", false) { - configPath := e.ConfigRoot - e.SecretsProviders.Sops = secrets.NewSopsSecretsProvider(configPath, e.Injector) - e.Injector.Register("sopsSecretsProvider", e.SecretsProviders.Sops) - } - - if e.SecretsProviders.Onepassword == nil && e.ConfigHandler.GetBool("secrets.onepassword.enabled", false) { - e.SecretsProviders.Onepassword = secrets.NewMockSecretsProvider(e.Injector) - e.Injector.Register("onepasswordSecretsProvider", e.SecretsProviders.Onepassword) - } -} - -// getAllEnvPrinters returns all environment printers in a consistent order. -// This ensures that environment variables are processed in a predictable sequence -// with WindsorEnv being processed last to take precedence. -func (e *Environment) getAllEnvPrinters() []envvars.EnvPrinter { - return []envvars.EnvPrinter{ - e.EnvPrinters.AwsEnv, - e.EnvPrinters.AzureEnv, - e.EnvPrinters.DockerEnv, - e.EnvPrinters.KubeEnv, - e.EnvPrinters.TalosEnv, - e.EnvPrinters.TerraformEnv, - e.EnvPrinters.WindsorEnv, - } -} - -// initializeComponents initializes all environment-related components required after setup. -// This includes initializing the tools manager (if present) and all configured environment printers. -// Each component's Initialize method is called if the component is non-nil. -// Returns an error if any initialization fails, otherwise returns nil. -func (e *Environment) initializeComponents() error { - if e.ToolsManager != nil { - if err := e.ToolsManager.Initialize(); err != nil { - return fmt.Errorf("failed to initialize tools manager: %w", err) - } - } - for _, printer := range e.getAllEnvPrinters() { - if printer != nil { - if err := printer.Initialize(); err != nil { - return fmt.Errorf("failed to initialize environment printer: %w", err) - } - } - } - return nil -} - -// loadSecrets loads secrets from configured secrets providers. -// It attempts to load secrets from both SOPS and 1Password providers if they are available. -func (e *Environment) loadSecrets() error { - providers := []secrets.SecretsProvider{ - e.SecretsProviders.Sops, - e.SecretsProviders.Onepassword, - } - - for _, provider := range providers { - if provider != nil { - if err := provider.LoadSecrets(); err != nil { - return fmt.Errorf("failed to load secrets: %w", err) - } - } - } - - return nil -} diff --git a/pkg/generators/generator.go b/pkg/generators/generator.go index 166912b68..27d083845 100644 --- a/pkg/generators/generator.go +++ b/pkg/generators/generator.go @@ -3,11 +3,11 @@ package generators import ( "fmt" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" bundler "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The Generator is a core component that provides a unified interface for code generation. diff --git a/pkg/generators/generator_test.go b/pkg/generators/generator_test.go index bd5621e10..38cda89a3 100644 --- a/pkg/generators/generator_test.go +++ b/pkg/generators/generator_test.go @@ -9,11 +9,11 @@ import ( "testing" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" bundler "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/generators/terraform_generator_test.go b/pkg/generators/terraform_generator_test.go index adcfc292b..0571ab77a 100644 --- a/pkg/generators/terraform_generator_test.go +++ b/pkg/generators/terraform_generator_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/hcl/v2/hclwrite" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" bundler "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/zclconf/go-cty/cty" ) diff --git a/pkg/infrastructure/infrastructure.go b/pkg/infrastructure/infrastructure.go index ad1f01d58..4eec6591f 100644 --- a/pkg/infrastructure/infrastructure.go +++ b/pkg/infrastructure/infrastructure.go @@ -5,10 +5,10 @@ import ( blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" "github.com/windsorcli/cli/pkg/constants" + "github.com/windsorcli/cli/pkg/context" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" - "github.com/windsorcli/cli/pkg/types" ) // The Infrastructure package provides high-level infrastructure management functionality @@ -24,7 +24,7 @@ import ( // InfrastructureExecutionContext holds the execution context for infrastructure operations. // It embeds the base ExecutionContext and includes all infrastructure-specific dependencies. type InfrastructureExecutionContext struct { - types.ExecutionContext + context.ExecutionContext TerraformStack terraforminfra.Stack KubernetesManager kubernetes.KubernetesManager diff --git a/pkg/infrastructure/infrastructure_test.go b/pkg/infrastructure/infrastructure_test.go index f45dc257c..2960a644d 100644 --- a/pkg/infrastructure/infrastructure_test.go +++ b/pkg/infrastructure/infrastructure_test.go @@ -6,13 +6,13 @@ import ( "testing" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" + "github.com/windsorcli/cli/pkg/context" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/types" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= @@ -89,7 +89,7 @@ func setupInfrastructureMocks(t *testing.T) *Mocks { kubernetesClient := kubernetes.NewMockKubernetesClient() clusterClient := cluster.NewMockClusterClient() - execCtx := &types.ExecutionContext{ + execCtx := &context.ExecutionContext{ ContextName: "test-context", ProjectRoot: "/test/project", ConfigRoot: "/test/project/contexts/test-context", @@ -669,7 +669,7 @@ func TestInfrastructure_Close(t *testing.T) { func TestInfrastructureExecutionContext(t *testing.T) { t.Run("CreatesInfrastructureExecutionContext", func(t *testing.T) { - execCtx := &types.ExecutionContext{ + execCtx := &context.ExecutionContext{ ContextName: "test-context", ProjectRoot: "/test/project", ConfigRoot: "/test/project/contexts/test-context", diff --git a/pkg/infrastructure/terraform/stack.go b/pkg/infrastructure/terraform/stack.go index eaf23555f..bc86ca4db 100644 --- a/pkg/infrastructure/terraform/stack.go +++ b/pkg/infrastructure/terraform/stack.go @@ -16,10 +16,10 @@ import ( "strings" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" + envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/envvars" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" ) // ============================================================================= diff --git a/pkg/infrastructure/terraform/stack_test.go b/pkg/infrastructure/terraform/stack_test.go index acd8b9a05..d3367955a 100644 --- a/pkg/infrastructure/terraform/stack_test.go +++ b/pkg/infrastructure/terraform/stack_test.go @@ -13,11 +13,11 @@ import ( "testing" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/envvars" + envvars "github.com/windsorcli/cli/pkg/context/env" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/pipelines/build_id_test.go b/pkg/pipelines/build_id_test.go index a42f7462f..2732d9f9e 100644 --- a/pkg/pipelines/build_id_test.go +++ b/pkg/pipelines/build_id_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/pipelines/check.go b/pkg/pipelines/check.go index a28da3fb2..46e564072 100644 --- a/pkg/pipelines/check.go +++ b/pkg/pipelines/check.go @@ -6,7 +6,7 @@ import ( "time" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/tools" + "github.com/windsorcli/cli/pkg/context/tools" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" ) diff --git a/pkg/pipelines/check_test.go b/pkg/pipelines/check_test.go index c01cf0e86..39b22bc74 100644 --- a/pkg/pipelines/check_test.go +++ b/pkg/pipelines/check_test.go @@ -8,12 +8,12 @@ import ( "testing" "time" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/tools" + "github.com/windsorcli/cli/pkg/context/tools" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // mockFileInfo implements os.FileInfo for testing diff --git a/pkg/pipelines/down.go b/pkg/pipelines/down.go index 9166e12d6..5eb845db1 100644 --- a/pkg/pipelines/down.go +++ b/pkg/pipelines/down.go @@ -7,11 +7,11 @@ import ( "path/filepath" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/envvars" + envvars "github.com/windsorcli/cli/pkg/context/env" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/virt" ) diff --git a/pkg/pipelines/down_test.go b/pkg/pipelines/down_test.go index 2968aa620..61a1fa131 100644 --- a/pkg/pipelines/down_test.go +++ b/pkg/pipelines/down_test.go @@ -8,12 +8,12 @@ import ( "testing" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" - "github.com/windsorcli/cli/pkg/environment/envvars" + "github.com/windsorcli/cli/pkg/context/config" + envvars "github.com/windsorcli/cli/pkg/context/env" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/virt" ) diff --git a/pkg/pipelines/exec_test.go b/pkg/pipelines/exec_test.go index ab2d57e1c..31e8a5472 100644 --- a/pkg/pipelines/exec_test.go +++ b/pkg/pipelines/exec_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" ) // ============================================================================= diff --git a/pkg/pipelines/init.go b/pkg/pipelines/init.go index 54526f47e..ba8e62553 100644 --- a/pkg/pipelines/init.go +++ b/pkg/pipelines/init.go @@ -8,17 +8,17 @@ import ( "runtime" "strings" - "github.com/windsorcli/cli/pkg/config" "github.com/windsorcli/cli/pkg/constants" + "github.com/windsorcli/cli/pkg/context/config" + envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/context/tools" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/envvars" - "github.com/windsorcli/cli/pkg/environment/tools" "github.com/windsorcli/cli/pkg/generators" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" "github.com/windsorcli/cli/pkg/resources/terraform" - "github.com/windsorcli/cli/pkg/shell" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/services" "github.com/windsorcli/cli/pkg/workstation/virt" diff --git a/pkg/pipelines/init_test.go b/pkg/pipelines/init_test.go index 7158ee913..9c015e29d 100644 --- a/pkg/pipelines/init_test.go +++ b/pkg/pipelines/init_test.go @@ -9,14 +9,14 @@ import ( "time" "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/tools" + "github.com/windsorcli/cli/pkg/context/tools" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/virt" ) diff --git a/pkg/pipelines/install_test.go b/pkg/pipelines/install_test.go index 8d6cc371f..9ac314644 100644 --- a/pkg/pipelines/install_test.go +++ b/pkg/pipelines/install_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" diff --git a/pkg/pipelines/pipeline.go b/pkg/pipelines/pipeline.go index 9fa5819ea..0149cd37b 100644 --- a/pkg/pipelines/pipeline.go +++ b/pkg/pipelines/pipeline.go @@ -7,11 +7,11 @@ import ( "path/filepath" secretsConfigType "github.com/windsorcli/cli/api/v1alpha1/secrets" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" - envpkg "github.com/windsorcli/cli/pkg/environment/envvars" - "github.com/windsorcli/cli/pkg/environment/tools" + envpkg "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/tools" "github.com/windsorcli/cli/pkg/generators" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" @@ -19,9 +19,9 @@ import ( "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" "github.com/windsorcli/cli/pkg/resources/terraform" - "github.com/windsorcli/cli/pkg/secrets" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/shell/ssh" + "github.com/windsorcli/cli/pkg/context/secrets" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/context/shell/ssh" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/services" "github.com/windsorcli/cli/pkg/workstation/virt" diff --git a/pkg/pipelines/pipeline_test.go b/pkg/pipelines/pipeline_test.go index 5a59dabb1..8540a74d9 100644 --- a/pkg/pipelines/pipeline_test.go +++ b/pkg/pipelines/pipeline_test.go @@ -12,16 +12,16 @@ import ( "github.com/windsorcli/cli/api/v1alpha1" "github.com/windsorcli/cli/api/v1alpha1/docker" secretsConfigType "github.com/windsorcli/cli/api/v1alpha1/secrets" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/envvars" - "github.com/windsorcli/cli/pkg/environment/tools" + envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/tools" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/virt" ) diff --git a/pkg/pipelines/up.go b/pkg/pipelines/up.go index 7571c20e3..bd10e9747 100644 --- a/pkg/pipelines/up.go +++ b/pkg/pipelines/up.go @@ -6,10 +6,10 @@ import ( "os" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/envvars" - "github.com/windsorcli/cli/pkg/environment/tools" + envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/tools" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/virt" ) diff --git a/pkg/pipelines/up_test.go b/pkg/pipelines/up_test.go index 2537ab48d..bef85b992 100644 --- a/pkg/pipelines/up_test.go +++ b/pkg/pipelines/up_test.go @@ -6,11 +6,11 @@ import ( "testing" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" - "github.com/windsorcli/cli/pkg/environment/envvars" - "github.com/windsorcli/cli/pkg/environment/tools" + "github.com/windsorcli/cli/pkg/context/config" + envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/tools" terraforminfra "github.com/windsorcli/cli/pkg/infrastructure/terraform" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/virt" ) diff --git a/pkg/resources/artifact/artifact.go b/pkg/resources/artifact/artifact.go index e11e4f98a..05a543a2c 100644 --- a/pkg/resources/artifact/artifact.go +++ b/pkg/resources/artifact/artifact.go @@ -19,7 +19,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/static" "github.com/google/go-containerregistry/pkg/v1/types" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The ArtifactBuilder creates tar.gz artifacts from prepared build directories. diff --git a/pkg/resources/artifact/artifact_test.go b/pkg/resources/artifact/artifact_test.go index 28afbfe60..a4c44f324 100644 --- a/pkg/resources/artifact/artifact_test.go +++ b/pkg/resources/artifact/artifact_test.go @@ -18,7 +18,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/types" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/resources/blueprint/blueprint_handler.go b/pkg/resources/blueprint/blueprint_handler.go index 632d1ad63..63d228e0a 100644 --- a/pkg/resources/blueprint/blueprint_handler.go +++ b/pkg/resources/blueprint/blueprint_handler.go @@ -18,12 +18,12 @@ import ( _ "embed" "github.com/goccy/go-yaml" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" "github.com/windsorcli/cli/pkg/resources/artifact" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/briandowns/spinner" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" diff --git a/pkg/resources/blueprint/blueprint_handler_helper_test.go b/pkg/resources/blueprint/blueprint_handler_helper_test.go index ac168212f..591c43017 100644 --- a/pkg/resources/blueprint/blueprint_handler_helper_test.go +++ b/pkg/resources/blueprint/blueprint_handler_helper_test.go @@ -7,7 +7,7 @@ import ( "time" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/pkg/resources/blueprint/blueprint_handler_private_test.go b/pkg/resources/blueprint/blueprint_handler_private_test.go index 6309cb84f..5b288857e 100644 --- a/pkg/resources/blueprint/blueprint_handler_private_test.go +++ b/pkg/resources/blueprint/blueprint_handler_private_test.go @@ -10,10 +10,10 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/pkg/resources/blueprint/blueprint_handler_public_test.go b/pkg/resources/blueprint/blueprint_handler_public_test.go index c498e0077..8b430c728 100644 --- a/pkg/resources/blueprint/blueprint_handler_public_test.go +++ b/pkg/resources/blueprint/blueprint_handler_public_test.go @@ -15,12 +15,12 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" "github.com/goccy/go-yaml" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" "github.com/windsorcli/cli/pkg/resources/artifact" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/pkg/resources/blueprint/feature_evaluator.go b/pkg/resources/blueprint/feature_evaluator.go index a4c14d268..6e76325ad 100644 --- a/pkg/resources/blueprint/feature_evaluator.go +++ b/pkg/resources/blueprint/feature_evaluator.go @@ -9,10 +9,10 @@ import ( "github.com/expr-lang/expr" "github.com/google/go-jsonnet" "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // FeatureEvaluator provides lightweight expression evaluation for blueprint feature conditions. @@ -315,6 +315,29 @@ func (e *FeatureEvaluator) buildExprEnvironment(config map[string]any, featurePa }, new(func(string) string), ), + expr.Function( + "split", + func(params ...any) (any, error) { + if len(params) != 2 { + return nil, fmt.Errorf("split() requires exactly 2 arguments, got %d", len(params)) + } + str, ok := params[0].(string) + if !ok { + return nil, fmt.Errorf("split() first argument must be a string, got %T", params[0]) + } + sep, ok := params[1].(string) + if !ok { + return nil, fmt.Errorf("split() second argument must be a string, got %T", params[1]) + } + parts := strings.Split(str, sep) + result := make([]any, len(parts)) + for i, part := range parts { + result[i] = part + } + return result, nil + }, + new(func(string, string) []any), + ), } } diff --git a/pkg/resources/blueprint/feature_evaluator_test.go b/pkg/resources/blueprint/feature_evaluator_test.go index a22452dbc..9f841b52c 100644 --- a/pkg/resources/blueprint/feature_evaluator_test.go +++ b/pkg/resources/blueprint/feature_evaluator_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go index 701a0c299..ee9dfbd49 100644 --- a/pkg/resources/resources.go +++ b/pkg/resources/resources.go @@ -3,10 +3,10 @@ package resources import ( "fmt" + "github.com/windsorcli/cli/pkg/context" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" "github.com/windsorcli/cli/pkg/resources/terraform" - "github.com/windsorcli/cli/pkg/types" ) // The Resources package provides high-level resource management functionality @@ -21,7 +21,7 @@ import ( // ResourcesExecutionContext holds the execution context for resource operations. // It embeds the base ExecutionContext and includes all resource-specific dependencies. type ResourcesExecutionContext struct { - types.ExecutionContext + context.ExecutionContext // Resource-specific dependencies ArtifactBuilder artifact.Artifact diff --git a/pkg/resources/resources_test.go b/pkg/resources/resources_test.go index 520a787bf..71ad940ec 100644 --- a/pkg/resources/resources_test.go +++ b/pkg/resources/resources_test.go @@ -3,10 +3,10 @@ package resources import ( "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" + "github.com/windsorcli/cli/pkg/context" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/types" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= @@ -22,7 +22,7 @@ func setupResourcesMocks(t *testing.T) *Mocks { shell := shell.NewMockShell() // Create execution context - execCtx := &types.ExecutionContext{ + execCtx := &context.ExecutionContext{ ContextName: "test-context", ProjectRoot: "/test/project", ConfigRoot: "/test/project/contexts/test-context", @@ -185,7 +185,7 @@ func TestResources_Generate(t *testing.T) { func TestResourcesExecutionContext(t *testing.T) { t.Run("CreatesResourcesExecutionContext", func(t *testing.T) { - execCtx := &types.ExecutionContext{ + execCtx := &context.ExecutionContext{ ContextName: "test-context", ProjectRoot: "/test/project", ConfigRoot: "/test/project/contexts/test-context", diff --git a/pkg/resources/terraform/module_resolver.go b/pkg/resources/terraform/module_resolver.go index 1a0d0fa5c..0b2a43453 100644 --- a/pkg/resources/terraform/module_resolver.go +++ b/pkg/resources/terraform/module_resolver.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/hcl/v2/hclwrite" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/zclconf/go-cty/cty" ) diff --git a/pkg/resources/terraform/module_resolver_test.go b/pkg/resources/terraform/module_resolver_test.go index 800edbe34..41581c55d 100644 --- a/pkg/resources/terraform/module_resolver_test.go +++ b/pkg/resources/terraform/module_resolver_test.go @@ -12,11 +12,11 @@ import ( "testing" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/resources/terraform/oci_module_resolver_test.go b/pkg/resources/terraform/oci_module_resolver_test.go index ef8a50cad..f32b2ca5f 100644 --- a/pkg/resources/terraform/oci_module_resolver_test.go +++ b/pkg/resources/terraform/oci_module_resolver_test.go @@ -11,7 +11,7 @@ import ( blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/resources/artifact" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // MockTarReader provides a mock implementation for TarReader interface diff --git a/pkg/resources/terraform/standard_module_resolver.go b/pkg/resources/terraform/standard_module_resolver.go index 798c861d1..c7579075f 100644 --- a/pkg/resources/terraform/standard_module_resolver.go +++ b/pkg/resources/terraform/standard_module_resolver.go @@ -5,10 +5,10 @@ import ( "path/filepath" "strings" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The StandardModuleResolver is a terraform module resolver for standard source types. diff --git a/pkg/resources/terraform/standard_module_resolver_test.go b/pkg/resources/terraform/standard_module_resolver_test.go index c8c134b3f..6cd2d6f61 100644 --- a/pkg/resources/terraform/standard_module_resolver_test.go +++ b/pkg/resources/terraform/standard_module_resolver_test.go @@ -9,10 +9,10 @@ import ( "encoding/json" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The StandardModuleResolverTest is a test suite for the StandardModuleResolver implementation diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 781266be9..2ab957d31 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -5,20 +5,20 @@ import ( "maps" "os" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context" + "github.com/windsorcli/cli/pkg/context/config" + envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/secrets" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/context/shell/ssh" + "github.com/windsorcli/cli/pkg/context/tools" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/environment/envvars" - "github.com/windsorcli/cli/pkg/environment/tools" "github.com/windsorcli/cli/pkg/generators" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" "github.com/windsorcli/cli/pkg/resources/artifact" "github.com/windsorcli/cli/pkg/resources/blueprint" "github.com/windsorcli/cli/pkg/resources/terraform" - "github.com/windsorcli/cli/pkg/secrets" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/shell/ssh" - "github.com/windsorcli/cli/pkg/types" "github.com/windsorcli/cli/pkg/workstation" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/services" @@ -442,19 +442,17 @@ func (r *Runtime) createWorkstation() (*workstation.Workstation, error) { return nil, fmt.Errorf("failed to get project root: %w", err) } - execCtx := &types.ExecutionContext{ + execCtx := &context.ExecutionContext{ ContextName: contextName, ProjectRoot: projectRoot, ConfigRoot: fmt.Sprintf("%s/contexts/%s", projectRoot, contextName), TemplateRoot: fmt.Sprintf("%s/contexts/_template", projectRoot), ConfigHandler: r.ConfigHandler, Shell: r.Shell, + Injector: r.Injector, } - workstationCtx := &workstation.WorkstationExecutionContext{ - ExecutionContext: *execCtx, - } - + workstationCtx := workstation.NewWorkstationExecutionContext(execCtx) ws, err := workstation.NewWorkstation(workstationCtx, r.Injector) if err != nil { return nil, fmt.Errorf("failed to create workstation: %w", err) diff --git a/pkg/runtime/runtime_loaders.go b/pkg/runtime/runtime_loaders.go index d8f84a11d..d6c2369d5 100644 --- a/pkg/runtime/runtime_loaders.go +++ b/pkg/runtime/runtime_loaders.go @@ -7,13 +7,13 @@ import ( "path/filepath" secretsConfigType "github.com/windsorcli/cli/api/v1alpha1/secrets" - "github.com/windsorcli/cli/pkg/config" - "github.com/windsorcli/cli/pkg/environment/envvars" + "github.com/windsorcli/cli/pkg/context/config" + envvars "github.com/windsorcli/cli/pkg/context/env" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" "github.com/windsorcli/cli/pkg/resources/blueprint" - "github.com/windsorcli/cli/pkg/secrets" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/secrets" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/runtime/runtime_loaders_test.go b/pkg/runtime/runtime_loaders_test.go index d89f30aa9..8ef6b84b0 100644 --- a/pkg/runtime/runtime_loaders_test.go +++ b/pkg/runtime/runtime_loaders_test.go @@ -7,11 +7,11 @@ import ( "testing" secretsConfigType "github.com/windsorcli/cli/api/v1alpha1/secrets" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/infrastructure/cluster" "github.com/windsorcli/cli/pkg/infrastructure/kubernetes" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The RuntimeLoadersTest is a test suite for the Runtime loader methods. diff --git a/pkg/runtime/runtime_test.go b/pkg/runtime/runtime_test.go index 2c4a5346c..5aef98690 100644 --- a/pkg/runtime/runtime_test.go +++ b/pkg/runtime/runtime_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" - "github.com/windsorcli/cli/pkg/config" - "github.com/windsorcli/cli/pkg/environment/envvars" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/config" + envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/shell" ) // The RuntimeTest is a test suite for the Runtime struct and its chaining methods. diff --git a/pkg/types/context.go b/pkg/types/context.go deleted file mode 100644 index eafc841c3..000000000 --- a/pkg/types/context.go +++ /dev/null @@ -1,38 +0,0 @@ -package types - -import ( - "github.com/windsorcli/cli/pkg/config" - "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/secrets" - "github.com/windsorcli/cli/pkg/shell" -) - -// ExecutionContext holds common execution values and core dependencies used across the Windsor CLI. -// These fields are set during various initialization steps rather than computed on-demand. -// Includes secret providers for Sops and 1Password, enabling access to secrets across all contexts. -type ExecutionContext struct { - // ContextName is the current context name - ContextName string - - // ProjectRoot is the project root directory path - ProjectRoot string - - // ConfigRoot is the config root directory (/contexts/) - ConfigRoot string - - // TemplateRoot is the template directory (/contexts/_template) - TemplateRoot string - - // Injector is the dependency injector - Injector di.Injector - - // Core dependencies - ConfigHandler config.ConfigHandler - Shell shell.Shell - - // SecretsProviders contains providers for Sops and 1Password secrets management - SecretsProviders struct { - Sops secrets.SecretsProvider - Onepassword secrets.SecretsProvider - } -} diff --git a/pkg/workstation/network/colima_network.go b/pkg/workstation/network/colima_network.go index 9f4e9db44..1b7366088 100644 --- a/pkg/workstation/network/colima_network.go +++ b/pkg/workstation/network/colima_network.go @@ -7,8 +7,8 @@ import ( "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/shell/ssh" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/context/shell/ssh" ) // The ColimaNetworkManager is a specialized network manager for Colima-based environments. diff --git a/pkg/workstation/network/network.go b/pkg/workstation/network/network.go index e1c4a00f6..e51fc62e7 100644 --- a/pkg/workstation/network/network.go +++ b/pkg/workstation/network/network.go @@ -5,11 +5,11 @@ import ( "net" "sort" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/shell/ssh" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/context/shell/ssh" "github.com/windsorcli/cli/pkg/workstation/services" ) diff --git a/pkg/workstation/network/network_test.go b/pkg/workstation/network/network_test.go index d68df51ff..60581c82c 100644 --- a/pkg/workstation/network/network_test.go +++ b/pkg/workstation/network/network_test.go @@ -7,10 +7,10 @@ import ( "strings" "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/shell/ssh" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/context/shell/ssh" "github.com/windsorcli/cli/pkg/workstation/services" ) diff --git a/pkg/workstation/services/dns_service_test.go b/pkg/workstation/services/dns_service_test.go index 11fbfa27e..c0bfd5015 100644 --- a/pkg/workstation/services/dns_service_test.go +++ b/pkg/workstation/services/dns_service_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/compose-spec/compose-go/v2/types" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" ) diff --git a/pkg/workstation/services/registry_service_test.go b/pkg/workstation/services/registry_service_test.go index dde4e8115..a407de82a 100644 --- a/pkg/workstation/services/registry_service_test.go +++ b/pkg/workstation/services/registry_service_test.go @@ -9,7 +9,7 @@ import ( "github.com/compose-spec/compose-go/v2/types" "github.com/windsorcli/cli/api/v1alpha1" "github.com/windsorcli/cli/api/v1alpha1/docker" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" ) diff --git a/pkg/workstation/services/service.go b/pkg/workstation/services/service.go index b864f1b74..708859761 100644 --- a/pkg/workstation/services/service.go +++ b/pkg/workstation/services/service.go @@ -7,9 +7,9 @@ import ( "strings" "github.com/compose-spec/compose-go/v2/types" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The Service is a core interface that defines the contract for service implementations diff --git a/pkg/workstation/services/service_test.go b/pkg/workstation/services/service_test.go index 4c228e66f..07d22f177 100644 --- a/pkg/workstation/services/service_test.go +++ b/pkg/workstation/services/service_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // The ServiceTest is a test suite for the Service interface and BaseService implementation diff --git a/pkg/workstation/virt/colima_virt_test.go b/pkg/workstation/virt/colima_virt_test.go index 800307b63..d85f74c8f 100644 --- a/pkg/workstation/virt/colima_virt_test.go +++ b/pkg/workstation/virt/colima_virt_test.go @@ -16,7 +16,7 @@ import ( colimaConfig "github.com/abiosoft/colima/config" "github.com/goccy/go-yaml" "github.com/shirou/gopsutil/mem" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" ) // ============================================================================= diff --git a/pkg/workstation/virt/mock_virt_test.go b/pkg/workstation/virt/mock_virt_test.go index a6caf606f..7331627b9 100644 --- a/pkg/workstation/virt/mock_virt_test.go +++ b/pkg/workstation/virt/mock_virt_test.go @@ -8,10 +8,10 @@ package virt import ( "testing" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/workstation/services" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/workstation/virt/virt.go b/pkg/workstation/virt/virt.go index 24fcea026..150db1619 100644 --- a/pkg/workstation/virt/virt.go +++ b/pkg/workstation/virt/virt.go @@ -10,9 +10,9 @@ import ( "os" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/workstation/virt/virt_test.go b/pkg/workstation/virt/virt_test.go index de6dead81..0cd678ee7 100644 --- a/pkg/workstation/virt/virt_test.go +++ b/pkg/workstation/virt/virt_test.go @@ -14,10 +14,10 @@ import ( "github.com/goccy/go-yaml" "github.com/shirou/gopsutil/mem" - "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/workstation/services" - "github.com/windsorcli/cli/pkg/shell" + "github.com/windsorcli/cli/pkg/context/shell" ) // ============================================================================= diff --git a/pkg/workstation/workstation.go b/pkg/workstation/workstation.go index 838f69cc6..afee5bb83 100644 --- a/pkg/workstation/workstation.go +++ b/pkg/workstation/workstation.go @@ -4,9 +4,9 @@ import ( "fmt" "os" + "github.com/windsorcli/cli/pkg/context" + "github.com/windsorcli/cli/pkg/context/shell/ssh" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell/ssh" - "github.com/windsorcli/cli/pkg/types" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/services" "github.com/windsorcli/cli/pkg/workstation/virt" @@ -25,7 +25,7 @@ import ( // WorkstationExecutionContext holds the execution context for workstation operations. // It embeds the base ExecutionContext and includes all workstation-specific dependencies. type WorkstationExecutionContext struct { - types.ExecutionContext + context.ExecutionContext // Workstation-specific dependencies (created as needed) NetworkManager network.NetworkManager @@ -47,9 +47,22 @@ type Workstation struct { // Constructor // ============================================================================= +// NewWorkstationExecutionContext creates a WorkstationExecutionContext from a base ExecutionContext. +// This helper function allows production code to easily create a workstation context from the base context. +func NewWorkstationExecutionContext(baseCtx *context.ExecutionContext) *WorkstationExecutionContext { + if baseCtx == nil { + return nil + } + return &WorkstationExecutionContext{ + ExecutionContext: *baseCtx, + } +} + // NewWorkstation creates a new Workstation instance with the provided execution context and injector. // The execution context should already have ConfigHandler and Shell set. // Other dependencies are created only if not already present on the context. +// For production use, create a WorkstationExecutionContext from a base ExecutionContext using NewWorkstationExecutionContext. +// For testing, pass a WorkstationExecutionContext with pre-initialized dependencies to override constructor behavior. func NewWorkstation(ctx *WorkstationExecutionContext, injector di.Injector) (*Workstation, error) { if ctx == nil { return nil, fmt.Errorf("execution context is required") diff --git a/pkg/workstation/workstation_test.go b/pkg/workstation/workstation_test.go index 8ed726761..296aebd1c 100644 --- a/pkg/workstation/workstation_test.go +++ b/pkg/workstation/workstation_test.go @@ -8,11 +8,11 @@ import ( "github.com/windsorcli/cli/api/v1alpha1" "github.com/windsorcli/cli/api/v1alpha1/docker" - "github.com/windsorcli/cli/pkg/config" + ctxpkg "github.com/windsorcli/cli/pkg/context" + "github.com/windsorcli/cli/pkg/context/config" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/context/shell/ssh" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/shell" - "github.com/windsorcli/cli/pkg/shell/ssh" - "github.com/windsorcli/cli/pkg/types" "github.com/windsorcli/cli/pkg/workstation/network" "github.com/windsorcli/cli/pkg/workstation/services" "github.com/windsorcli/cli/pkg/workstation/virt" @@ -204,7 +204,7 @@ func setupMocks(t *testing.T, opts ...*SetupOptions) *Mocks { func setupWorkstationContext(mocks *Mocks) *WorkstationExecutionContext { return &WorkstationExecutionContext{ - ExecutionContext: types.ExecutionContext{ + ExecutionContext: ctxpkg.ExecutionContext{ ContextName: "test-context", ProjectRoot: "/test/project", ConfigRoot: "/test/project/contexts/test-context", @@ -271,7 +271,7 @@ func TestNewWorkstation(t *testing.T) { // Given mocks := setupMocks(t) ctx := &WorkstationExecutionContext{ - ExecutionContext: types.ExecutionContext{ + ExecutionContext: ctxpkg.ExecutionContext{ Shell: mocks.Shell, }, } @@ -295,7 +295,7 @@ func TestNewWorkstation(t *testing.T) { // Given mocks := setupMocks(t) ctx := &WorkstationExecutionContext{ - ExecutionContext: types.ExecutionContext{ + ExecutionContext: ctxpkg.ExecutionContext{ ConfigHandler: mocks.ConfigHandler, }, }