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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions cmd/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ var (
)

var downCmd = &cobra.Command{
Use: "down",
Short: "Tear down the Windsor environment",
Long: "Tear down the Windsor environment by executing necessary shell commands.",
SilenceUsage: true,
PersistentPreRunE: checkTrust,
Use: "down",
Short: "Tear down the Windsor environment",
Long: "Tear down the Windsor environment by executing necessary shell commands.",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)
Expand Down
9 changes: 4 additions & 5 deletions cmd/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import (
)

var envCmd = &cobra.Command{
Use: "env",
Short: "Output commands to set environment variables",
Long: "Output commands to set environment variables for the application.",
SilenceUsage: true,
PersistentPreRunE: checkTrust,
Use: "env",
Short: "Output commands to set environment variables",
Long: "Output commands to set environment variables for the application.",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)
Expand Down
6 changes: 4 additions & 2 deletions cmd/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ func TestEnvCmd(t *testing.T) {
})

t.Run("SuccessWithHook", func(t *testing.T) {
// Given proper output capture
// Given proper output capture and mock setup
_, stderr := setup(t)
setupMocks(t)

rootCmd.SetArgs([]string{"env", "--hook"})

Expand Down Expand Up @@ -107,8 +108,9 @@ func TestEnvCmd(t *testing.T) {
})

t.Run("SuccessWithAllFlags", func(t *testing.T) {
// Given proper output capture
// Given proper output capture and mock setup
_, stderr := setup(t)
setupMocks(t)

rootCmd.SetArgs([]string{"env", "--decrypt", "--hook", "--verbose"})

Expand Down
9 changes: 4 additions & 5 deletions cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import (

// execCmd represents the exec command
var execCmd = &cobra.Command{
Use: "exec [command] [args...]",
Short: "Execute a command with environment variables",
Long: "Execute a command with environment variables loaded from configuration and secrets",
Args: cobra.MinimumNArgs(1),
PersistentPreRunE: checkTrust,
Use: "exec [command] [args...]",
Short: "Execute a command with environment variables",
Long: "Execute a command with environment variables loaded from configuration and secrets",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
// Safety check for arguments
if len(args) == 0 {
Expand Down
7 changes: 3 additions & 4 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ import (
var installWaitFlag bool

var installCmd = &cobra.Command{
Use: "install",
Short: "Install the blueprint's cluster-level services",
SilenceUsage: true,
PersistentPreRunE: checkTrust,
Use: "install",
Short: "Install the blueprint's cluster-level services",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)
Expand Down
91 changes: 56 additions & 35 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -32,45 +33,57 @@ func Execute() error {

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "windsor",
Short: "A command line interface to assist your cloud native development workflow",
Long: "A command line interface to assist your cloud native development workflow",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// Set context from root command
ctx := cmd.Root().Context()

// Add verbose flag to context if set
if verbose {
ctx = context.WithValue(ctx, "verbose", true)
}

cmd.SetContext(ctx)

return nil
},
Use: "windsor",
Short: "A command line interface to assist your cloud native development workflow",
Long: "A command line interface to assist your cloud native development workflow",
PersistentPreRunE: commandPreflight,
}

func init() {
// Define the --verbose flag
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose output")
}

// checkTrust performs trust validation for Windsor CLI commands requiring a trusted project directory.
// It verifies directory trust status by checking if the current project directory is in the trusted file list.
// For the "init" command, or for the "env" command with the --hook flag set, trust validation is skipped.
// Returns an error if the directory is untrusted.
func checkTrust(cmd *cobra.Command, args []string) error {
if cmd.Name() == "init" {
return nil
// commandPreflight orchestrates global CLI preflight checks and context initialization for all commands.
// Intended for use as cobra.Command.PersistentPreRunE, it ensures the command context is configured and
// the current directory is authorized for Windsor operations prior to command execution.
func commandPreflight(cmd *cobra.Command, args []string) error {
if err := setupGlobalContext(cmd); err != nil {
return err
}
if err := enforceTrustedDirectory(cmd); err != nil {
return err
}
return nil
}

if cmd.Name() == "env" {
if hook, _ := cmd.Flags().GetBool("hook"); hook {
return nil
}
// setupGlobalContext injects global flags and context values into the command's context.
// It sets the verbose flag in the context if enabled.
func setupGlobalContext(cmd *cobra.Command) error {
ctx := cmd.Root().Context()
if ctx == nil {
ctx = context.Background()
}
if verbose {
ctx = context.WithValue(ctx, "verbose", true)
}
cmd.SetContext(ctx)
return nil
}

// enforceTrustedDirectory checks if the current working directory is trusted for Windsor operations.
// Enforces trust for a defined set of commands, including "env". For "env" with --hook, exits silently to avoid shell integration noise.
// Returns an error if the directory is not trusted.
func enforceTrustedDirectory(cmd *cobra.Command) error {
const notTrustedDirMsg = "not in a trusted directory. If you are in a Windsor project, run 'windsor init' to approve"
enforcedCommands := []string{"up", "down", "exec", "install", "env"}
cmdName := cmd.Name()
shouldEnforce := slices.Contains(enforcedCommands, cmdName)

if !shouldEnforce {
return nil
}

// Use shims to allow mocking in tests
currentDir, err := shims.Getwd()
if err != nil {
return fmt.Errorf("Error getting current directory: %w", err)
Expand All @@ -87,18 +100,26 @@ func checkTrust(cmd *cobra.Command, args []string) error {
data, err := shims.ReadFile(trustedFilePath)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("not in a trusted directory. If you are in a Windsor project, run 'windsor init' to approve")
return fmt.Errorf(notTrustedDirMsg)
}
return fmt.Errorf("not in a trusted directory. If you are in a Windsor project, run 'windsor init' to approve")
return fmt.Errorf(notTrustedDirMsg)
}

trustedDirs := strings.Split(strings.TrimSpace(string(data)), "\n")
for _, trustedDir := range trustedDirs {
trimmedDir := strings.TrimSpace(trustedDir)
if trimmedDir != "" && strings.HasPrefix(currentDir, trimmedDir) {
iter := strings.SplitSeq(strings.TrimSpace(string(data)), "\n")

for trustedDir := range iter {
trustedDir = strings.TrimSpace(trustedDir)
if trustedDir != "" && strings.HasPrefix(currentDir, trustedDir) {
return nil
}
}

return fmt.Errorf("not in a trusted directory. If you are in a Windsor project, run 'windsor init' to approve")
if cmdName == "env" {
hook, _ := cmd.Flags().GetBool("hook")
if hook {
shims.Exit(0)
}
}

return fmt.Errorf(notTrustedDirMsg)
}
34 changes: 20 additions & 14 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,10 @@ func TestRootCmd_PersistentPreRunE(t *testing.T) {
})
}

func TestCheckTrust(t *testing.T) {
func TestCommandPreflight(t *testing.T) {
// Set up mocks for all tests
setupMocks(t)

createMockCmd := func(name string) *cobra.Command {
return &cobra.Command{
Use: name,
Expand All @@ -280,7 +283,7 @@ func TestCheckTrust(t *testing.T) {
cmd := createMockCmd("init")

// When checking trust
err := checkTrust(cmd, []string{})
err := commandPreflight(cmd, []string{})

// Then no error should occur (trust check is skipped)
if err != nil {
Expand All @@ -295,7 +298,7 @@ func TestCheckTrust(t *testing.T) {
cmd.Flags().Set("hook", "true")

// When checking trust
err := checkTrust(cmd, []string{})
err := commandPreflight(cmd, []string{})

// Then no error should occur (trust check is skipped for env --hook)
if err != nil {
Expand All @@ -308,20 +311,23 @@ func TestCheckTrust(t *testing.T) {
cmd := createMockCmd("env")
cmd.Flags().Bool("hook", false, "hook flag")

// Set up a temporary directory that's not trusted
// Override shims to return an untrusted directory
tmpDir := t.TempDir()
originalDir, err := os.Getwd()
if err != nil {
t.Fatalf("Failed to get current directory: %v", err)
}
defer os.Chdir(originalDir)

if err := os.Chdir(tmpDir); err != nil {
t.Fatalf("Failed to change directory: %v", err)
origShims := shims
defer func() { shims = origShims }()

shims = &Shims{
Exit: func(int) {},
UserHomeDir: func() (string, error) { return t.TempDir(), nil },
Getwd: func() (string, error) { return tmpDir, nil },
ReadFile: func(filename string) ([]byte, error) {
// Return trusted file content that does NOT include tmpDir
return []byte("/test/project\n"), nil
},
}

// When checking trust
err = checkTrust(cmd, []string{})
err := commandPreflight(cmd, []string{})

// Then an error should occur about untrusted directory
if err == nil {
Expand Down Expand Up @@ -380,7 +386,7 @@ func TestCheckTrust(t *testing.T) {
}

// When checking trust
err = checkTrust(cmd, []string{})
err = commandPreflight(cmd, []string{})

// Then no error should occur
if err != nil {
Expand Down
9 changes: 4 additions & 5 deletions cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ var (
)

var upCmd = &cobra.Command{
Use: "up",
Short: "Set up the Windsor environment",
Long: "Set up the Windsor environment by executing necessary shell commands.",
SilenceUsage: true,
PersistentPreRunE: checkTrust,
Use: "up",
Short: "Set up the Windsor environment",
Long: "Set up the Windsor environment by executing necessary shell commands.",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)
Expand Down
7 changes: 3 additions & 4 deletions cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ var Goos = runtime.GOOS

// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Display the current version",
Long: "Display the current version of the application",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return nil },
Use: "version",
Short: "Display the current version",
Long: "Display the current version of the application",
Run: func(cmd *cobra.Command, args []string) {
platform := fmt.Sprintf("%s/%s", Goos, runtime.GOARCH)
cmd.Printf("Version: %s\nCommit SHA: %s\nPlatform: %s\n", version, commitSHA, platform)
Expand Down
Loading