diff --git a/cmd/workflow/pause.go b/cmd/workflow/disable.go similarity index 72% rename from cmd/workflow/pause.go rename to cmd/workflow/disable.go index d608072..d3196f0 100644 --- a/cmd/workflow/pause.go +++ b/cmd/workflow/disable.go @@ -15,16 +15,28 @@ import ( "github.com/spf13/cobra" ) -func NewPauseCmd(f *cmdutil.Factory) *cobra.Command { +func NewDisableCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ - Use: "pause ", - Short: "Pause a workflow", - Args: cobra.ExactArgs(1), - Example: ` # Pause a workflow (will prompt for confirmation) - kh wf pause abc123 - - # Pause without prompting - kh wf pause abc123 --yes`, + Use: "disable ", + // pause is the original name and stays working indefinitely; scripts + // and published links depend on it. + Aliases: []string{"pause"}, + Short: "Disable a workflow so it stops running", + Long: `Disable a workflow so it stops running. + +Turns off a workflow without deleting it. Runs already in flight are unaffected; +the trigger simply stops firing new ones. + +See also: kh workflow enable`, + Args: cobra.ExactArgs(1), + Example: ` # Disable a workflow (will prompt for confirmation) + kh wf disable abc123 + + # Disable without prompting + kh wf disable abc123 --yes + + # pause is an alias and still works + kh wf pause abc123`, RunE: func(cmd *cobra.Command, args []string) error { workflowID := args[0] @@ -85,7 +97,7 @@ func NewPauseCmd(f *cmdutil.Factory) *cobra.Command { p := output.NewPrinter(f.IOStreams, cmd) return p.PrintData(result, func(tw table.Writer) { - fmt.Fprintf(f.IOStreams.Out, "Workflow %s paused\n", workflowID) + fmt.Fprintf(f.IOStreams.Out, "Workflow %s disabled\n", workflowID) tw.Render() }) }, diff --git a/cmd/workflow/pause_test.go b/cmd/workflow/disable_test.go similarity index 93% rename from cmd/workflow/pause_test.go rename to cmd/workflow/disable_test.go index 17a8901..a25fb5e 100644 --- a/cmd/workflow/pause_test.go +++ b/cmd/workflow/disable_test.go @@ -63,7 +63,7 @@ func TestPauseSendsPATCH(t *testing.T) { assert.Equal(t, "PATCH", receivedMethod) assert.Equal(t, "/api/workflows/wf-abc", receivedPath) assert.Equal(t, false, receivedBody["enabled"]) - assert.Contains(t, outBuf.String(), "paused") + assert.Contains(t, outBuf.String(), "disabled") } func TestPauseYesFlagSkipsConfirmation(t *testing.T) { @@ -106,7 +106,7 @@ func TestPausePromptDeclineReturnsCancelError(t *testing.T) { // In non-TTY mode, --yes is not required and confirmation is skipped (auto-proceed) require.NoError(t, err) - assert.Contains(t, outBuf.String(), "paused") + assert.Contains(t, outBuf.String(), "disabled") } func TestPauseNonTTYAutoProceeds(t *testing.T) { @@ -155,20 +155,20 @@ func TestPauseHasYesFlag(t *testing.T) { ios, _, _, _ := iostreams.Test() f := &cmdutil.Factory{AppVersion: "1.0.0", IOStreams: ios} parent := workflow.NewWorkflowCmd(f) - var pauseCmd *cobra.Command + var disableCmd *cobra.Command for _, c := range parent.Commands() { - if strings.HasPrefix(c.Use, "pause") { - pauseCmd = c + if strings.HasPrefix(c.Use, "disable") { + disableCmd = c break } } - require.NotNil(t, pauseCmd, "pause command not found") + require.NotNil(t, disableCmd, "disable command not found") // --yes is a persistent flag inherited from root; verify it's accessible - flag := pauseCmd.Flags().Lookup("yes") + flag := disableCmd.Flags().Lookup("yes") if flag == nil { - flag = pauseCmd.InheritedFlags().Lookup("yes") + flag = disableCmd.InheritedFlags().Lookup("yes") } _ = flag // The test just verifies pause command exists and is wired correctly - assert.NotNil(t, pauseCmd) + assert.NotNil(t, disableCmd) } diff --git a/cmd/workflow/resume.go b/cmd/workflow/enable.go similarity index 73% rename from cmd/workflow/resume.go rename to cmd/workflow/enable.go index 3eb0d68..204b2f2 100644 --- a/cmd/workflow/resume.go +++ b/cmd/workflow/enable.go @@ -15,17 +15,28 @@ import ( "github.com/spf13/cobra" ) -func NewResumeCmd(f *cmdutil.Factory) *cobra.Command { +func NewEnableCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ - Use: "resume ", - Aliases: []string{"activate"}, - Short: "Resume a paused workflow", - Args: cobra.ExactArgs(1), - Example: ` # Resume a workflow (will prompt for confirmation) - kh wf resume abc123 - - # Resume without prompting - kh wf resume abc123 --yes`, + Use: "enable ", + // resume is the original name and stays working indefinitely; scripts + // and published links depend on it. + Aliases: []string{"resume", "activate"}, + Short: "Enable a workflow so it runs on its trigger", + Long: `Enable a workflow so it runs on its trigger. + +Turns on a workflow that is currently disabled. This is the last step after +creating one - a workflow does nothing until it is enabled. + +See also: kh workflow disable`, + Args: cobra.ExactArgs(1), + Example: ` # Enable a workflow (will prompt for confirmation) + kh wf enable abc123 + + # Enable without prompting + kh wf enable abc123 --yes + + # resume is an alias and still works + kh wf resume abc123`, RunE: func(cmd *cobra.Command, args []string) error { workflowID := args[0] @@ -88,7 +99,7 @@ func NewResumeCmd(f *cmdutil.Factory) *cobra.Command { p := output.NewPrinter(f.IOStreams, cmd) return p.PrintData(result, func(tw table.Writer) { - fmt.Fprintf(f.IOStreams.Out, "Workflow %s resumed\n", workflowID) + fmt.Fprintf(f.IOStreams.Out, "Workflow %s enabled\n", workflowID) tw.Render() }) }, diff --git a/cmd/workflow/enable_disable_aliases_test.go b/cmd/workflow/enable_disable_aliases_test.go new file mode 100644 index 0000000..a532e48 --- /dev/null +++ b/cmd/workflow/enable_disable_aliases_test.go @@ -0,0 +1,66 @@ +package workflow_test + +// enable/disable are the primary names, but resume/pause were the originals and +// are kept working indefinitely - scripts, tutorials and published doc links +// depend on them. These tests pin that promise: each alias must reach the same +// command and send the same request body, so a future rename cannot quietly +// drop one. + +import ( + "testing" + + "github.com/keeperhub/cli/cmd/workflow" + "github.com/keeperhub/cli/pkg/cmdutil" + "github.com/keeperhub/cli/pkg/iostreams" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// findSubcommand returns the subcommand reachable by the given name or alias. +func findSubcommand(t *testing.T, name string) (string, bool) { + t.Helper() + ios, _, _, _ := iostreams.Test() + root := workflow.NewWorkflowCmd(&cmdutil.Factory{IOStreams: ios}) + for _, c := range root.Commands() { + if c.Name() == name { + return c.Name(), true + } + for _, alias := range c.Aliases { + if alias == name { + return c.Name(), true + } + } + } + return "", false +} + +func TestEnableIsReachableByItsAliases(t *testing.T) { + for _, name := range []string{"enable", "resume", "activate"} { + resolved, found := findSubcommand(t, name) + require.True(t, found, "%q should reach a subcommand", name) + assert.Equal(t, "enable", resolved, "%q should resolve to enable", name) + } +} + +func TestDisableIsReachableByItsAliases(t *testing.T) { + for _, name := range []string{"disable", "pause"} { + resolved, found := findSubcommand(t, name) + require.True(t, found, "%q should reach a subcommand", name) + assert.Equal(t, "disable", resolved, "%q should resolve to disable", name) + } +} + +func TestEnableAndDisableCrossReferenceEachOther(t *testing.T) { + // "enable" is the word people search for; someone who lands on one should + // be told about the other rather than having to guess the opposite verb. + ios, _, _, _ := iostreams.Test() + root := workflow.NewWorkflowCmd(&cmdutil.Factory{IOStreams: ios}) + + longByName := map[string]string{} + for _, c := range root.Commands() { + longByName[c.Name()] = c.Long + } + + assert.Contains(t, longByName["enable"], "kh workflow disable") + assert.Contains(t, longByName["disable"], "kh workflow enable") +} diff --git a/cmd/workflow/resume_test.go b/cmd/workflow/enable_test.go similarity index 96% rename from cmd/workflow/resume_test.go rename to cmd/workflow/enable_test.go index d027382..0ab25c4 100644 --- a/cmd/workflow/resume_test.go +++ b/cmd/workflow/enable_test.go @@ -59,9 +59,9 @@ func TestResumeSendsPATCHWithEnabledTrue(t *testing.T) { require.NoError(t, err) assert.Equal(t, "PATCH", receivedMethod) assert.Equal(t, "/api/workflows/wf-abc", receivedPath) - // The inverse of pause: this is the assertion that distinguishes the two. + // The inverse of disable: this is the assertion that distinguishes the two. assert.Equal(t, true, receivedBody["enabled"]) - assert.Contains(t, outBuf.String(), "resumed") + assert.Contains(t, outBuf.String(), "enabled") } func TestResumeYesFlagSkipsConfirmation(t *testing.T) { diff --git a/cmd/workflow/workflow.go b/cmd/workflow/workflow.go index ecf32d7..befdec1 100644 --- a/cmd/workflow/workflow.go +++ b/cmd/workflow/workflow.go @@ -27,8 +27,8 @@ func NewWorkflowCmd(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(NewDeleteCmd(f)) cmd.AddCommand(NewUpdateCmd(f)) cmd.AddCommand(NewGoLiveCmd(f)) - cmd.AddCommand(NewPauseCmd(f)) - cmd.AddCommand(NewResumeCmd(f)) + cmd.AddCommand(NewDisableCmd(f)) + cmd.AddCommand(NewEnableCmd(f)) return cmd } diff --git a/cmd/workflow/workflow_test.go b/cmd/workflow/workflow_test.go index 2fd9a99..963ea10 100644 --- a/cmd/workflow/workflow_test.go +++ b/cmd/workflow/workflow_test.go @@ -23,7 +23,7 @@ func TestWorkflowCmdHas9Subcommands(t *testing.T) { f := newTestFactory() wfCmd := workflow.NewWorkflowCmd(f) cmds := wfCmd.Commands() - assert.Equal(t, 9, len(cmds), "expected 9 subcommands: list, run, get, go-live, pause, resume, create, delete, update") + assert.Equal(t, 9, len(cmds), "expected 9 subcommands: list, run, get, go-live, disable, enable, create, delete, update") } func TestWorkflowCmdHasAlias(t *testing.T) { diff --git a/docs/kh_workflow.md b/docs/kh_workflow.md index c4c79e9..84a27a8 100644 --- a/docs/kh_workflow.md +++ b/docs/kh_workflow.md @@ -34,11 +34,11 @@ Manage workflows * [kh](kh.md) - KeeperHub CLI * [kh workflow create](kh_workflow_create.md) - Create a workflow * [kh workflow delete](kh_workflow_delete.md) - Delete a workflow +* [kh workflow disable](kh_workflow_disable.md) - Disable a workflow so it stops running +* [kh workflow enable](kh_workflow_enable.md) - Enable a workflow so it runs on its trigger * [kh workflow get](kh_workflow_get.md) - Get a workflow * [kh workflow go-live](kh_workflow_go-live.md) - Publish a workflow * [kh workflow list](kh_workflow_list.md) - List workflows -* [kh workflow pause](kh_workflow_pause.md) - Pause a workflow -* [kh workflow resume](kh_workflow_resume.md) - Resume a paused workflow * [kh workflow run](kh_workflow_run.md) - Run a workflow * [kh workflow update](kh_workflow_update.md) - Update a workflow diff --git a/docs/kh_workflow_pause.md b/docs/kh_workflow_disable.md similarity index 50% rename from docs/kh_workflow_pause.md rename to docs/kh_workflow_disable.md index f75918a..45e17bc 100644 --- a/docs/kh_workflow_pause.md +++ b/docs/kh_workflow_disable.md @@ -1,25 +1,37 @@ -## kh workflow pause +## kh workflow disable -Pause a workflow +Disable a workflow so it stops running + +### Synopsis + +Disable a workflow so it stops running. + +Turns off a workflow without deleting it. Runs already in flight are unaffected; +the trigger simply stops firing new ones. + +See also: kh workflow enable ``` -kh workflow pause [flags] +kh workflow disable [flags] ``` ### Examples ``` - # Pause a workflow (will prompt for confirmation) - kh wf pause abc123 + # Disable a workflow (will prompt for confirmation) + kh wf disable abc123 + + # Disable without prompting + kh wf disable abc123 --yes - # Pause without prompting - kh wf pause abc123 --yes + # pause is an alias and still works + kh wf pause abc123 ``` ### Options ``` - -h, --help help for pause + -h, --help help for disable -y, --yes Skip confirmation prompt ``` diff --git a/docs/kh_workflow_enable.md b/docs/kh_workflow_enable.md new file mode 100644 index 0000000..b7f07af --- /dev/null +++ b/docs/kh_workflow_enable.md @@ -0,0 +1,51 @@ +## kh workflow enable + +Enable a workflow so it runs on its trigger + +### Synopsis + +Enable a workflow so it runs on its trigger. + +Turns on a workflow that is currently disabled. This is the last step after +creating one - a workflow does nothing until it is enabled. + +See also: kh workflow disable + +``` +kh workflow enable [flags] +``` + +### Examples + +``` + # Enable a workflow (will prompt for confirmation) + kh wf enable abc123 + + # Enable without prompting + kh wf enable abc123 --yes + + # resume is an alias and still works + kh wf resume abc123 +``` + +### Options + +``` + -h, --help help for enable + -y, --yes Skip confirmation prompt +``` + +### Options inherited from parent commands + +``` + -H, --host string KeeperHub host (default: app.keeperhub.com) + --jq string Filter JSON output with a jq expression + --json Output as JSON + --no-color Disable color output + --org string Organization ID to use (overrides default from auth) +``` + +### SEE ALSO + +* [kh workflow](kh_workflow.md) - Manage workflows + diff --git a/docs/kh_workflow_resume.md b/docs/kh_workflow_resume.md deleted file mode 100644 index d6a740a..0000000 --- a/docs/kh_workflow_resume.md +++ /dev/null @@ -1,39 +0,0 @@ -## kh workflow resume - -Resume a paused workflow - -``` -kh workflow resume [flags] -``` - -### Examples - -``` - # Resume a workflow (will prompt for confirmation) - kh wf resume abc123 - - # Resume without prompting - kh wf resume abc123 --yes -``` - -### Options - -``` - -h, --help help for resume - -y, --yes Skip confirmation prompt -``` - -### Options inherited from parent commands - -``` - -H, --host string KeeperHub host (default: app.keeperhub.com) - --jq string Filter JSON output with a jq expression - --json Output as JSON - --no-color Disable color output - --org string Organization ID to use (overrides default from auth) -``` - -### SEE ALSO - -* [kh workflow](kh_workflow.md) - Manage workflows -