Skip to content

Commit 30c1c47

Browse files
committed
Change deploy and diff commands to use positional arguments instead of --context flag
- Update deploy command to accept <context> <stack-name> as positional args - Update diff command to accept <context> <stack-name> as positional args - Remove --context flag from both commands - Update all tests to use new argument structure - Update example documentation to reflect new command syntax - Commands now work as: stackaroo deploy dev vpc, stackaroo diff dev vpc
1 parent 8677684 commit 30c1c47

File tree

7 files changed

+92
-111
lines changed

7 files changed

+92
-111
lines changed

cmd/deploy.go

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@ import (
1616
)
1717

1818
var (
19-
contextName string
2019
// deployer can be injected for testing
2120
deployer deploy.Deployer
2221
)
2322

2423
// deployCmd represents the deploy command
2524
var deployCmd = &cobra.Command{
26-
Use: "deploy",
25+
Use: "deploy <context> <stack-name>",
2726
Short: "Deploy CloudFormation stacks",
2827
Long: `Deploy CloudFormation stacks with integrated change preview.
2928
@@ -39,21 +38,17 @@ ChangeSets to provide accurate previews including:
3938
For new stacks, the command proceeds directly with stack creation.
4039
4140
Examples:
42-
stackaroo deploy vpc --context dev
43-
stackaroo deploy app --context prod
41+
stackaroo deploy dev vpc
42+
stackaroo deploy prod app
4443
4544
The preview shows the same detailed diff information as 'stackaroo diff' but
4645
automatically proceeds with deployment after displaying the changes.`,
47-
Args: cobra.ExactArgs(1),
46+
Args: cobra.ExactArgs(2),
4847
RunE: func(cmd *cobra.Command, args []string) error {
49-
stackName := args[0]
48+
contextName := args[0]
49+
stackName := args[1]
5050
ctx := context.Background()
5151

52-
// Context must be provided
53-
if contextName == "" {
54-
return fmt.Errorf("--context must be specified")
55-
}
56-
5752
return deployWithConfig(ctx, stackName, contextName)
5853
},
5954
}
@@ -125,8 +120,4 @@ func deployWithConfig(ctx context.Context, stackName, contextName string) error
125120

126121
func init() {
127122
rootCmd.AddCommand(deployCmd)
128-
deployCmd.Flags().StringVar(&contextName, "context", "", "deployment context")
129-
if err := deployCmd.MarkFlagRequired("context"); err != nil {
130-
panic(fmt.Sprintf("failed to mark context flag as required: %v", err))
131-
}
132123
}

cmd/deploy_test.go

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func TestDeployCommand_Exists(t *testing.T) {
4141
deployCmd := findCommand(rootCmd, "deploy")
4242

4343
assert.NotNil(t, deployCmd, "deploy command should be registered")
44-
assert.Equal(t, "deploy", deployCmd.Use)
44+
assert.Equal(t, "deploy <context> <stack-name>", deployCmd.Use)
4545
}
4646

4747
func TestDeployCommand_AcceptsStackName(t *testing.T) {
@@ -53,19 +53,24 @@ func TestDeployCommand_AcceptsStackName(t *testing.T) {
5353
assert.NotNil(t, deployCmd.Args, "deploy command should have Args validation set")
5454
}
5555

56-
func TestDeployCommand_HasContextFlag(t *testing.T) {
57-
// Test that deploy command has a --context flag
56+
func TestDeployCommand_AcceptsTwoArgs(t *testing.T) {
57+
// Test that deploy command accepts exactly two arguments (context and stack name)
5858
deployCmd := findCommand(rootCmd, "deploy")
5959
assert.NotNil(t, deployCmd)
6060

61-
// Check that --context flag exists
62-
contextFlag := deployCmd.Flags().Lookup("context")
63-
assert.NotNil(t, contextFlag, "deploy command should have --context flag")
64-
assert.Equal(t, "context", contextFlag.Name)
61+
// Test that Args validation requires exactly 2 arguments
62+
err := deployCmd.Args(deployCmd, []string{"dev", "vpc"})
63+
assert.NoError(t, err, "Two arguments should be valid")
64+
65+
err = deployCmd.Args(deployCmd, []string{"dev"})
66+
assert.Error(t, err, "One argument should be invalid")
67+
68+
err = deployCmd.Args(deployCmd, []string{})
69+
assert.Error(t, err, "No arguments should be invalid")
6570
}
6671

67-
func TestDeployCommand_RequiresContext(t *testing.T) {
68-
// Test that deploy command requires --context flag
72+
func TestDeployCommand_RequiresTwoArgs(t *testing.T) {
73+
// Test that deploy command requires both context and stack name arguments
6974

7075
// Mock deployer that shouldn't be called
7176
mockDeployer := &MockDeployer{}
@@ -74,12 +79,12 @@ func TestDeployCommand_RequiresContext(t *testing.T) {
7479
SetDeployer(mockDeployer)
7580
defer SetDeployer(oldDeployer)
7681

77-
// Execute without context flag - should fail
82+
// Execute with only one argument - should fail
7883
rootCmd.SetArgs([]string{"deploy", "test-stack"})
7984

8085
err := rootCmd.Execute()
81-
assert.Error(t, err, "deploy command should require --context flag")
82-
assert.Contains(t, err.Error(), "required flag(s) \"context\" not set")
86+
assert.Error(t, err, "deploy command should require both context and stack name arguments")
87+
assert.Contains(t, err.Error(), "accepts 2 arg(s), received 1")
8388

8489
// Verify no deployer calls were made
8590
mockDeployer.AssertExpectations(t)
@@ -129,7 +134,7 @@ stacks:
129134
}()
130135

131136
// Execute the root command with deploy subcommand and arguments
132-
rootCmd.SetArgs([]string{"deploy", "test-stack", "--context", "test"})
137+
rootCmd.SetArgs([]string{"deploy", "test", "test-stack"})
133138

134139
// Execute the command - should return error
135140
err = rootCmd.Execute()
@@ -139,7 +144,7 @@ stacks:
139144
}
140145

141146
func TestDeployCommand_RequiresStackName(t *testing.T) {
142-
// Test that deploy command requires exactly one argument (stack name)
147+
// Test that deploy command requires exactly two arguments (context and stack name)
143148

144149
// Mock deployer that shouldn't be called (no expectations set)
145150
mockDeployer := &MockDeployer{}
@@ -151,10 +156,15 @@ func TestDeployCommand_RequiresStackName(t *testing.T) {
151156
// Test with no arguments
152157
rootCmd.SetArgs([]string{"deploy"})
153158
err := rootCmd.Execute()
154-
assert.Error(t, err, "should error when no stack name provided")
159+
assert.Error(t, err, "should error when no arguments provided")
160+
161+
// Test with one argument (missing stack name)
162+
rootCmd.SetArgs([]string{"deploy", "dev"})
163+
err = rootCmd.Execute()
164+
assert.Error(t, err, "should error when only context provided")
155165

156166
// Test with too many arguments
157-
rootCmd.SetArgs([]string{"deploy", "stack1", "stack2"})
167+
rootCmd.SetArgs([]string{"deploy", "dev", "stack1", "stack2"})
158168
err = rootCmd.Execute()
159169
assert.Error(t, err, "should error when too many arguments provided")
160170

@@ -224,12 +234,12 @@ stacks:
224234
}()
225235

226236
// First deployment should succeed
227-
rootCmd.SetArgs([]string{"deploy", "stack-1", "--context", "test"})
237+
rootCmd.SetArgs([]string{"deploy", "test", "stack-1"})
228238
err = rootCmd.Execute()
229239
assert.NoError(t, err, "first deployment should succeed")
230240

231241
// Second deployment should fail
232-
rootCmd.SetArgs([]string{"deploy", "stack-2", "--context", "test"})
242+
rootCmd.SetArgs([]string{"deploy", "test", "stack-2"})
233243
err = rootCmd.Execute()
234244
assert.Error(t, err, "second deployment should fail")
235245
assert.Contains(t, err.Error(), "second deployment failed", "error should contain expected message")
@@ -316,8 +326,8 @@ stacks:
316326
require.NoError(t, err)
317327
}()
318328

319-
// Execute deploy command with context flag
320-
rootCmd.SetArgs([]string{"deploy", "vpc", "--context", "dev"})
329+
// Execute deploy command with context and stack name
330+
rootCmd.SetArgs([]string{"deploy", "dev", "vpc"})
321331

322332
err = rootCmd.Execute()
323333
assert.NoError(t, err, "deploy command should execute successfully with config")
@@ -402,7 +412,7 @@ stacks:
402412

403413
// This should resolve dependencies and deploy: vpc → database → app
404414
// But current implementation will only deploy app
405-
rootCmd.SetArgs([]string{"deploy", "app", "--context", "test"})
415+
rootCmd.SetArgs([]string{"deploy", "test", "app"})
406416

407417
err = rootCmd.Execute()
408418
assert.NoError(t, err, "deploy should succeed")
@@ -478,7 +488,7 @@ stacks:
478488
}()
479489

480490
// Deploy app - should trigger resolver to deploy vpc → database → app
481-
rootCmd.SetArgs([]string{"deploy", "app", "--context", "test"})
491+
rootCmd.SetArgs([]string{"deploy", "test", "app"})
482492

483493
err = rootCmd.Execute()
484494
assert.NoError(t, err, "deploy should succeed")
@@ -576,7 +586,7 @@ stacks:
576586
// Helper function to find a command by name
577587
func findCommand(parent *cobra.Command, name string) *cobra.Command {
578588
for _, cmd := range parent.Commands() {
579-
if cmd.Use == name {
589+
if cmd.Name() == name {
580590
return cmd
581591
}
582592
}

cmd/diff.go

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
)
1717

1818
var (
19-
diffContextName string
2019
diffTemplateOnly bool
2120
diffParametersOnly bool
2221
diffTagsOnly bool
@@ -27,7 +26,7 @@ var (
2726

2827
// diffCmd represents the diff command
2928
var diffCmd = &cobra.Command{
30-
Use: "diff [stack-name]",
29+
Use: "diff <context> <stack-name>",
3130
Short: "Show differences between deployed stack and local configuration",
3231
Long: `Compare the currently deployed CloudFormation stack with your local configuration.
3332
@@ -40,26 +39,22 @@ the current configuration. It compares:
4039
• Resource-level changes (when possible via AWS ChangeSets)
4140
4241
Examples:
43-
stackaroo diff vpc --context dev # Show all changes
44-
stackaroo diff vpc --context prod --template # Template diff only
45-
stackaroo diff vpc --context dev --parameters # Parameter diff only
46-
stackaroo diff vpc --context dev --format json # JSON output`,
47-
Args: cobra.ExactArgs(1),
42+
stackaroo diff dev vpc # Show all changes
43+
stackaroo diff prod vpc --template # Template diff only
44+
stackaroo diff dev vpc --parameters # Parameter diff only
45+
stackaroo diff dev vpc --format json # JSON output`,
46+
Args: cobra.ExactArgs(2),
4847
RunE: func(cmd *cobra.Command, args []string) error {
49-
stackName := args[0]
48+
contextName := args[0]
49+
stackName := args[1]
5050
ctx := context.Background()
5151

52-
// Context must be provided
53-
if diffContextName == "" {
54-
return fmt.Errorf("--context must be specified")
55-
}
56-
5752
// Validate format option
5853
if diffFormat != "text" && diffFormat != "json" {
5954
return fmt.Errorf("--format must be 'text' or 'json'")
6055
}
6156

62-
return diffWithConfig(ctx, stackName, diffContextName)
57+
return diffWithConfig(ctx, stackName, contextName)
6358
},
6459
}
6560

@@ -145,12 +140,6 @@ func diffWithConfig(ctx context.Context, stackName, contextName string) error {
145140
func init() {
146141
rootCmd.AddCommand(diffCmd)
147142

148-
// Required flags
149-
diffCmd.Flags().StringVar(&diffContextName, "context", "", "deployment context")
150-
if err := diffCmd.MarkFlagRequired("context"); err != nil {
151-
panic(fmt.Sprintf("failed to mark context flag as required: %v", err))
152-
}
153-
154143
// Optional flags for filtering diff output
155144
diffCmd.Flags().BoolVar(&diffTemplateOnly, "template", false, "show only template differences")
156145
diffCmd.Flags().BoolVar(&diffParametersOnly, "parameters", false, "show only parameter differences")

0 commit comments

Comments
 (0)