diff --git a/internal/commands/build_configs.go b/internal/commands/build_configs.go index 21a3b01..44a0a7f 100644 --- a/internal/commands/build_configs.go +++ b/internal/commands/build_configs.go @@ -13,7 +13,9 @@ func newBuildConfigsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "build-configs", Short: "Manage build configurations", - Long: `The build environment for a project — which language versions, OS image, and runtime tooling are available to build commands. Pair with "dhq language-versions" to discover what's pinnable.`, + Long: `The build environment for a project — which language versions, OS image, and runtime tooling are available to build commands. Pair with "dhq language-versions" to discover what's pinnable. + +Each project has one default build config (read-only) and may have overrides that customize the environment for specific servers. Only overrides can be updated or deleted. To customize the default, create an override with "dhq build-configs create".`, } cmd.AddCommand( &cobra.Command{ @@ -37,13 +39,13 @@ func newBuildConfigsCmd() *cobra.Command { } rows := make([][]string, len(configs)) for i, c := range configs { - def := "" + kind := "(override)" if c.Default { - def = "(default)" + kind = "(default)" } - rows[i] = []string{c.Identifier, def} + rows[i] = []string{c.Identifier, kind} } - env.WriteTable([]string{"Identifier", "Default"}, rows) + env.WriteTable([]string{"Identifier", "Type"}, rows) return nil }, }, diff --git a/internal/commands/ssh_commands.go b/internal/commands/ssh_commands.go index a337190..aec9e9e 100644 --- a/internal/commands/ssh_commands.go +++ b/internal/commands/ssh_commands.go @@ -86,11 +86,32 @@ Build commands run on the build server; SSH commands run on the deploy target.`, return cmd } +// validSSHTimings mirrors Command::TIMING in the DeployHQ Rails model. +var validSSHTimings = []string{"all", "first", "after_first"} + +func validateSSHTiming(timing string) error { + if timing == "" { + return nil + } + for _, t := range validSSHTimings { + if timing == t { + return nil + } + } + return &output.UserError{ + Message: fmt.Sprintf("Invalid --timing %q", timing), + Hint: "Valid values: all, first, after_first", + } +} + func newSSHCommandsUpdateCmd() *cobra.Command { var command, description, timing string cmd := &cobra.Command{ Use: "update ", Short: "Update an SSH command", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + if err := validateSSHTiming(timing); err != nil { + return err + } projectID, err := cliCtx.RequireProject() if err != nil { return err @@ -111,7 +132,7 @@ func newSSHCommandsUpdateCmd() *cobra.Command { } cmd.Flags().StringVar(&command, "command", "", "SSH command") cmd.Flags().StringVar(&description, "description", "", "Description") - cmd.Flags().StringVar(&timing, "timing", "", "Timing: before or after") + cmd.Flags().StringVar(&timing, "timing", "", "Timing: all, first, or after_first") return cmd } @@ -123,6 +144,9 @@ func newSSHCommandsCreateCmd() *cobra.Command { if command == "" { return &output.UserError{Message: "--command is required"} } + if err := validateSSHTiming(timing); err != nil { + return err + } projectID, err := cliCtx.RequireProject() if err != nil { return err @@ -143,6 +167,6 @@ func newSSHCommandsCreateCmd() *cobra.Command { } cmd.Flags().StringVar(&command, "command", "", "SSH command (required)") cmd.Flags().StringVar(&description, "description", "", "Description") - cmd.Flags().StringVar(&timing, "timing", "after", "Timing: before or after") + cmd.Flags().StringVar(&timing, "timing", "all", "Timing: all, first, or after_first") return cmd } diff --git a/pkg/sdk/ssh_commands.go b/pkg/sdk/ssh_commands.go index a7cda47..4a355ee 100644 --- a/pkg/sdk/ssh_commands.go +++ b/pkg/sdk/ssh_commands.go @@ -22,7 +22,7 @@ type SSHCommand struct { // SSHCommandCreateRequest is the payload for creating/updating an SSH command. type SSHCommandCreateRequest struct { Description string `json:"description,omitempty"` - Command string `json:"command"` + Command string `json:"command,omitempty"` HaltOnError *bool `json:"halt_on_error,omitempty"` Timing string `json:"timing,omitempty"` Timeout *int `json:"timeout,omitempty"`