From 622b6b4a509000f097cfab90d3e0690e3f5d0de0 Mon Sep 17 00:00:00 2001 From: David Porter Date: Tue, 21 Apr 2026 23:45:38 -0700 Subject: [PATCH] Adds a describe command for activities --- go.mod | 2 + go.sum | 4 +- internal/temporalcli/commands.activity.go | 82 +++++++++++++++++++++++ internal/temporalcli/commands.gen.go | 35 ++++++++++ 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 252629666..b88e2185f 100644 --- a/go.mod +++ b/go.mod @@ -216,3 +216,5 @@ require ( sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) + +replace go.temporal.io/api => github.com/davidporter-id-au/api-go v0.0.0-20260422011909-c01e522f0b3b diff --git a/go.sum b/go.sum index 1c3572ce4..40dcb1cbd 100644 --- a/go.sum +++ b/go.sum @@ -123,6 +123,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidporter-id-au/api-go v0.0.0-20260422011909-c01e522f0b3b h1:ym7Cw33V9pqPnzcNCvePGtVgsfGH04NWb212YNmr+vo= +github.com/davidporter-id-au/api-go v0.0.0-20260422011909-c01e522f0b3b/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= github.com/dgryski/go-farm v0.0.0-20140601200337-fc41e106ee0e/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -450,8 +452,6 @@ go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZY go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= -go.temporal.io/api v1.62.8 h1:g8RAZmdebYODoNa2GLA4M4TsXNe1096WV3n26C4+fdw= -go.temporal.io/api v1.62.8/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2 h1:1hKeH3GyR6YD6LKMHGCZ76t6h1Sgha0hXVQBxWi3dlQ= go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2/go.mod h1:T8dnzVPeO+gaUTj9eDgm/lT2lZH4+JXNvrGaQGyVi50= go.temporal.io/sdk v1.41.1 h1:yOpvsHyDD1lNuwlGBv/SUodCPhjv9nDeC9lLHW/fJUA= diff --git a/internal/temporalcli/commands.activity.go b/internal/temporalcli/commands.activity.go index 0ab6cf305..52cdc0e10 100644 --- a/internal/temporalcli/commands.activity.go +++ b/internal/temporalcli/commands.activity.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/fatih/color" "github.com/temporalio/cli/internal/printer" activitypb "go.temporal.io/api/activity/v1" "go.temporal.io/api/batch/v1" @@ -328,6 +329,87 @@ func (c *TemporalActivityUnpauseCommand) run(cctx *CommandContext, args []string return nil } +func (c *TemporalActivityDescribeCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + resp, err := cl.WorkflowService().DescribeActivityExecution(cctx, &workflowservice.DescribeActivityExecutionRequest{ + Namespace: c.Parent.Namespace, + ActivityId: c.ActivityId, + RunId: c.ActivityRunId, + IncludeInput: c.IncludeInput, + IncludeOutcome: c.IncludeOutcome, + }) + if err != nil { + return fmt.Errorf("unable to describe Activity: %w", err) + } + + // JSON output: emit the raw proto response + if cctx.JSONOutput { + return cctx.Printer.PrintStructured(resp, printer.StructuredOptions{}) + } + + // Text output: card-style sections + info := resp.Info + cctx.Printer.Println(color.MagentaString("Activity Info:")) + _ = cctx.Printer.PrintStructured(struct { + ActivityId string + RunId string + Type string + Status string + RunState string `cli:",cardOmitEmpty"` + TaskQueue string `cli:",cardOmitEmpty"` + Attempt int32 + ScheduleTime time.Time `cli:",cardOmitEmpty"` + LastStartedTime time.Time `cli:",cardOmitEmpty"` + LastHeartbeatTime time.Time `cli:",cardOmitEmpty"` + CloseTime time.Time `cli:",cardOmitEmpty"` + ExpirationTime time.Time `cli:",cardOmitEmpty"` + ScheduleToCloseTimeout time.Duration `cli:",cardOmitEmpty"` + StartToCloseTimeout time.Duration `cli:",cardOmitEmpty"` + HeartbeatTimeout time.Duration `cli:",cardOmitEmpty"` + LastWorkerIdentity string `cli:",cardOmitEmpty"` + CanceledReason string `cli:",cardOmitEmpty"` + StateTransitionCount int64 `cli:",cardOmitEmpty"` + }{ + ActivityId: info.GetActivityId(), + RunId: resp.GetRunId(), + Type: info.GetActivityType().GetName(), + Status: info.GetStatus().String(), + RunState: info.GetRunState().String(), + TaskQueue: info.GetTaskQueue(), + Attempt: info.GetAttempt(), + ScheduleTime: timestampToTime(info.GetScheduleTime()), + LastStartedTime: timestampToTime(info.GetLastStartedTime()), + LastHeartbeatTime: timestampToTime(info.GetLastHeartbeatTime()), + CloseTime: timestampToTime(info.GetCloseTime()), + ExpirationTime: timestampToTime(info.GetExpirationTime()), + ScheduleToCloseTimeout: info.GetScheduleToCloseTimeout().AsDuration(), + StartToCloseTimeout: info.GetStartToCloseTimeout().AsDuration(), + HeartbeatTimeout: info.GetHeartbeatTimeout().AsDuration(), + LastWorkerIdentity: info.GetLastWorkerIdentity(), + CanceledReason: info.GetCanceledReason(), + StateTransitionCount: info.GetStateTransitionCount(), + }, printer.StructuredOptions{}) + + if resp.Input != nil { + cctx.Printer.Println() + cctx.Printer.Println(color.MagentaString("Input:")) + _ = cctx.Printer.PrintStructured(resp.Input, printer.StructuredOptions{}) + } + + if resp.Outcome != nil { + cctx.Printer.Println() + cctx.Printer.Println(color.MagentaString("Outcome:")) + _ = cctx.Printer.PrintStructured(resp.Outcome, printer.StructuredOptions{}) + } + + return nil +} + func (c *TemporalActivityResetCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.ClientOptions) if err != nil { diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index ad01d3083..b5cfb61d0 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -418,6 +418,7 @@ func NewTemporalActivityCommand(cctx *CommandContext, parent *TemporalCommand) * } s.Command.Args = cobra.NoArgs s.Command.AddCommand(&NewTemporalActivityCompleteCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalActivityDescribeCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalActivityFailCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalActivityPauseCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalActivityResetCommand(cctx, &s).Command) @@ -460,6 +461,40 @@ func NewTemporalActivityCompleteCommand(cctx *CommandContext, parent *TemporalAc return &s } +type TemporalActivityDescribeCommand struct { + Parent *TemporalActivityCommand + Command cobra.Command + ActivityId string + ActivityRunId string + IncludeInput bool + IncludeOutcome bool +} + +func NewTemporalActivityDescribeCommand(cctx *CommandContext, parent *TemporalActivityCommand) *TemporalActivityDescribeCommand { + var s TemporalActivityDescribeCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "describe [flags]" + s.Command.Short = "Describe an Activity Execution" + if hasHighlighting { + s.Command.Long = "Retrieve details about an Activity Execution, including its status, timing,\nand retry information:\n\n\x1b[1mtemporal activity describe \\\n --activity-id YourActivityId\x1b[0m\n\nTo include the input passed to the activity:\n\n\x1b[1mtemporal activity describe \\\n --activity-id YourActivityId \\\n --include-input\x1b[0m\n\nTo include the outcome (result or failure) if the activity has completed:\n\n\x1b[1mtemporal activity describe \\\n --activity-id YourActivityId \\\n --include-outcome\x1b[0m" + } else { + s.Command.Long = "Retrieve details about an Activity Execution, including its status, timing,\nand retry information:\n\n```\ntemporal activity describe \\\n --activity-id YourActivityId\n```\n\nTo include the input passed to the activity:\n\n```\ntemporal activity describe \\\n --activity-id YourActivityId \\\n --include-input\n```\n\nTo include the outcome (result or failure) if the activity has completed:\n\n```\ntemporal activity describe \\\n --activity-id YourActivityId \\\n --include-outcome\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVarP(&s.ActivityId, "activity-id", "a", "", "Activity ID. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "activity-id") + s.Command.Flags().StringVar(&s.ActivityRunId, "activity-run-id", "", "Activity Run ID. If not specified, targets the latest run.") + s.Command.Flags().BoolVar(&s.IncludeInput, "include-input", false, "Include the activity input in the response.") + s.Command.Flags().BoolVar(&s.IncludeOutcome, "include-outcome", false, "Include the activity outcome (result/failure) in the response if completed.") + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type TemporalActivityFailCommand struct { Parent *TemporalActivityCommand Command cobra.Command