Skip to content

Commit 4d6767d

Browse files
aknyshclaude
andauthored
feat: register terraform compound subcommands in Cobra command tree (#2044)
* feat: register terraform compound subcommands in Cobra command tree Register compound terraform subcommands (state, providers, workspace) as proper Cobra child commands with dedicated documentation pages and updated screengrabs. - Add cmd/terraform/{state,providers,workspace}.go with child subcommands - Add cmd/terraform/utils.go with newTerraformPassthroughSubcommand helper - Add compound subcommand parsing (parseCompoundSubcommand, processTerraformCompoundSubcommand) - Add website documentation for all providers/state/workspace subcommands - Update screengrabs for all CLI commands - Add blog posts for chdir config isolation and artifactory store fix Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: register per-subcommand compat flags for compound terraform subcommands Add per-subcommand compat flag definitions for all 15 compound terraform subcommands (state/providers/workspace families), register them with the command registry, add comprehensive tests, and update website docs with detailed "Native Terraform Flags" sections. Compat flags added: - state list: -state, -id - state mv: -lock, -lock-timeout, -ignore-remote-version - state pull: (none) - state push: -force, -lock, -lock-timeout, -ignore-remote-version - state replace-provider: -auto-approve, -lock, -lock-timeout, -ignore-remote-version - state rm: -lock, -lock-timeout, -ignore-remote-version - state show: -state - providers lock: -platform, -fs-mirror, -net-mirror, -enable-plugin-cache - providers mirror: -platform - providers schema: -json - workspace list: (none) - workspace select: -or-create - workspace new: -lock, -lock-timeout, -state - workspace delete: -force, -lock, -lock-timeout - workspace show: (none) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent d72e3e8 commit 4d6767d

File tree

181 files changed

+22332
-5762
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

181 files changed

+22332
-5762
lines changed

cmd/terraform/compat_flags.go

Lines changed: 191 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,27 @@ import (
77

88
// Terraform flag name constants.
99
const (
10-
tfFlagJSON = "-json"
11-
tfFlagNoColor = "-no-color"
12-
tfFlagState = "-state"
13-
tfFlagStateOut = "-state-out"
14-
tfFlagBackup = "-backup"
15-
tfFlagLock = "-lock"
16-
tfFlagLockTimeout = "-lock-timeout"
10+
tfFlagJSON = "-json"
11+
tfFlagNoColor = "-no-color"
12+
tfFlagState = "-state"
13+
tfFlagStateOut = "-state-out"
14+
tfFlagBackup = "-backup"
15+
tfFlagLock = "-lock"
16+
tfFlagLockTimeout = "-lock-timeout"
17+
tfFlagIgnoreRemoteVersion = "-ignore-remote-version"
1718
)
1819

1920
// Terraform flag description constants.
2021
const (
21-
descDisableColorOutput = "Disable color output"
22-
descPathToStateFile = "Path to the state file"
23-
descPathToReadSaveState = "Path to read and save state"
24-
descPathToWriteState = "Path to write updated state"
25-
descPathToBackupState = "Path to backup the existing state file"
26-
descLockStateFile = "Lock the state file"
27-
descDurationRetryLock = "Duration to retry state lock"
28-
descOutputJSONFormat = "Output in JSON format"
22+
descDisableColorOutput = "Disable color output"
23+
descPathToStateFile = "Path to the state file"
24+
descPathToReadSaveState = "Path to read and save state"
25+
descPathToWriteState = "Path to write updated state"
26+
descPathToBackupState = "Path to backup the existing state file"
27+
descLockStateFile = "Lock the state file"
28+
descDurationRetryLock = "Duration to retry state lock"
29+
descOutputJSONFormat = "Output in JSON format"
30+
descIgnoreRemoteVersionChecks = "Ignore remote state version constraints"
2931
)
3032

3133
// TerraformGlobalCompatFlags returns TRUE global terraform flags.
@@ -189,6 +191,81 @@ func StateCompatFlags() map[string]compat.CompatibilityFlag {
189191
}
190192
}
191193

194+
// StateListCompatFlags returns compatibility flags specific to terraform state list.
195+
func StateListCompatFlags() map[string]compat.CompatibilityFlag {
196+
defer perf.Track(nil, "terraform.StateListCompatFlags")()
197+
198+
return map[string]compat.CompatibilityFlag{
199+
tfFlagState: {Behavior: compat.AppendToSeparated, Description: descPathToStateFile},
200+
"-id": {Behavior: compat.AppendToSeparated, Description: "Filter results by resource ID"},
201+
}
202+
}
203+
204+
// StateMvCompatFlags returns compatibility flags specific to terraform state mv.
205+
// Note: terraform's -dry-run is intentionally excluded to avoid conflict with Atmos --dry-run.
206+
func StateMvCompatFlags() map[string]compat.CompatibilityFlag {
207+
defer perf.Track(nil, "terraform.StateMvCompatFlags")()
208+
209+
return map[string]compat.CompatibilityFlag{
210+
tfFlagLock: {Behavior: compat.AppendToSeparated, Description: descLockStateFile},
211+
tfFlagLockTimeout: {Behavior: compat.AppendToSeparated, Description: descDurationRetryLock},
212+
tfFlagIgnoreRemoteVersion: {Behavior: compat.AppendToSeparated, Description: descIgnoreRemoteVersionChecks},
213+
}
214+
}
215+
216+
// StatePullCompatFlags returns compatibility flags specific to terraform state pull.
217+
// State pull has no native flags beyond standard terraform flags.
218+
func StatePullCompatFlags() map[string]compat.CompatibilityFlag {
219+
defer perf.Track(nil, "terraform.StatePullCompatFlags")()
220+
221+
return map[string]compat.CompatibilityFlag{}
222+
}
223+
224+
// StatePushCompatFlags returns compatibility flags specific to terraform state push.
225+
func StatePushCompatFlags() map[string]compat.CompatibilityFlag {
226+
defer perf.Track(nil, "terraform.StatePushCompatFlags")()
227+
228+
return map[string]compat.CompatibilityFlag{
229+
"-force": {Behavior: compat.AppendToSeparated, Description: "Write the state even if lineages don't match or serial is lower"},
230+
tfFlagLock: {Behavior: compat.AppendToSeparated, Description: descLockStateFile},
231+
tfFlagLockTimeout: {Behavior: compat.AppendToSeparated, Description: descDurationRetryLock},
232+
tfFlagIgnoreRemoteVersion: {Behavior: compat.AppendToSeparated, Description: descIgnoreRemoteVersionChecks},
233+
}
234+
}
235+
236+
// StateReplaceProviderCompatFlags returns compatibility flags specific to terraform state replace-provider.
237+
func StateReplaceProviderCompatFlags() map[string]compat.CompatibilityFlag {
238+
defer perf.Track(nil, "terraform.StateReplaceProviderCompatFlags")()
239+
240+
return map[string]compat.CompatibilityFlag{
241+
"-auto-approve": {Behavior: compat.AppendToSeparated, Description: "Skip interactive approval"},
242+
tfFlagLock: {Behavior: compat.AppendToSeparated, Description: descLockStateFile},
243+
tfFlagLockTimeout: {Behavior: compat.AppendToSeparated, Description: descDurationRetryLock},
244+
tfFlagIgnoreRemoteVersion: {Behavior: compat.AppendToSeparated, Description: descIgnoreRemoteVersionChecks},
245+
}
246+
}
247+
248+
// StateRmCompatFlags returns compatibility flags specific to terraform state rm.
249+
// Note: terraform's -dry-run is intentionally excluded to avoid conflict with Atmos --dry-run.
250+
func StateRmCompatFlags() map[string]compat.CompatibilityFlag {
251+
defer perf.Track(nil, "terraform.StateRmCompatFlags")()
252+
253+
return map[string]compat.CompatibilityFlag{
254+
tfFlagLock: {Behavior: compat.AppendToSeparated, Description: descLockStateFile},
255+
tfFlagLockTimeout: {Behavior: compat.AppendToSeparated, Description: descDurationRetryLock},
256+
tfFlagIgnoreRemoteVersion: {Behavior: compat.AppendToSeparated, Description: descIgnoreRemoteVersionChecks},
257+
}
258+
}
259+
260+
// StateShowCompatFlags returns compatibility flags specific to terraform state show.
261+
func StateShowCompatFlags() map[string]compat.CompatibilityFlag {
262+
defer perf.Track(nil, "terraform.StateShowCompatFlags")()
263+
264+
return map[string]compat.CompatibilityFlag{
265+
tfFlagState: {Behavior: compat.AppendToSeparated, Description: descPathToStateFile},
266+
}
267+
}
268+
192269
// ImportCompatFlags returns compatibility flags specific to terraform import.
193270
func ImportCompatFlags() map[string]compat.CompatibilityFlag {
194271
defer perf.Track(nil, "terraform.ImportCompatFlags")()
@@ -305,6 +382,53 @@ func WorkspaceCompatFlags() map[string]compat.CompatibilityFlag {
305382
}
306383
}
307384

385+
// WorkspaceListCompatFlags returns compatibility flags specific to terraform workspace list.
386+
// Workspace list has no native flags beyond standard terraform flags.
387+
func WorkspaceListCompatFlags() map[string]compat.CompatibilityFlag {
388+
defer perf.Track(nil, "terraform.WorkspaceListCompatFlags")()
389+
390+
return map[string]compat.CompatibilityFlag{}
391+
}
392+
393+
// WorkspaceSelectCompatFlags returns compatibility flags specific to terraform workspace select.
394+
func WorkspaceSelectCompatFlags() map[string]compat.CompatibilityFlag {
395+
defer perf.Track(nil, "terraform.WorkspaceSelectCompatFlags")()
396+
397+
return map[string]compat.CompatibilityFlag{
398+
"-or-create": {Behavior: compat.AppendToSeparated, Description: "Create the workspace if it doesn't exist"},
399+
}
400+
}
401+
402+
// WorkspaceNewCompatFlags returns compatibility flags specific to terraform workspace new.
403+
func WorkspaceNewCompatFlags() map[string]compat.CompatibilityFlag {
404+
defer perf.Track(nil, "terraform.WorkspaceNewCompatFlags")()
405+
406+
return map[string]compat.CompatibilityFlag{
407+
tfFlagLock: {Behavior: compat.AppendToSeparated, Description: descLockStateFile},
408+
tfFlagLockTimeout: {Behavior: compat.AppendToSeparated, Description: descDurationRetryLock},
409+
tfFlagState: {Behavior: compat.AppendToSeparated, Description: descPathToStateFile},
410+
}
411+
}
412+
413+
// WorkspaceDeleteCompatFlags returns compatibility flags specific to terraform workspace delete.
414+
func WorkspaceDeleteCompatFlags() map[string]compat.CompatibilityFlag {
415+
defer perf.Track(nil, "terraform.WorkspaceDeleteCompatFlags")()
416+
417+
return map[string]compat.CompatibilityFlag{
418+
"-force": {Behavior: compat.AppendToSeparated, Description: "Force deletion even if the workspace has resources"},
419+
tfFlagLock: {Behavior: compat.AppendToSeparated, Description: descLockStateFile},
420+
tfFlagLockTimeout: {Behavior: compat.AppendToSeparated, Description: descDurationRetryLock},
421+
}
422+
}
423+
424+
// WorkspaceShowCompatFlags returns compatibility flags specific to terraform workspace show.
425+
// Workspace show has no native flags beyond standard terraform flags.
426+
func WorkspaceShowCompatFlags() map[string]compat.CompatibilityFlag {
427+
defer perf.Track(nil, "terraform.WorkspaceShowCompatFlags")()
428+
429+
return map[string]compat.CompatibilityFlag{}
430+
}
431+
308432
// ProvidersCompatFlags returns compatibility flags specific to terraform providers.
309433
// Note: terraform providers has no special flags beyond standard terraform flags.
310434
func ProvidersCompatFlags() map[string]compat.CompatibilityFlag {
@@ -313,6 +437,36 @@ func ProvidersCompatFlags() map[string]compat.CompatibilityFlag {
313437
return map[string]compat.CompatibilityFlag{}
314438
}
315439

440+
// ProvidersLockCompatFlags returns compatibility flags specific to terraform providers lock.
441+
func ProvidersLockCompatFlags() map[string]compat.CompatibilityFlag {
442+
defer perf.Track(nil, "terraform.ProvidersLockCompatFlags")()
443+
444+
return map[string]compat.CompatibilityFlag{
445+
"-platform": {Behavior: compat.AppendToSeparated, Description: "Target platform for provider packages (can be specified multiple times)"},
446+
"-fs-mirror": {Behavior: compat.AppendToSeparated, Description: "Consult the given filesystem mirror directory for provider packages"},
447+
"-net-mirror": {Behavior: compat.AppendToSeparated, Description: "Consult the given network mirror for provider packages"},
448+
"-enable-plugin-cache": {Behavior: compat.AppendToSeparated, Description: "Enable the global plugin cache during lock file creation"},
449+
}
450+
}
451+
452+
// ProvidersMirrorCompatFlags returns compatibility flags specific to terraform providers mirror.
453+
func ProvidersMirrorCompatFlags() map[string]compat.CompatibilityFlag {
454+
defer perf.Track(nil, "terraform.ProvidersMirrorCompatFlags")()
455+
456+
return map[string]compat.CompatibilityFlag{
457+
"-platform": {Behavior: compat.AppendToSeparated, Description: "Target platform for provider packages (can be specified multiple times)"},
458+
}
459+
}
460+
461+
// ProvidersSchemaCompatFlags returns compatibility flags specific to terraform providers schema.
462+
func ProvidersSchemaCompatFlags() map[string]compat.CompatibilityFlag {
463+
defer perf.Track(nil, "terraform.ProvidersSchemaCompatFlags")()
464+
465+
return map[string]compat.CompatibilityFlag{
466+
tfFlagJSON: {Behavior: compat.AppendToSeparated, Description: "Output provider schemas in JSON format"},
467+
}
468+
}
469+
316470
// AllTerraformCompatFlags returns a combined set of all terraform compatibility flags.
317471
// This is used for preprocessing in Execute() to identify terraform pass-through flags.
318472
// Before Cobra parses the command line, we combine all possible flags so we can
@@ -353,5 +507,27 @@ func AllTerraformCompatFlags() map[string]compat.CompatibilityFlag {
353507
mergeFlags(WorkspaceCompatFlags())
354508
mergeFlags(ProvidersCompatFlags())
355509

510+
// Per-subcommand compat flags for compound subcommands.
511+
// State sub-subcommands.
512+
mergeFlags(StateListCompatFlags())
513+
mergeFlags(StateMvCompatFlags())
514+
mergeFlags(StatePullCompatFlags())
515+
mergeFlags(StatePushCompatFlags())
516+
mergeFlags(StateReplaceProviderCompatFlags())
517+
mergeFlags(StateRmCompatFlags())
518+
mergeFlags(StateShowCompatFlags())
519+
520+
// Providers sub-subcommands.
521+
mergeFlags(ProvidersLockCompatFlags())
522+
mergeFlags(ProvidersMirrorCompatFlags())
523+
mergeFlags(ProvidersSchemaCompatFlags())
524+
525+
// Workspace sub-subcommands.
526+
mergeFlags(WorkspaceListCompatFlags())
527+
mergeFlags(WorkspaceSelectCompatFlags())
528+
mergeFlags(WorkspaceNewCompatFlags())
529+
mergeFlags(WorkspaceDeleteCompatFlags())
530+
mergeFlags(WorkspaceShowCompatFlags())
531+
356532
return flags
357533
}

cmd/terraform/providers.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/spf13/cobra"
55

66
"github.com/cloudposse/atmos/cmd/internal"
7+
"github.com/cloudposse/atmos/pkg/flags/compat"
78
)
89

910
// providersCmd represents the terraform providers command.
@@ -15,12 +16,33 @@ var providersCmd = &cobra.Command{
1516
For complete Terraform/OpenTofu documentation, see:
1617
https://developer.hashicorp.com/terraform/cli/commands/providers
1718
https://opentofu.org/docs/cli/commands/providers`,
19+
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
1820
RunE: func(cmd *cobra.Command, args []string) error {
1921
return terraformRun(terraformCmd, cmd, args)
2022
},
2123
}
2224

25+
// providersSubcmds defines the terraform providers sub-subcommands.
26+
// Each entry is registered as a Cobra child command of providersCmd,
27+
// enabling proper command tree routing instead of hardcoded argument parsing.
28+
// The compatFunc provides per-subcommand compat flags for the command registry.
29+
var providersSubcmds = []struct {
30+
name string
31+
short string
32+
compatFunc func() map[string]compat.CompatibilityFlag
33+
}{
34+
{"lock", "Write out dependency locks for the configured providers", ProvidersLockCompatFlags},
35+
{"mirror", "Save local copies of all required provider plugins", ProvidersMirrorCompatFlags},
36+
{"schema", "Show schemas for the providers used in the configuration", ProvidersSchemaCompatFlags},
37+
}
38+
2339
func init() {
40+
// Register sub-subcommands for providers (e.g., "providers lock", "providers mirror").
41+
for _, sub := range providersSubcmds {
42+
providersCmd.AddCommand(newTerraformPassthroughSubcommand(providersCmd, sub.name, sub.short))
43+
internal.RegisterCommandCompatFlags("terraform", "providers-"+sub.name, sub.compatFunc())
44+
}
45+
2446
// Register completions for providersCmd.
2547
RegisterTerraformCompletions(providersCmd)
2648

cmd/terraform/state.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/spf13/cobra"
55

66
"github.com/cloudposse/atmos/cmd/internal"
7+
"github.com/cloudposse/atmos/pkg/flags/compat"
78
)
89

910
// stateCmd represents the terraform state command.
@@ -15,12 +16,37 @@ var stateCmd = &cobra.Command{
1516
For complete Terraform/OpenTofu documentation, see:
1617
https://developer.hashicorp.com/terraform/cli/commands/state
1718
https://opentofu.org/docs/cli/commands/state`,
19+
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
1820
RunE: func(cmd *cobra.Command, args []string) error {
1921
return terraformRun(terraformCmd, cmd, args)
2022
},
2123
}
2224

25+
// stateSubcommands defines the terraform state sub-subcommands.
26+
// Each entry is registered as a Cobra child command of stateCmd,
27+
// enabling proper command tree routing instead of hardcoded argument parsing.
28+
// The compatFunc provides per-subcommand compat flags for the command registry.
29+
var stateSubcommands = []struct {
30+
name string
31+
short string
32+
compatFunc func() map[string]compat.CompatibilityFlag
33+
}{
34+
{"list", "List resources in the Terraform state", StateListCompatFlags},
35+
{"mv", "Move an item in Terraform state", StateMvCompatFlags},
36+
{"pull", "Pull current state and output to stdout", StatePullCompatFlags},
37+
{"push", "Update remote state from a local state file", StatePushCompatFlags},
38+
{"replace-provider", "Replace provider in the state", StateReplaceProviderCompatFlags},
39+
{"rm", "Remove instances from the Terraform state", StateRmCompatFlags},
40+
{"show", "Show a resource in the Terraform state", StateShowCompatFlags},
41+
}
42+
2343
func init() {
44+
// Register sub-subcommands for state (e.g., "state list", "state mv").
45+
for _, sub := range stateSubcommands {
46+
stateCmd.AddCommand(newTerraformPassthroughSubcommand(stateCmd, sub.name, sub.short))
47+
internal.RegisterCommandCompatFlags("terraform", "state-"+sub.name, sub.compatFunc())
48+
}
49+
2450
// Register completions for stateCmd.
2551
RegisterTerraformCompletions(stateCmd)
2652

0 commit comments

Comments
 (0)