From 8a965d25fa5726d90995e6355b13fa346d427935 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 6 Aug 2025 16:53:14 +1000 Subject: [PATCH 1/5] feat: task wait command support -f argument --- pkg/cmd/task/wait/wait.go | 84 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/task/wait/wait.go b/pkg/cmd/task/wait/wait.go index cfc769c8..ba725040 100644 --- a/pkg/cmd/task/wait/wait.go +++ b/pkg/cmd/task/wait/wait.go @@ -9,6 +9,7 @@ import ( "github.com/OctopusDeploy/cli/pkg/cmd" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/output" "github.com/OctopusDeploy/cli/pkg/util" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tasks" @@ -34,12 +35,23 @@ type WaitOptions struct { PollInterval int CancelOnTimeout bool ShowProgress bool + Command *cobra.Command } type ServerTasksCallback func([]string) ([]*tasks.Task, error) type TaskDetailsCallback func(string) (*tasks.TaskDetailsResource, error) -func NewWaitOps(dependencies *cmd.Dependencies, taskIDs []string, timeout int, pollInterval int, cancelOnTimeout bool, showProgress bool) *WaitOptions { +type TaskAsJson struct { + Id string `json:"Id"` + Description string `json:"Description"` + State string `json:"State"` + StartTime *time.Time `json:"StartTime"` + CompletedTime *time.Time `json:"CompletedTime"` + Duration string `json:"Duration"` + FinishedSuccessfully *bool `json:"FinishedSuccessfully"` +} + +func NewWaitOps(dependencies *cmd.Dependencies, taskIDs []string, timeout int, pollInterval int, cancelOnTimeout bool, showProgress bool, cmd *cobra.Command) *WaitOptions { return &WaitOptions{ Dependencies: dependencies, TaskIDs: taskIDs, @@ -50,6 +62,7 @@ func NewWaitOps(dependencies *cmd.Dependencies, taskIDs []string, timeout int, p PollInterval: pollInterval, CancelOnTimeout: cancelOnTimeout, ShowProgress: showProgress, + Command: cmd, } } @@ -70,7 +83,7 @@ func NewCmdWait(f factory.Factory) *cobra.Command { taskIDs = append(taskIDs, util.ReadValuesFromPipe()...) dependencies := cmd.NewDependencies(f, c) - opts := NewWaitOps(dependencies, taskIDs, timeout, pollInterval, cancelOnTimeout, showProgress) + opts := NewWaitOps(dependencies, taskIDs, timeout, pollInterval, cancelOnTimeout, showProgress, c) return WaitRun(opts) }, @@ -115,7 +128,14 @@ func WaitRun(opts *WaitOptions) error { failedTaskIDs = append(failedTaskIDs, t.ID) } - formatter.PrintTaskInfo(t) + outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) + if opts.Command.Flags().Changed(constants.FlagOutputFormat) && + (outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable) && + (!opts.ShowProgress || (t.IsCompleted != nil && *t.IsCompleted)) { + _ = output.PrintResource(t, opts.Command, getTaskMappers()) + } else { + formatter.PrintTaskInfo(t) + } } if len(pendingTaskIDs) == 0 { @@ -156,7 +176,15 @@ func WaitRun(opts *WaitOptions) error { if t.FinishedSuccessfully != nil && !*t.FinishedSuccessfully { failedTaskIDs = append(failedTaskIDs, t.ID) } - formatter.PrintTaskInfo(t) + + outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) + if opts.Command.Flags().Changed(constants.FlagOutputFormat) && + (outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable) { + _ = output.PrintResource(t, opts.Command, getTaskMappers()) + } else { + formatter.PrintTaskInfo(t) + } + pendingTaskIDs = removeTaskID(pendingTaskIDs, t.ID) } } @@ -235,3 +263,51 @@ func removeTaskID(taskIDs []string, taskID string) []string { } return taskIDs } + +func getTaskMappers() output.Mappers[*tasks.Task] { + return output.Mappers[*tasks.Task]{ + Json: func(task *tasks.Task) any { + var duration string + if task.StartTime != nil && task.CompletedTime != nil { + duration = task.CompletedTime.Sub(*task.StartTime).Round(time.Second).String() + } + return TaskAsJson{ + Id: task.ID, + Description: task.Description, + State: task.State, + StartTime: task.StartTime, + CompletedTime: task.CompletedTime, + Duration: duration, + FinishedSuccessfully: task.FinishedSuccessfully, + } + }, + Table: output.TableDefinition[*tasks.Task]{ + Header: []string{"ID", "DESCRIPTION", "STATE", "STARTED", "COMPLETED", "DURATION"}, + Row: func(task *tasks.Task) []string { + var startTime, completedTime, duration string + if task.StartTime != nil { + startTime = task.StartTime.Format("02-01-2006 15:04:05") + } + if task.CompletedTime != nil { + completedTime = task.CompletedTime.Format("02-01-2006 15:04:05") + } + if task.StartTime != nil && task.CompletedTime != nil { + duration = task.CompletedTime.Sub(*task.StartTime).Round(time.Second).String() + } + + state := task.State + switch task.State { + case "Failed", "TimedOut": + state = output.Red(task.State) + case "Success": + state = output.Green(task.State) + case "Queued", "Executing", "Cancelling", "Canceled": + state = output.Yellow(task.State) + } + + return []string{task.ID, task.Description, state, startTime, completedTime, duration} + }, + }, + Basic: nil, // Not used - we use the existing formatter for basic output + } +} From 16894d2ea7c7d460490abf7db85ddec386201f9b Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 6 Aug 2025 17:12:25 +1000 Subject: [PATCH 2/5] enhance table format --- pkg/cmd/task/wait/wait.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/task/wait/wait.go b/pkg/cmd/task/wait/wait.go index ba725040..bed7534a 100644 --- a/pkg/cmd/task/wait/wait.go +++ b/pkg/cmd/task/wait/wait.go @@ -119,6 +119,7 @@ func WaitRun(opts *WaitOptions) error { pendingTaskIDs := make([]string, 0) failedTaskIDs := make([]string, 0) formatter := NewTaskOutputFormatter(opts.Out) + tableHeaderPrinted := false for _, t := range serverTasks { if t.IsCompleted == nil || !*t.IsCompleted { @@ -132,7 +133,16 @@ func WaitRun(opts *WaitOptions) error { if opts.Command.Flags().Changed(constants.FlagOutputFormat) && (outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable) && (!opts.ShowProgress || (t.IsCompleted != nil && *t.IsCompleted)) { - _ = output.PrintResource(t, opts.Command, getTaskMappers()) + if outputFormat == constants.OutputFormatJson { + _ = output.PrintResource(t, opts.Command, getTaskMappers()) + } else if outputFormat == constants.OutputFormatTable { + mappers := getTaskMappers() + if tableHeaderPrinted { + mappers.Table.Header = nil // Don't print header for subsequent updates + } + _ = output.PrintResource(t, opts.Command, mappers) + tableHeaderPrinted = true + } } else { formatter.PrintTaskInfo(t) } @@ -180,7 +190,16 @@ func WaitRun(opts *WaitOptions) error { outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) if opts.Command.Flags().Changed(constants.FlagOutputFormat) && (outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable) { - _ = output.PrintResource(t, opts.Command, getTaskMappers()) + if outputFormat == constants.OutputFormatJson { + _ = output.PrintResource(t, opts.Command, getTaskMappers()) + } else if outputFormat == constants.OutputFormatTable { + mappers := getTaskMappers() + if tableHeaderPrinted { + mappers.Table.Header = nil // Don't print header for subsequent updates + } + _ = output.PrintResource(t, opts.Command, mappers) + tableHeaderPrinted = true + } } else { formatter.PrintTaskInfo(t) } From 7b809c2fc04489643c1590df00b89e5d2d412edb Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 7 Aug 2025 10:16:57 +1000 Subject: [PATCH 3/5] fix test fail --- pkg/cmd/task/wait/wait.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/task/wait/wait.go b/pkg/cmd/task/wait/wait.go index bed7534a..fa0e40f9 100644 --- a/pkg/cmd/task/wait/wait.go +++ b/pkg/cmd/task/wait/wait.go @@ -129,8 +129,11 @@ func WaitRun(opts *WaitOptions) error { failedTaskIDs = append(failedTaskIDs, t.ID) } - outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) - if opts.Command.Flags().Changed(constants.FlagOutputFormat) && + outputFormat := "" + if opts.Command != nil { + outputFormat, _ = opts.Command.Flags().GetString(constants.FlagOutputFormat) + } + if opts.Command != nil && opts.Command.Flags().Changed(constants.FlagOutputFormat) && (outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable) && (!opts.ShowProgress || (t.IsCompleted != nil && *t.IsCompleted)) { if outputFormat == constants.OutputFormatJson { @@ -187,8 +190,11 @@ func WaitRun(opts *WaitOptions) error { failedTaskIDs = append(failedTaskIDs, t.ID) } - outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) - if opts.Command.Flags().Changed(constants.FlagOutputFormat) && + outputFormat := "" + if opts.Command != nil { + outputFormat, _ = opts.Command.Flags().GetString(constants.FlagOutputFormat) + } + if opts.Command != nil && opts.Command.Flags().Changed(constants.FlagOutputFormat) && (outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable) { if outputFormat == constants.OutputFormatJson { _ = output.PrintResource(t, opts.Command, getTaskMappers()) From babfbcdad57e690f982d4f7428a8091d380e1ec8 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 7 Aug 2025 10:37:45 +1000 Subject: [PATCH 4/5] refactor --- pkg/cmd/task/wait/wait.go | 67 ++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/pkg/cmd/task/wait/wait.go b/pkg/cmd/task/wait/wait.go index fa0e40f9..79b7b5ad 100644 --- a/pkg/cmd/task/wait/wait.go +++ b/pkg/cmd/task/wait/wait.go @@ -129,24 +129,10 @@ func WaitRun(opts *WaitOptions) error { failedTaskIDs = append(failedTaskIDs, t.ID) } - outputFormat := "" - if opts.Command != nil { - outputFormat, _ = opts.Command.Flags().GetString(constants.FlagOutputFormat) - } - if opts.Command != nil && opts.Command.Flags().Changed(constants.FlagOutputFormat) && - (outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable) && - (!opts.ShowProgress || (t.IsCompleted != nil && *t.IsCompleted)) { - if outputFormat == constants.OutputFormatJson { - _ = output.PrintResource(t, opts.Command, getTaskMappers()) - } else if outputFormat == constants.OutputFormatTable { - mappers := getTaskMappers() - if tableHeaderPrinted { - mappers.Table.Header = nil // Don't print header for subsequent updates - } - _ = output.PrintResource(t, opts.Command, mappers) - tableHeaderPrinted = true - } + if shouldUseCustomOutputFormat(opts, t) { + printTaskWithCustomFormat(opts, t, &tableHeaderPrinted) } else { + // Use existing formatter for default, basic, and progress display formatter.PrintTaskInfo(t) } } @@ -190,23 +176,10 @@ func WaitRun(opts *WaitOptions) error { failedTaskIDs = append(failedTaskIDs, t.ID) } - outputFormat := "" - if opts.Command != nil { - outputFormat, _ = opts.Command.Flags().GetString(constants.FlagOutputFormat) - } - if opts.Command != nil && opts.Command.Flags().Changed(constants.FlagOutputFormat) && - (outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable) { - if outputFormat == constants.OutputFormatJson { - _ = output.PrintResource(t, opts.Command, getTaskMappers()) - } else if outputFormat == constants.OutputFormatTable { - mappers := getTaskMappers() - if tableHeaderPrinted { - mappers.Table.Header = nil // Don't print header for subsequent updates - } - _ = output.PrintResource(t, opts.Command, mappers) - tableHeaderPrinted = true - } + if shouldUseCustomOutputFormat(opts, t) { + printTaskWithCustomFormat(opts, t, &tableHeaderPrinted) } else { + // Use existing formatter for default, basic, and progress display formatter.PrintTaskInfo(t) } @@ -336,3 +309,31 @@ func getTaskMappers() output.Mappers[*tasks.Task] { Basic: nil, // Not used - we use the existing formatter for basic output } } + +func shouldUseCustomOutputFormat(opts *WaitOptions, t *tasks.Task) bool { + if opts.Command == nil { + return false + } + + outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) + isFormatSpecified := opts.Command.Flags().Changed(constants.FlagOutputFormat) + isJsonOrTable := outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable + isTaskReady := !opts.ShowProgress || (t.IsCompleted != nil && *t.IsCompleted) + + return isFormatSpecified && isJsonOrTable && isTaskReady +} + +func printTaskWithCustomFormat(opts *WaitOptions, t *tasks.Task, tableHeaderPrinted *bool) { + outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) + + if outputFormat == constants.OutputFormatJson { + _ = output.PrintResource(t, opts.Command, getTaskMappers()) + } else if outputFormat == constants.OutputFormatTable { + mappers := getTaskMappers() + if *tableHeaderPrinted { + mappers.Table.Header = nil // Don't print header for subsequent updates + } + _ = output.PrintResource(t, opts.Command, mappers) + *tableHeaderPrinted = true + } +} From 2faf88f757de82cb4c27fa14f53b1ae864012f0a Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 7 Aug 2025 10:42:14 +1000 Subject: [PATCH 5/5] tidy --- pkg/cmd/task/wait/wait.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/task/wait/wait.go b/pkg/cmd/task/wait/wait.go index 79b7b5ad..069fb595 100644 --- a/pkg/cmd/task/wait/wait.go +++ b/pkg/cmd/task/wait/wait.go @@ -132,7 +132,6 @@ func WaitRun(opts *WaitOptions) error { if shouldUseCustomOutputFormat(opts, t) { printTaskWithCustomFormat(opts, t, &tableHeaderPrinted) } else { - // Use existing formatter for default, basic, and progress display formatter.PrintTaskInfo(t) } } @@ -179,7 +178,6 @@ func WaitRun(opts *WaitOptions) error { if shouldUseCustomOutputFormat(opts, t) { printTaskWithCustomFormat(opts, t, &tableHeaderPrinted) } else { - // Use existing formatter for default, basic, and progress display formatter.PrintTaskInfo(t) } @@ -314,18 +312,18 @@ func shouldUseCustomOutputFormat(opts *WaitOptions, t *tasks.Task) bool { if opts.Command == nil { return false } - + outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) isFormatSpecified := opts.Command.Flags().Changed(constants.FlagOutputFormat) isJsonOrTable := outputFormat == constants.OutputFormatJson || outputFormat == constants.OutputFormatTable isTaskReady := !opts.ShowProgress || (t.IsCompleted != nil && *t.IsCompleted) - + return isFormatSpecified && isJsonOrTable && isTaskReady } func printTaskWithCustomFormat(opts *WaitOptions, t *tasks.Task, tableHeaderPrinted *bool) { outputFormat, _ := opts.Command.Flags().GetString(constants.FlagOutputFormat) - + if outputFormat == constants.OutputFormatJson { _ = output.PrintResource(t, opts.Command, getTaskMappers()) } else if outputFormat == constants.OutputFormatTable {