From 197f3bbee8ec992bc5b368462fdb8215e944627b Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:00:22 -0500 Subject: [PATCH 01/23] chore: pin alpine docker image to 3.23.4 (#1015) ## Summary - Pin Alpine base image from `3.23` (minor) to `3.23.4` (patch) for reproducible builds (cherry picked from commit 4d183ea22d83508ccf0e4341f05e3a35de6642e5) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8b19020af..5b679d63a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.23 +FROM alpine:3.23.4 ARG TARGETARCH From 59733145b6ba26727091027761d5377c56557f67 Mon Sep 17 00:00:00 2001 From: Kevin Woo <3469532+kevinawoo@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:35:21 -0700 Subject: [PATCH 02/23] Fix help with value flags (#1006) ## Summary - avoid routing built-in help invocations through extension probing when known value flags are present - initialize Cobra's default help flag before extension preflight parses command flags - add regression coverage for `--help` combined with `--address` and `--namespace` ## Root Cause Extension preflight checked raw remaining args with a string-prefix heuristic. Values for known flags, such as `123` in `--address 123`, looked like positional extension args, so the preflight parsed `--help` before Cobra had registered the default help flag and surfaced `pflag: help requested` as a real error. Fixes #1003. ## Validation - `go run ./cmd/temporal workflow list --address 123 --help` - `go test ./internal/temporalcli -run 'TestHelp|TestExtension' -count=1` - `go test ./internal/temporalcli -count=1` --------- Co-authored-by: alex.stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 1a064e53fe0dcecfc6d0735d429912c2218531a2) --- internal/temporalcli/commands.extension.go | 23 ++++++++++++++----- .../temporalcli/commands.extension_test.go | 21 +++++++++++++++++ internal/temporalcli/commands.help_test.go | 15 ++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/internal/temporalcli/commands.extension.go b/internal/temporalcli/commands.extension.go index d46de64dc..794242263 100644 --- a/internal/temporalcli/commands.extension.go +++ b/internal/temporalcli/commands.extension.go @@ -31,12 +31,10 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo // Find the deepest matching built-in command and remaining args. foundCmd, remainingArgs, findErr := tcmd.Command.Find(cctx.Options.Args) - // Check if remaining args include positional args (not just flags). - // If not, a built-in command fully handles this - no extension needed. - hasPosArgs := slices.ContainsFunc(remainingArgs, isPosArg) - if findErr == nil && !hasPosArgs { - return nil, false - } + // Cobra normally adds --help/-h before parsing, but extension dispatch + // pre-parses flags before Cobra's execution path runs. We Initialize it so that + // help is treated as a normal CLI flag instead of surfacing as pflag.ErrHelp from flag parsing. + foundCmd.InitDefaultHelpFlag() // Group args into these lists: // - cliParseArgs: args to validate (subset of cliPassArgs) @@ -44,6 +42,12 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo // - extArgs: args to pass to extension and use for extension lookup cliParseArgs, cliPassArgs, extArgs := groupArgs(foundCmd, remainingArgs) + // Check if remaining args include positional args (not just flags). + // If not, a built-in command fully handles this - no extension needed. + if findErr == nil && !slices.ContainsFunc(extArgs, isPosArg) { + return nil, false + } + // Search for an extension executable. cmdPrefix := strings.Fields(foundCmd.CommandPath()) extPath, extArgs := lookupExtension(cmdPrefix, extArgs) @@ -95,6 +99,13 @@ func groupArgs(foundCmd *cobra.Command, args []string) (cliParseArgs, cliPassArg name, hasInline := parseFlagArg(arg) if f, takesValue := lookupFlag(foundCmd, name); f != nil { + // Help flag after positional args should go to extArgs so it + // gets forwarded to extensions (e.g. "temporal foo bar --help"). + // Before positional args it stays in cliPassArgs for Cobra to handle. + if f.Name == "help" && seenPos { + extArgs = append(extArgs, arg) + continue + } // Known CLI flag: goes to cliPassArgs. // Flags in cliArgsToParseForExtension also go to cliParseArgs. shouldParse := cliArgsToParseForExtension[f.Name] diff --git a/internal/temporalcli/commands.extension_test.go b/internal/temporalcli/commands.extension_test.go index 46ab15c49..266a8fcd1 100644 --- a/internal/temporalcli/commands.extension_test.go +++ b/internal/temporalcli/commands.extension_test.go @@ -88,6 +88,19 @@ func TestExtension_DoesNotOverrideBuiltinCommand(t *testing.T) { res := h.Execute("workflow", "list", "--help") assert.Contains(t, res.Stdout.String(), "List Workflow Executions") }) + + t.Run("subcommand with value flags", func(t *testing.T) { + for _, args := range [][]string{ + {"workflow", "list", "--address", "123", "--help"}, + {"workflow", "list", "--help", "--address", "123"}, + {"workflow", "list", "--namespace", "foo", "--help"}, + } { + res := h.Execute(args...) + assert.Contains(t, res.Stdout.String(), "List Workflow Executions") + assert.NotContains(t, res.Stderr.String(), "pflag: help requested") + assert.NoError(t, res.Err) + } + }) } func TestExtension_Flags(t *testing.T) { @@ -149,6 +162,14 @@ func TestExtension_Flags(t *testing.T) { {args: "workflow diagram foo --flag value", want: "temporal-workflow-diagram-foo --flag value"}, // nested commands {args: "workflow diagram --output invalid", want: "temporal-workflow-diagram --output invalid"}, // invalid value passed through + // Help flags are forwarded to extensions, not handled locally. + + {args: "foo --help", want: "temporal-foo --help"}, + {args: "foo -h", want: "temporal-foo -h"}, + {args: "foo bar --help", want: "temporal-foo-bar --help"}, // most specific extension wins + {args: "workflow diagram --help", want: "temporal-workflow-diagram --help"}, + {args: "workflow diagram -h", want: "temporal-workflow-diagram -h"}, + // Note: Flag aliases are already implicitly tested via other command-specific tests. } diff --git a/internal/temporalcli/commands.help_test.go b/internal/temporalcli/commands.help_test.go index ebf6757cd..b3cd10c00 100644 --- a/internal/temporalcli/commands.help_test.go +++ b/internal/temporalcli/commands.help_test.go @@ -29,6 +29,21 @@ func TestHelp_Subcommand(t *testing.T) { assert.NoError(t, res.Err) } +func TestHelp_WithValueFlag(t *testing.T) { + h := NewCommandHarness(t) + + for _, args := range [][]string{ + {"workflow", "list", "--address", "123", "--help"}, + {"workflow", "list", "--help", "--address", "123"}, + {"workflow", "list", "--namespace", "foo", "--help"}, + } { + res := h.Execute(args...) + assert.Contains(t, res.Stdout.String(), "List Workflow Executions.") + assert.NotContains(t, res.Stderr.String(), "pflag: help requested") + assert.NoError(t, res.Err) + } +} + func TestHelp_HelpShowsAllFlag(t *testing.T) { h := NewCommandHarness(t) res := h.Execute("help", "--help") From 13d21da03593a7977da21f936595b5559ee3d424 Mon Sep 17 00:00:00 2001 From: Kevin Woo <3469532+kevinawoo@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:44:51 -0700 Subject: [PATCH 03/23] Fix flakey test by disabling EC2 metadata lookup in test case (#1005) ## Summary - Disable EC2 IMDS lookups inside `TestCreateWorkerDeploymentVersion_Errors`. - Keep the fake AWS Lambda validation failure deterministic and local. ## Root Cause The test intentionally passes fake AWS Lambda and assume-role inputs. Server-side worker-deployment validation uses `go.temporal.io/auto-scaled-workers`, which imports the AWS SDK and validates the `aws-lambda` compute provider. That path builds default AWS config and calls STS/Lambda validation. Before STS can assume the fake role, the AWS SDK needs base credentials. In a local or CI environment with no AWS env creds/profile/container creds, the AWS SDK falls back to EC2 Instance Metadata Service. Outside EC2, that means waiting on IMDS/network retries even though the test only wants an expected validation failure. ## Scope of the Fix This is broader AWS SDK behavior: `AWS_EC2_METADATA_DISABLED=true` is useful for any test/process that invokes AWS SDK default credential resolution without real credentials and should not hit EC2 IMDS. In this CLI test suite, the enabled surface is narrow: - `TestCreateWorkerDeploymentVersion_Errors` is the only enabled test that passes a complete fake AWS Lambda compute config to the server. - The later missing-field subcases in that same test fail in CLI-side validation before server-side AWS validation. - `TestCreateWorkerDeploymentVersion_LambdaComputeConfig` would also hit the AWS path, but it is skipped because it needs real AWS fixtures. So the fix is intentionally test-local rather than package-wide, to avoid changing behavior for future tests that might intentionally exercise AWS credential resolution. ## Validation - `go test -mod=readonly ./internal/temporalcli -run 'TestSharedServerSuite/TestCreateWorkerDeploymentVersion_Errors' -count=1 -v` Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 569da5e34e76d152dc5a3c4b0a08f1ef458cd7d8) --- internal/temporalcli/commands.worker.deployment_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/temporalcli/commands.worker.deployment_test.go b/internal/temporalcli/commands.worker.deployment_test.go index a3963dd23..8b97854e3 100644 --- a/internal/temporalcli/commands.worker.deployment_test.go +++ b/internal/temporalcli/commands.worker.deployment_test.go @@ -1199,6 +1199,10 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_EmptyComputeConfig } func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { + // Keep the fake AWS validation case local; otherwise the SDK tries EC2 IMDS + // during credential resolution and waits on network retries. + s.T().Setenv("AWS_EC2_METADATA_DISABLED", "true") + deploymentName := uuid.NewString() taskQueue := uuid.NewString() From 039a4d01c8ae291d2ddb33b9ce9a17308bcbc32e Mon Sep 17 00:00:00 2001 From: Rodrigo Zhou Date: Fri, 1 May 2026 16:39:20 -0700 Subject: [PATCH 04/23] Sort output of listing search attributes (#1016) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What was changed Sort output of listing search attributes ## Why? Easier to check the list than viewing a random order of search attributes. ## Checklist 1. Closes 2. How was this tested: 3. Any docs updates needed? (cherry picked from commit 3f779e1fb840335166c7a14777d374b80ca06650) --- .../temporalcli/commands.operator_search_attribute.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/temporalcli/commands.operator_search_attribute.go b/internal/temporalcli/commands.operator_search_attribute.go index aa0df7e38..b1919d9eb 100644 --- a/internal/temporalcli/commands.operator_search_attribute.go +++ b/internal/temporalcli/commands.operator_search_attribute.go @@ -2,6 +2,7 @@ package temporalcli import ( "fmt" + "slices" "strings" "github.com/fatih/color" @@ -130,12 +131,17 @@ func (c *TemporalOperatorSearchAttributeListCommand) run(cctx *CommandContext, a Type: saType.String(), }) } + slices.SortFunc(sas, func(a, b saNameType) int { return strings.Compare(a.Name, b.Name) }) + + var csa []saNameType for saName, saType := range resp.CustomAttributes { - sas = append(sas, saNameType{ + csa = append(csa, saNameType{ Name: saName, Type: saType.String(), }) } + slices.SortFunc(csa, func(a, b saNameType) int { return strings.Compare(a.Name, b.Name) }) + sas = append(sas, csa...) return cctx.Printer.PrintStructured(sas, printer.StructuredOptions{Table: &printer.TableOptions{}}) } From aaa7221402e2f775c2593d308784b273b4c9a996 Mon Sep 17 00:00:00 2001 From: Stephan Behnke Date: Fri, 15 May 2026 15:57:51 -0700 Subject: [PATCH 05/23] Refresh Nexus endpoints on read in dev-server (#1025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What was changed Mirrors the existing `forceSearchAttributesCacheRefreshOnRead` default: makes Nexus endpoint writes immediately visible to readers instead of after the next background long-poll refresh. It was added here https://github.com/temporalio/temporal/pull/10208 ## Why Better development experience. (cherry picked from commit fee2351cbd8ad71258775c60f453afabe1da164b) --- internal/temporalcli/commands.server.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/temporalcli/commands.server.go b/internal/temporalcli/commands.server.go index ac216423f..90c5b2fe3 100644 --- a/internal/temporalcli/commands.server.go +++ b/internal/temporalcli/commands.server.go @@ -14,10 +14,16 @@ import ( var defaultDynamicConfigValues = map[string]any{ // Make search attributes immediately visible on creation, so users don't // have to wait for eventual consistency to happen when testing against the - // dev-server. Since it's a very rare thing to create search attributes, + // dev-server. Since it's a very rare thing to create search attributes, // we're comfortable that this is very unlikely to mask bugs in user code. "system.forceSearchAttributesCacheRefreshOnRead": true, + // Make Nexus endpoints immediately visible on creation, so users don't + // have to wait for eventual consistency to happen when testing against the + // dev-server. Since it's a very rare thing to create Nexus endpoints, + // we're comfortable that this is very unlikely to mask bugs in user code. + "system.forceNexusEndpointRefreshOnRead": true, + // Since we disable the SA cache, we need to bump max QPS accordingly. // These numbers were chosen to maintain the ratio between the two that's // established in the defaults. From 9c0abe78c03fb9296dece920354bfeb362f7520d Mon Sep 17 00:00:00 2001 From: Jiechen Zhong Date: Tue, 19 May 2026 09:13:31 -0700 Subject: [PATCH 06/23] :boom: Workflow delete now prompts for confirmation (#1029) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What was changed 1. Added a warning to `temporal workflow delete` explaining that deleting Workflow Executions in a global Namespace removes them from all replicas, and that requests sent to a passive cluster are forwarded to the active cluster by default unless `--grpc-meta xdc-redirection=false` is specified. 2. Delete workflow now prompts for confirmation ## Why? The CLI should make that blast radius clear before users confirm deletion. The passive-cluster note helps users who intentionally want to target a passive cluster avoid the default frontend forwarding behavior. ## Checklist 1. Closes 2. How was this tested: unit-test and local run. single deletion ``` go run ./cmd/temporal workflow delete -w pay-retention-92728 WARNING: Deleting Workflow Executions in a global Namespace removes them from all replicas. Requests sent to a passive cluster are forwarded to the active cluster by default; to target the passive cluster directly, specify `--grpc-meta xdc-redirection=false`. Delete Workflow "pay-retention-92728"? y/N Error: user denied confirmation exit status 1 ``` batch deletion ``` go run ./cmd/temporal workflow delete --query "WorkflowId = 'pay-retention-92728'" WARNING: Deleting Workflow Executions in a global Namespace removes them from all replicas. Requests sent to a passive cluster are forwarded to the active cluster by default; to target the passive cluster directly, specify `--grpc-meta xdc-redirection=false`. Start batch against approximately 1 workflow(s)? y/N y Started batch for job ID: ddab94d3-719f-4a4a-b379-12cdd8229e36 ``` 3. Any docs updates needed? --------- Co-authored-by: Claude Opus 4.7 (1M context) (cherry picked from commit b281f4bc70fb16f51030ec63a49aa792687c99a7) --- internal/temporalcli/commands.gen.go | 4 +- internal/temporalcli/commands.workflow.go | 33 +++++++++++++-- .../temporalcli/commands.workflow_test.go | 41 +++++++++++++++++++ internal/temporalcli/commands.yaml | 7 +++- 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 08a68010e..03424647a 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -3756,9 +3756,9 @@ func NewTemporalWorkflowDeleteCommand(cctx *CommandContext, parent *TemporalWork s.Command.Use = "delete [flags]" s.Command.Short = "Remove Workflow Execution" if hasHighlighting { - s.Command.Long = "Delete a Workflow Executions and its Event History:\n\n\x1b[1mtemporal workflow delete \\\n --workflow-id YourWorkflowId\x1b[0m\n\nThe removal executes asynchronously. If the Execution is Running, the Service\nterminates it before deletion.\n\nVisit https://docs.temporal.io/visibility to read more about Search Attributes\nand Query creation. See \x1b[1mtemporal batch --help\x1b[0m for a quick reference." + s.Command.Long = "Delete a Workflow Execution and its Event History:\n\n\x1b[1mtemporal workflow delete \\\n --workflow-id YourWorkflowId\x1b[0m\n\nThe removal executes asynchronously. If the Execution is Running, the Service\nterminates it before deletion.\n\nWARNING: Deleting Workflow Executions in a global Namespace removes them from\nall replicas. Requests sent to a passive cluster are forwarded to the active\ncluster by default; to target the passive cluster directly, specify\n\x1b[1m--grpc-meta xdc-redirection=false\x1b[0m.\n\nVisit https://docs.temporal.io/visibility to read more about Search Attributes\nand Query creation. See \x1b[1mtemporal batch --help\x1b[0m for a quick reference." } else { - s.Command.Long = "Delete a Workflow Executions and its Event History:\n\n```\ntemporal workflow delete \\\n --workflow-id YourWorkflowId\n```\n\nThe removal executes asynchronously. If the Execution is Running, the Service\nterminates it before deletion.\n\nVisit https://docs.temporal.io/visibility to read more about Search Attributes\nand Query creation. See `temporal batch --help` for a quick reference." + s.Command.Long = "Delete a Workflow Execution and its Event History:\n\n```\ntemporal workflow delete \\\n --workflow-id YourWorkflowId\n```\n\nThe removal executes asynchronously. If the Execution is Running, the Service\nterminates it before deletion.\n\nWARNING: Deleting Workflow Executions in a global Namespace removes them from\nall replicas. Requests sent to a passive cluster are forwarded to the active\ncluster by default; to target the passive cluster directly, specify\n`--grpc-meta xdc-redirection=false`.\n\nVisit https://docs.temporal.io/visibility to read more about Search Attributes\nand Query creation. See `temporal batch --help` for a quick reference." } s.Command.Args = cobra.NoArgs s.SingleWorkflowOrBatchOptions.BuildFlags(s.Command.Flags()) diff --git a/internal/temporalcli/commands.workflow.go b/internal/temporalcli/commands.workflow.go index 5a98d6ffe..20ebec8d1 100644 --- a/internal/temporalcli/commands.workflow.go +++ b/internal/temporalcli/commands.workflow.go @@ -30,6 +30,8 @@ import ( const metadataQueryName = "__temporal_workflow_metadata" +const workflowDeleteWarning = "WARNING: Deleting Workflow Executions in a global Namespace removes them from all replicas. Requests sent to a passive cluster are forwarded to the active cluster by default; to target the passive cluster directly, specify `--grpc-meta xdc-redirection=false`." + func (c *TemporalWorkflowCancelCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.ClientOptions) if err != nil { @@ -69,13 +71,29 @@ func (c *TemporalWorkflowDeleteCommand) run(cctx *CommandContext, args []string) } defer cl.Close() - exec, batchReq, err := c.workflowExecOrBatch(cctx, c.Parent.Namespace, cl, singleOrBatchOverrides{}) + // Only warn when the namespace is global. + nsResp, nsErr := cl.WorkflowService().DescribeNamespace(cctx, &workflowservice.DescribeNamespaceRequest{ + Namespace: c.Parent.Namespace, + }) + if nsErr != nil || nsResp.GetIsGlobalNamespace() { + fmt.Fprintln(cctx.Options.Stderr, workflowDeleteWarning) + } + + exec, batchReq, err := c.workflowExecOrBatch(cctx, c.Parent.Namespace, cl, singleOrBatchOverrides{ + AllowYesWithWorkflowID: true, + }) // Run single or batch if err != nil { return err } else if exec != nil { - _, err := cl.WorkflowService().DeleteWorkflowExecution(cctx, &workflowservice.DeleteWorkflowExecutionRequest{ + yes, err := cctx.promptYes(workflowDeleteSingleConfirmationMessage(exec), c.Yes) + if err != nil { + return err + } else if !yes { + return fmt.Errorf("user denied confirmation") + } + _, err = cl.WorkflowService().DeleteWorkflowExecution(cctx, &workflowservice.DeleteWorkflowExecutionRequest{ Namespace: c.Parent.Namespace, WorkflowExecution: &common.WorkflowExecution{WorkflowId: c.WorkflowId, RunId: c.RunId}, }) @@ -96,6 +114,14 @@ func (c *TemporalWorkflowDeleteCommand) run(cctx *CommandContext, args []string) return nil } +func workflowDeleteSingleConfirmationMessage(exec *common.WorkflowExecution) string { + action := fmt.Sprintf("Delete Workflow %q", exec.GetWorkflowId()) + if exec.GetRunId() != "" { + action += fmt.Sprintf(" with Run ID %q", exec.GetRunId()) + } + return fmt.Sprintf("%s? y/N", action) +} + func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.ClientOptions) if err != nil { @@ -511,6 +537,7 @@ func defaultReason() string { type singleOrBatchOverrides struct { AllowReasonWithWorkflowID bool + AllowYesWithWorkflowID bool } func (s *SingleWorkflowOrBatchOptions) workflowExecOrBatch( @@ -525,7 +552,7 @@ func (s *SingleWorkflowOrBatchOptions) workflowExecOrBatch( return nil, nil, fmt.Errorf("cannot set query when workflow ID is set") } else if s.Reason != "" && !overrides.AllowReasonWithWorkflowID { return nil, nil, fmt.Errorf("cannot set reason when workflow ID is set") - } else if s.Yes { + } else if s.Yes && !overrides.AllowYesWithWorkflowID { return nil, nil, fmt.Errorf("cannot set 'yes' when workflow ID is set") } else if s.Rps != 0 { return nil, nil, fmt.Errorf("cannot set rps when workflow ID is set") diff --git a/internal/temporalcli/commands.workflow_test.go b/internal/temporalcli/commands.workflow_test.go index 5a11f3627..20d6d256b 100644 --- a/internal/temporalcli/commands.workflow_test.go +++ b/internal/temporalcli/commands.workflow_test.go @@ -169,6 +169,44 @@ func (s *SharedServerSuite) testSignalBatchWorkflow(json bool) *CommandResult { return res } +func (s *SharedServerSuite) TestWorkflow_Delete_SingleWorkflowRequiresConfirmation() { + res := s.Execute( + "workflow", "delete", + "--address", s.Address(), + "--workflow-id", "delete-confirmation-test", + ) + s.EqualError(res.Err, "user denied confirmation") + // Dev-server's default namespace is non-global, so the warning is suppressed. + s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") + s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") +} + +func (s *SharedServerSuite) TestWorkflow_Delete_SingleWorkflowSuccess() { + s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { + ctx.Done().Receive(ctx, nil) + return nil, ctx.Err() + }) + + run, err := s.Client.ExecuteWorkflow( + s.Context, + client.StartWorkflowOptions{TaskQueue: s.Worker().Options.TaskQueue}, + DevWorkflow, + "ignored", + ) + s.NoError(err) + + res := s.Execute( + "workflow", "delete", + "--address", s.Address(), + "--workflow-id", run.GetID(), + "--yes", + ) + s.NoError(res.Err) + s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") + s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") + s.Contains(res.Stdout.String(), "Delete workflow succeeded") +} + func (s *SharedServerSuite) TestWorkflow_Delete_BatchWorkflowSuccess() { s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { ctx.Done().Receive(ctx, nil) @@ -212,6 +250,9 @@ func (s *SharedServerSuite) TestWorkflow_Delete_BatchWorkflowSuccess() { "-y", ) s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Start batch against approximately 2 workflow(s)") + s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") + s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") // Confirm workflows were deleted s.Eventually(func() bool { diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 32af0d955..78bdf3513 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -3665,7 +3665,7 @@ commands: - name: temporal workflow delete summary: Remove Workflow Execution description: | - Delete a Workflow Executions and its Event History: + Delete a Workflow Execution and its Event History: ``` temporal workflow delete \ @@ -3675,6 +3675,11 @@ commands: The removal executes asynchronously. If the Execution is Running, the Service terminates it before deletion. + WARNING: Deleting Workflow Executions in a global Namespace removes them from + all replicas. Requests sent to a passive cluster are forwarded to the active + cluster by default; to target the passive cluster directly, specify + `--grpc-meta xdc-redirection=false`. + Visit https://docs.temporal.io/visibility to read more about Search Attributes and Query creation. See `temporal batch --help` for a quick reference. option-sets: From edc4c1fb90d0ea5869e7e2badd8858d22de8f1f4 Mon Sep 17 00:00:00 2001 From: Kent Gruber Date: Thu, 21 May 2026 12:48:43 -0400 Subject: [PATCH 07/23] VLN-1350: remediate missing-dependency-cooldown (#1024) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > 🏕️ This pull request was created by [camper](https://github.com/temporalio/camper), an automated security campaign tool. ## Finding
Rulemissing-dependency-cooldown
SeverityMEDIUM
Repositorytemporalio/cli
TicketVLN-1350
## Summary - `.github/dependabot.yml`: Created Dependabot configuration with `gomod` and `github-actions` update entries, each set to `schedule.interval: weekly` and `cooldown.default-days: 14`. ## Instructions - **Approve** to merge this fix - **Request changes** to trigger a new remediation attempt - `/camper rebase` — rebase onto the base branch - `/camper close` — close this PR without merging - `/camper retry` — close and retry with a new fix Co-authored-by: picatz <14850816+picatz@users.noreply.github.com> Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 7a10bc5d04524cfc3710977a59ff6f9aacc2ad25) --- .github/dependabot.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..aa2061724 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + - package-ecosystem: gomod + directory: "/" + schedule: + interval: weekly + cooldown: + default-days: 14 + + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + cooldown: + default-days: 14 From 3895e2c16e5e4cd791c02d024bd2dd4f02a910ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:48:36 -0500 Subject: [PATCH 08/23] Bump actions/upload-artifact from 4 to 7 (#1034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7.
Release notes

Sourced from actions/upload-artifact's releases.

v7.0.0

v7 What's new

Direct Uploads

Adds support for uploading single files directly (unzipped). Callers can set the new archive parameter to false to skip zipping the file during upload. Right now, we only support single files. The action will fail if the glob passed resolves to multiple files. The name parameter is also ignored with this setting. Instead, the name of the artifact will be the name of the uploaded file.

ESM

To support new versions of the @actions/* packages, we've upgraded the package to ESM.

What's Changed

New Contributors

Full Changelog: https://github.com/actions/upload-artifact/compare/v6...v7.0.0

v6.0.0

v6 - What's new

[!IMPORTANT] actions/upload-artifact@v6 now runs on Node.js 24 (runs.using: node24) and requires a minimum Actions Runner version of 2.327.1. If you are using self-hosted runners, ensure they are updated before upgrading.

Node.js 24

This release updates the runtime to Node.js 24. v5 had preliminary support for Node.js 24, however this action was by default still running on Node.js 20. Now this action by default will run on Node.js 24.

What's Changed

Full Changelog: https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0

v5.0.0

What's Changed

BREAKING CHANGE: this update supports Node v24.x. This is not a breaking change per-se but we're treating it as such.

... (truncated)

Commits
  • 043fb46 Merge pull request #797 from actions/yacaovsnc/update-dependency
  • 634250c Include changes in typespec/ts-http-runtime 0.3.5
  • e454baa Readme: bump all the example versions to v7 (#796)
  • 74fad66 Update the readme with direct upload details (#795)
  • bbbca2d Support direct file uploads (#764)
  • 589182c Upgrade the module to ESM and bump dependencies (#762)
  • 47309c9 Merge pull request #754 from actions/Link-/add-proxy-integration-tests
  • 02a8460 Add proxy integration test
  • b7c566a Merge pull request #745 from actions/upload-artifact-v6-release
  • e516bc8 docs: correct description of Node.js 24 support in README
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/upload-artifact&package-manager=github_actions&previous-version=4&new-version=7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 25551a59ea5703b8d9590a0af8603974a5cf2d72) --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 22764a5af..3a9ee87a3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -72,7 +72,7 @@ jobs: run: gotestsum --junitfile junit-xml/${{matrix.os}}.xml -- ./... - name: Upload junit-xml artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: always() with: name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--${{matrix.os}} From 21ad19ecc087c9ce4abdfe8a28c341d8b9445ead Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Tue, 26 May 2026 16:50:37 -0500 Subject: [PATCH 09/23] chore: add PR template (#1045) ## What changed? Adds a PR template to guide contributors through the checklist of CLI design principles, help text standards, and testing expectations. (cherry picked from commit c4061c4455e21b313832be1ddf974f9bc97caa81) --- .github/pull_request_template.md | 74 ++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..0b129b983 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,74 @@ +## Related issues + + + +## What changed? + + + +## Checklist + + + +**Stability** +- [ ] Breaking changes are marked with 💥 in the PR title and release notes +- [ ] Changes to JSON output (`-o json` / `-o jsonl`) are treated as breaking changes + +**Design** +- [ ] This feature does not depend on Cloud-only APIs or behavior (it works against an OSS server) +- [ ] New commands follow `temporal ` structure (e.g. `temporal workflow start`) +- [ ] New flags are named after the API concept, not the implementation mechanism (good: `--search-attribute`, bad: `--index-field`) +- [ ] New flags don't duplicate an existing flag that serves the same purpose +- [ ] New flags do not have short aliases without strong justification +- [ ] Experimental features are marked with `(Experimental)` in `commands.yaml` + +**Help text** (see style guide at the top of `commands.yaml`) +- [ ] All flags shown in help text and examples are implemented and functional +- [ ] Summaries use sentence case and have no trailing period +- [ ] Long descriptions end with a period and include at least one example invocation +- [ ] Examples use long flags (`--namespace`, not `-n`), one flag per line +- [ ] Placeholder values use `YourXxx` form (`YourWorkflowId`, `YourNamespace`) + +**Behavior** +- [ ] Results go to stdout; errors and warnings go to stderr +- [ ] Error messages are lowercase with no trailing punctuation + +**Tests** +- [ ] Added functional test(s) (`SharedServerSuite`) +- [ ] Added unit test(s) (`func TestXxx`) where applicable + +## Manual tests + + + +**Setup** +``` +temporal server start-dev --headless +temporal workflow start \ + --type YourWorkflowType \ + --task-queue YourTaskQueue \ + --workflow-id YourWorkflowId +``` + +**Happy path** +``` +$ temporal \ + --flag value + +``` + +**Error case** +``` +$ temporal \ + --invalid-combination +Error: +$ echo $? +1 +``` + +**Composition** +``` +$ temporal ... +$ temporal --flag + +``` From 8ecade98d250f94bbb00ce259a745c6ddb751bd6 Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Fri, 29 May 2026 10:11:28 -0500 Subject: [PATCH 10/23] chore: improve dependabot config (#1044) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What was changed - Group all GitHub Actions updates into a single PR - Ignore indirect Go dependencies (only direct deps get PRs) - Ignore all `go.temporal.io/*` dependencies — these are managed manually to ensure coordinated upgrades across all temporal packages (cherry picked from commit 87ea7e06f042846047ba49fe90daed47c9673396) --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index aa2061724..4ebbf0d4e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,11 @@ updates: interval: weekly cooldown: default-days: 14 + ignore: + - dependency-name: "*" + dependency-type: indirect + # Temporal dependencies are managed manually to ensure coordinated upgrades across all temporal packages + - dependency-name: "go.temporal.io/*" - package-ecosystem: github-actions directory: "/" @@ -13,3 +18,7 @@ updates: interval: weekly cooldown: default-days: 14 + groups: + github-actions: + patterns: + - "*" From 4dbfdbea51f22aa05fd2de5c357995fd5c555b68 Mon Sep 17 00:00:00 2001 From: John Votta Date: Fri, 29 May 2026 09:16:50 -0700 Subject: [PATCH 11/23] Fix task-queue config set help: use real fairness weight flag names (#1056) The `temporal task-queue config set` help text referenced three flags that don't exist: - `--fairness-key-weight-set =` - `--fairness-key-weight-unset ` - `--fairness-key-weight-unset-all` The real flags, defined a few lines below in the same YAML block, are: - `--fairness-key-weight =` (with `=default` to unset a single key) - `--fairness-key-weight-clear-all` This PR updates the example block and the unset instructions in `commands.yaml` to match, and regenerates `commands.gen.go`. Verified locally with `go run ./cmd/temporal task-queue config set --help`. This bug also surfaces in the auto-generated docs site at [docs/cli/task-queue.mdx](https://docs.temporal.io/cli/task-queue#set), which a customer hit in [this Slack thread](https://temporaltechnologies.slack.com/archives/C0748KDH2DD/p1779899719317389?thread_ts=1779898503.202859&cid=C0748KDH2DD). The docs PR over in `temporalio/documentation#4625` works around it by referencing the real flag names directly. Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 2a4ea2a231f1ed86c78746658312418ed403c2f9) --- internal/temporalcli/commands.gen.go | 4 ++-- internal/temporalcli/commands.yaml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 03424647a..468900910 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -2548,9 +2548,9 @@ func NewTemporalTaskQueueConfigSetCommand(cctx *CommandContext, parent *Temporal s.Command.Use = "set [flags]" s.Command.Short = "Set Task Queue configuration" if hasHighlighting { - s.Command.Long = "Update configuration settings for a Task Queue.\n\n\x1b[1mtemporal task-queue config set \\\n --task-queue YourTaskQueue \\\n --task-queue-type activity \\\n --namespace YourNamespace \\\n --queue-rps-limit \\\n --queue-rps-limit-reason \\\n --fairness-key-rps-limit-default \\\n --fairness-key-rps-limit-reason \\\n --fairness-key-weight-set HighPriority=2.0 \\\n --fairness-key-weight-set LowPriority=0.5\x1b[0m\n\nThis command supports updating:\n- Queue rate limits: Controls the overall rate limit of the task queue.\n This setting overrides the worker rate limit if set.\n Unless modified, this is the system-defined rate limit.\n- Fairness key rate limit defaults: Sets default rate limits for fairness keys.\n If set, each individual fairness key will be limited to this rate,\n scaled by the weight of the fairness key.\n- Fairness key weight overrides: Set custom weights for specific fairness keys.\n Weights control the relative share of capacity each key receives.\n\nTo unset a rate limit, pass in 'default', for example: --queue-rps-limit default\nTo unset specific fairness weights, use --fairness-key-weight-unset \nTo unset all fairness weights, use --fairness-key-weight-unset-all" + s.Command.Long = "Update configuration settings for a Task Queue.\n\n\x1b[1mtemporal task-queue config set \\\n --task-queue YourTaskQueue \\\n --task-queue-type activity \\\n --namespace YourNamespace \\\n --queue-rps-limit \\\n --queue-rps-limit-reason \\\n --fairness-key-rps-limit-default \\\n --fairness-key-rps-limit-reason \\\n --fairness-key-weight HighPriority=2.0 \\\n --fairness-key-weight LowPriority=0.5\x1b[0m\n\nThis command supports updating:\n- Queue rate limits: Controls the overall rate limit of the task queue.\n This setting overrides the worker rate limit if set.\n Unless modified, this is the system-defined rate limit.\n- Fairness key rate limit defaults: Sets default rate limits for fairness keys.\n If set, each individual fairness key will be limited to this rate,\n scaled by the weight of the fairness key.\n- Fairness key weight overrides: Set custom weights for specific fairness keys.\n Weights control the relative share of capacity each key receives.\n\nTo unset a rate limit, pass in 'default', for example: --queue-rps-limit default\nTo unset a specific fairness weight, use --fairness-key-weight =default\nTo unset all fairness weights, use --fairness-key-weight-clear-all" } else { - s.Command.Long = "Update configuration settings for a Task Queue.\n\n```\ntemporal task-queue config set \\\n --task-queue YourTaskQueue \\\n --task-queue-type activity \\\n --namespace YourNamespace \\\n --queue-rps-limit \\\n --queue-rps-limit-reason \\\n --fairness-key-rps-limit-default \\\n --fairness-key-rps-limit-reason \\\n --fairness-key-weight-set HighPriority=2.0 \\\n --fairness-key-weight-set LowPriority=0.5\n```\n\nThis command supports updating:\n- Queue rate limits: Controls the overall rate limit of the task queue.\n This setting overrides the worker rate limit if set.\n Unless modified, this is the system-defined rate limit.\n- Fairness key rate limit defaults: Sets default rate limits for fairness keys.\n If set, each individual fairness key will be limited to this rate,\n scaled by the weight of the fairness key.\n- Fairness key weight overrides: Set custom weights for specific fairness keys.\n Weights control the relative share of capacity each key receives.\n\nTo unset a rate limit, pass in 'default', for example: --queue-rps-limit default\nTo unset specific fairness weights, use --fairness-key-weight-unset \nTo unset all fairness weights, use --fairness-key-weight-unset-all" + s.Command.Long = "Update configuration settings for a Task Queue.\n\n```\ntemporal task-queue config set \\\n --task-queue YourTaskQueue \\\n --task-queue-type activity \\\n --namespace YourNamespace \\\n --queue-rps-limit \\\n --queue-rps-limit-reason \\\n --fairness-key-rps-limit-default \\\n --fairness-key-rps-limit-reason \\\n --fairness-key-weight HighPriority=2.0 \\\n --fairness-key-weight LowPriority=0.5\n```\n\nThis command supports updating:\n- Queue rate limits: Controls the overall rate limit of the task queue.\n This setting overrides the worker rate limit if set.\n Unless modified, this is the system-defined rate limit.\n- Fairness key rate limit defaults: Sets default rate limits for fairness keys.\n If set, each individual fairness key will be limited to this rate,\n scaled by the weight of the fairness key.\n- Fairness key weight overrides: Set custom weights for specific fairness keys.\n Weights control the relative share of capacity each key receives.\n\nTo unset a rate limit, pass in 'default', for example: --queue-rps-limit default\nTo unset a specific fairness weight, use --fairness-key-weight =default\nTo unset all fairness weights, use --fairness-key-weight-clear-all" } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 78bdf3513..262cc55dd 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -3492,8 +3492,8 @@ commands: --queue-rps-limit-reason \ --fairness-key-rps-limit-default \ --fairness-key-rps-limit-reason \ - --fairness-key-weight-set HighPriority=2.0 \ - --fairness-key-weight-set LowPriority=0.5 + --fairness-key-weight HighPriority=2.0 \ + --fairness-key-weight LowPriority=0.5 ``` This command supports updating: @@ -3507,8 +3507,8 @@ commands: Weights control the relative share of capacity each key receives. To unset a rate limit, pass in 'default', for example: --queue-rps-limit default - To unset specific fairness weights, use --fairness-key-weight-unset - To unset all fairness weights, use --fairness-key-weight-unset-all + To unset a specific fairness weight, use --fairness-key-weight =default + To unset all fairness weights, use --fairness-key-weight-clear-all options: - name: task-queue type: string From 08eb77dd818fce62b8e79a442e5e676fb6a0e764 Mon Sep 17 00:00:00 2001 From: Kent Gruber Date: Fri, 29 May 2026 15:49:21 -0400 Subject: [PATCH 12/23] VLN-1354: pin and bump GitHub Actions to latest versions (#1054) VLN-1354: pin and bump GitHub Actions to latest versions Pin all GitHub Actions to full 40-character commit SHAs and bump to latest versions across all workflows: - actions/checkout v4.3.1 -> v6.0.2 - actions/setup-go -> v6.4.0 - actions/upload-artifact (already pinned, kept current) - actions/create-github-app-token v2.2.2 -> v3.1.1 (#1037) - actions/github-script v8.0.0 -> v9.0.0 (#1036) - docker/setup-qemu-action v3.2.0 -> v4.1.0 - docker/setup-buildx-action v3.10.0 -> v4.1.0 (#1035) - docker/login-action v3.5.0 -> v4.2.0 - goreleaser/goreleaser-action v6.4.0 -> v7.2.1 (#1039) Resolves open Dependabot PRs #1035, #1036, #1037, #1038, #1039. Left temporalio/public-actions refs unchanged per exception policy. --------- Co-authored-by: picatz <14850816+picatz@users.noreply.github.com> Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 12655a439044ff2e05af85ed59364d30a95895c4) --- .github/workflows/build-and-publish-docker.yml | 10 +++++----- .github/workflows/ci.yaml | 10 +++++----- .github/workflows/govulncheck.yml | 4 ++-- .github/workflows/release.yml | 6 +++--- .github/workflows/trigger-docs.yml | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-and-publish-docker.yml b/.github/workflows/build-and-publish-docker.yml index f6bc513ab..502c103d1 100644 --- a/.github/workflows/build-and-publish-docker.yml +++ b/.github/workflows/build-and-publish-docker.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -36,7 +36,7 @@ jobs: env: INPUT_VERSION: ${{ inputs.version }} INPUT_TAG_LATEST: ${{ inputs.tag_latest }} - uses: actions/github-script@v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const inputVersion = process.env.INPUT_VERSION; @@ -82,14 +82,14 @@ jobs: ls -lh dist/arm64/temporal - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub if: inputs.publish - uses: docker/login-action@v3 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3a9ee87a3..2314b3ef3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod @@ -53,12 +53,12 @@ jobs: HAS_SECRETS: ${{ secrets.TEMPORAL_CLIENT_CERT != '' && secrets.TEMPORAL_CLIENT_KEY != '' }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod @@ -72,7 +72,7 @@ jobs: run: gotestsum --junitfile junit-xml/${{matrix.os}}.xml -- ./... - name: Upload junit-xml artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() with: name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--${{matrix.os}} diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index b8b6cbe97..7c49fd9df 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -11,8 +11,8 @@ jobs: name: Govulncheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - uses: actions/setup-go@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod - uses: temporalio/public-actions/golang/govulncheck@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0bcd5d666..24ed5570e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,12 +15,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: "go.mod" check-latest: true @@ -47,7 +47,7 @@ jobs: run: echo "go=$(go version | cut -d ' ' -f 3)" >> "$GITHUB_OUTPUT" - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 + uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1 with: version: v2.12.7 args: release diff --git a/.github/workflows/trigger-docs.yml b/.github/workflows/trigger-docs.yml index cfa6349a8..40b4a2de4 100644 --- a/.github/workflows/trigger-docs.yml +++ b/.github/workflows/trigger-docs.yml @@ -39,7 +39,7 @@ jobs: - name: Generate token id: generate_token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 with: app-id: ${{ secrets.TEMPORAL_CICD_APP_ID }} private-key: ${{ secrets.TEMPORAL_CICD_PRIVATE_KEY }} From 9ea8ccd8b02a416abb0936b984c1ac8f625df15b Mon Sep 17 00:00:00 2001 From: Nasit Sarwar Sony Date: Sat, 30 May 2026 04:08:40 -0700 Subject: [PATCH 13/23] =?UTF-8?q?=20Add=20temporal=20schedule=20list-match?= =?UTF-8?q?ing-times=20command=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=E2=80=A6=20(#1047)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Related issues Closes #1030 ## What changed? Adds `temporal schedule list-matching-times` command implementing the `ListScheduleMatchingTimes` RPC. Allows users to preview when a schedule's spec would match within a given time range (past or future) without taking any actions. ## Checklist **Stability** - [x] Breaking changes are marked with :boom: in the PR title and release notes - [x] Changes to JSON output (`-o json` / `-o jsonl`) are treated as breaking changes **Design** - [x] This feature does not depend on Cloud-only APIs or behavior (it works against an OSS server) - [x] New commands follow `temporal ` structure (e.g. `temporal workflow start`) - [x] New flags are named after the API concept, not the implementation mechanism (good: `--search-attribute`, bad: `--index-field`) - [x] New flags don't duplicate an existing flag that serves the same purpose - [x] New flags do not have short aliases without strong justification - [x] Experimental features are marked with `(Experimental feature)` in `commands.yaml` **Help text** (see style guide at the top of `commands.yaml`) - [x] All flags shown in help text and examples are implemented and functional - [x] Summaries use sentence case and have no trailing period - [x] Long descriptions end with a period and include at least one example invocation - [x] Examples use long flags (`--namespace`, not `-n`), one flag per line - [x] Placeholder values use `YourXxx` form (`YourWorkflowId`, `YourNamespace`) **Behavior** - [x] Results go to stdout; errors and warnings go to stderr - [x] Error messages are lowercase with no trailing punctuation **Tests** - [x] Added functional test(s) (`SharedServerSuite`) - [ ] Added unit test(s) (`func TestXxx`) where applicable ## Manual tests **Setup** ``` temporal server start-dev --headless temporal schedule create \ --schedule-id YourScheduleId \ --interval 1h \ --workflow-id YourWorkflowId \ --task-queue YourTaskQueue \ --workflow-type YourWorkflowType ``` **Happy path** ``` $ temporal schedule list-matching-times \ --schedule-id YourScheduleId \ --start-time 2025-01-01T00:00:00Z \ --end-time 2025-01-01T23:59:59Z Time 2025-01-01T00:00:00Z 2025-01-01T01:00:00Z ... $ temporal schedule list-matching-times \ --schedule-id YourScheduleId \ --start-time 2025-01-01T00:00:00Z \ --end-time 2025-01-01T23:59:59Z \ -o json {"startTime":["2025-01-01T00:00:00Z","2025-01-01T01:00:00Z",...]} ``` **Error case** ``` $ temporal schedule list-matching-times \ --schedule-id nonexistent-id \ --start-time 2025-01-01T00:00:00Z \ --end-time 2025-01-01T23:59:59Z Error: ... $ echo $? 1 ``` **Composition** ``` $ temporal schedule list-matching-times \ --schedule-id YourScheduleId \ --start-time 2025-06-01T00:00:00Z \ --end-time 2025-06-07T23:59:59Z \ -o json | jq '.startTime | length' 168 ``` (cherry picked from commit 5380e71e9bf51bc047f9203d04666128dca7a99f) --- internal/temporalcli/commands.gen.go | 34 ++++++++++++ internal/temporalcli/commands.schedule.go | 34 ++++++++++++ .../temporalcli/commands.schedule_test.go | 53 +++++++++++++++++++ internal/temporalcli/commands.yaml | 31 +++++++++++ 4 files changed, 152 insertions(+) diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 468900910..5186d70e3 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -2103,6 +2103,7 @@ func NewTemporalScheduleCommand(cctx *CommandContext, parent *TemporalCommand) * s.Command.AddCommand(&NewTemporalScheduleDeleteCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleDescribeCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleListCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalScheduleListMatchingTimesCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleToggleCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleTriggerCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleUpdateCommand(cctx, &s).Command) @@ -2270,6 +2271,39 @@ func NewTemporalScheduleListCommand(cctx *CommandContext, parent *TemporalSchedu return &s } +type TemporalScheduleListMatchingTimesCommand struct { + Parent *TemporalScheduleCommand + Command cobra.Command + ScheduleIdOptions + StartTime cliext.FlagTimestamp + EndTime cliext.FlagTimestamp +} + +func NewTemporalScheduleListMatchingTimesCommand(cctx *CommandContext, parent *TemporalScheduleCommand) *TemporalScheduleListMatchingTimesCommand { + var s TemporalScheduleListMatchingTimesCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "list-matching-times [flags]" + s.Command.Short = "List matching times for a Schedule (Experimental feature)" + if hasHighlighting { + s.Command.Long = "\nNote: This is an experimental feature and may change in the future.\n\nList the times a Schedule's spec would match within a given time\nrange. The time range may be in the past or future. Use this\ncommand to preview when a Schedule will take actions without\nactually running them.\n\nFor example:\n\n\x1b[1m temporal schedule list-matching-times \\\n --schedule-id \"YourScheduleId\" \\\n --start-time \"2024-01-01T00:00:00Z\" \\\n --end-time \"2024-01-31T23:59:59Z\"\x1b[0m" + } else { + s.Command.Long = "\nNote: This is an experimental feature and may change in the future.\n\nList the times a Schedule's spec would match within a given time\nrange. The time range may be in the past or future. Use this\ncommand to preview when a Schedule will take actions without\nactually running them.\n\nFor example:\n\n```\n temporal schedule list-matching-times \\\n --schedule-id \"YourScheduleId\" \\\n --start-time \"2024-01-01T00:00:00Z\" \\\n --end-time \"2024-01-31T23:59:59Z\"\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().Var(&s.StartTime, "start-time", "Start of time range to list matching times. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "start-time") + s.Command.Flags().Var(&s.EndTime, "end-time", "End of time range to list matching times. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "end-time") + s.ScheduleIdOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type TemporalScheduleToggleCommand struct { Parent *TemporalScheduleCommand Command cobra.Command diff --git a/internal/temporalcli/commands.schedule.go b/internal/temporalcli/commands.schedule.go index 5deadb8ec..3a18271bd 100644 --- a/internal/temporalcli/commands.schedule.go +++ b/internal/temporalcli/commands.schedule.go @@ -11,6 +11,7 @@ import ( "github.com/temporalio/cli/cliext" "github.com/temporalio/cli/internal/printer" "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/timestamppb" commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" @@ -606,3 +607,36 @@ func formatDuration(d time.Duration) string { s = strings.TrimSpace(s) return s } + +func (c *TemporalScheduleListMatchingTimesCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + res, err := cl.WorkflowService().ListScheduleMatchingTimes(cctx, &workflowservice.ListScheduleMatchingTimesRequest{ + Namespace: c.Parent.Namespace, + ScheduleId: c.ScheduleId, + StartTime: timestamppb.New(c.StartTime.Time()), + EndTime: timestamppb.New(c.EndTime.Time()), + }) + if err != nil { + return err + } + + if cctx.JSONOutput { + return cctx.Printer.PrintStructured(res, printer.StructuredOptions{}) + } + + type matchingTime struct { + Time string `json:"time"` + } + var rows []matchingTime + for _, t := range res.StartTime { + rows = append(rows, matchingTime{Time: t.AsTime().Format(time.RFC3339)}) + } + return cctx.Printer.PrintStructured(rows, printer.StructuredOptions{ + Table: &printer.TableOptions{}, + }) +} diff --git a/internal/temporalcli/commands.schedule_test.go b/internal/temporalcli/commands.schedule_test.go index d828e55ea..d4d089bf8 100644 --- a/internal/temporalcli/commands.schedule_test.go +++ b/internal/temporalcli/commands.schedule_test.go @@ -9,6 +9,7 @@ import ( "io" "math/rand" "regexp" + "strings" "time" "github.com/stretchr/testify/assert" @@ -553,3 +554,55 @@ func (s *SharedServerSuite) TestSchedule_Memo_Update() { return j.Schedule.Action.StartWorkflow.Memo.Fields.Bar.Data == "Mg==" }, 10*time.Second, 100*time.Millisecond) } + +func (s *SharedServerSuite) TestSchedule_ListMatchingTimes() { + // use a calendar spec with known hours so results are deterministic + schedId, _, res := s.createSchedule("--calendar", `{"hour":"3,6,9"}`) + s.NoError(res.Err) + + // query a full day - should match exactly 3 times + res = s.Execute( + "schedule", "list-matching-times", + "--address", s.Address(), + "-s", schedId, + "--start-time", "2025-01-01T00:00:00Z", + "--end-time", "2025-01-01T23:59:59Z", + ) + s.NoError(res.Err) + // text output should contain parseable RFC3339 timestamps + for _, line := range strings.Split(res.Stdout.String(), "\n") { + line = strings.TrimSpace(line) + if line == "" || line == "Time" { + continue + } + _, err := time.Parse(time.RFC3339, line) + s.NoError(err, "should parse text line as time: %q", line) + } + + // json output + res = s.Execute( + "schedule", "list-matching-times", + "--address", s.Address(), + "-s", schedId, + "--start-time", "2025-01-01T00:00:00Z", + "--end-time", "2025-01-01T23:59:59Z", + "-o", "json", + ) + s.NoError(res.Err) + var resp struct { + StartTime []string `json:"startTime"` + } + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &resp)) + s.Equal(3, len(resp.StartTime)) +} + +func (s *SharedServerSuite) TestSchedule_ListMatchingTimes_NotFound() { + res := s.Execute( + "schedule", "list-matching-times", + "--address", s.Address(), + "-s", "nonexistent-schedule-id", + "--start-time", "2025-01-01T00:00:00Z", + "--end-time", "2025-01-01T23:59:59Z", + ) + s.Error(res.Err) +} diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 262cc55dd..1d383d179 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -2320,6 +2320,37 @@ commands: - overlap-policy - schedule-id + - name: temporal schedule list-matching-times + summary: List matching times for a Schedule (Experimental feature) + description: | + + Note: This is an experimental feature and may change in the future. + + List the times a Schedule's spec would match within a given time + range. The time range may be in the past or future. Use this + command to preview when a Schedule will take actions without + actually running them. + + For example: + + ``` + temporal schedule list-matching-times \ + --schedule-id "YourScheduleId" \ + --start-time "2024-01-01T00:00:00Z" \ + --end-time "2024-01-31T23:59:59Z" + ``` + options: + - name: start-time + type: timestamp + description: Start of time range to list matching times. + required: true + - name: end-time + type: timestamp + description: End of time range to list matching times. + required: true + option-sets: + - schedule-id + - name: temporal schedule create summary: Create a new Schedule description: | From e7b4e05b16890e21643cdb3d3721e8b92e4e8135 Mon Sep 17 00:00:00 2001 From: hussam-salah <156124396+hussam-salah@users.noreply.github.com> Date: Sat, 30 May 2026 13:35:43 +0200 Subject: [PATCH 14/23] Remove time.Sleep() in commands.taskqueue_test.go (#1020) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - remove time.Sleep() needed before v1.26.2 because of server cache bug - update test assertions of "2 seconds ago" to "now" since we won't wait ## What was changed test `TestTaskQueue_Describe_Simple` at `commands.taskqueue_test.go` was updated to remove unnecessary `time.Sleep(1 * time.Second) ` ## Why? The waiting was a necessary workaround of server cache bug, bug was fixed on v1.26.2 ## Checklist 1. Closes #741 3. How was this tested: Running the unit tests 4. Any docs updates needed? Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 90f1364fab59b9dea05648eaaaf522cbbc067b66) --- .../temporalcli/commands.taskqueue_test.go | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/internal/temporalcli/commands.taskqueue_test.go b/internal/temporalcli/commands.taskqueue_test.go index 6c5e3ba1a..7ca102c00 100644 --- a/internal/temporalcli/commands.taskqueue_test.go +++ b/internal/temporalcli/commands.taskqueue_test.go @@ -262,10 +262,6 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple() { ) s.NoError(res.Err) - // TODO(antlai-temporal): Delete when a server caching bug in 1.26.2 is fixed, - // see https://github.com/temporalio/temporal/pull/6978 - time.Sleep(1 * time.Second) - // Text res = s.Execute( "task-queue", "describe", @@ -279,10 +275,6 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple() { // No pollers on id1 s.NotContains(res.Stdout.String(), "now") - // TODO(antlai-temporal): Delete when a server caching bug in 1.26.2 is fixed, - // see https://github.com/temporalio/temporal/pull/6978 - time.Sleep(1 * time.Second) - res = s.Execute( "task-queue", "describe", "--select-unversioned", @@ -294,12 +286,8 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple() { s.NoError(res.Err) s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "unreachable") - s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "workflow", s.DevServer.Options.ClientOptions.Identity, "2 seconds ago", "100000") - s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "activity", s.DevServer.Options.ClientOptions.Identity, "2 seconds ago", "100000") - - // TODO(antlai-temporal): Delete when a server caching bug in 1.26.2 is fixed, - // see https://github.com/temporalio/temporal/pull/6978 - time.Sleep(1 * time.Second) + s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "workflow", s.DevServer.Options.ClientOptions.Identity, "now", "100000") + s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "activity", s.DevServer.Options.ClientOptions.Identity, "now", "100000") res = s.Execute( "task-queue", "describe", @@ -315,10 +303,6 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple() { // No pollers on id2 s.NotContains(res.Stdout.String(), "now") - // TODO(antlai-temporal): Delete when a server caching bug in 1.26.2 is fixed, - // see https://github.com/temporalio/temporal/pull/6978 - time.Sleep(1 * time.Second) - res = s.Execute( "task-queue", "describe", "--select-all-active", From 286129fd0976eea3fb0fc6114e35eca27d603d45 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Sat, 30 May 2026 04:56:54 -0700 Subject: [PATCH 15/23] Add persistence info to start-dev banner (#1033) ## What was changed `temporal server start-dev` now prints a `Persistence:` line at the end of the startup banner. In-memory (no `--db-filename`): ``` Persistence: in-memory (Workflow Executions are lost when the server process exits) ``` File-backed: ``` Persistence: file (/path/to/dev.sqlite) ``` ## Why? Per #634, the start-dev banner did not surface what form of persistence the dev server uses, so users could not tell at a glance whether Workflow Executions would survive a restart. ## Checklist 1. Closes #634 2. How was this tested: Two new tests in `internal/temporalcli/commands.server_test.go` start the dev server, capture stdout, and assert that the banner contains `Persistence:` plus the expected backend description. Both pass locally and fail when the banner change is reverted. ``` $ go test -run TestServer_StartDev_BannerPersistence ./internal/temporalcli/ ok github.com/temporalio/cli/internal/temporalcli 10.318s ``` --------- Signed-off-by: Sai Asish Y Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit f015ce2ac01ec26b97fa51cf5ce29ab047c03e07) --- internal/temporalcli/commands.server.go | 13 ++- internal/temporalcli/commands.server_test.go | 87 ++++++++++++++++++++ 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/internal/temporalcli/commands.server.go b/internal/temporalcli/commands.server.go index 90c5b2fe3..bd6bed6af 100644 --- a/internal/temporalcli/commands.server.go +++ b/internal/temporalcli/commands.server.go @@ -160,15 +160,20 @@ func (t *TemporalServerStartDevCommand) run(cctx *CommandContext, args []string) defer s.Stop() cctx.Printer.Printlnf("Temporal CLI %v\n", VersionString()) - cctx.Printer.Printlnf("%-17s %v:%v", "Temporal Server:", toFriendlyIp(opts.FrontendIP), opts.FrontendPort) + cctx.Printer.Printlnf("%-21s %v:%v", "Temporal Server:", toFriendlyIp(opts.FrontendIP), opts.FrontendPort) + if t.DbFilename == "" { + cctx.Printer.Printlnf("%-21s %v", "Temporal Persistence:", "in-memory") + } else { + cctx.Printer.Printlnf("%-21s %v", "Temporal Persistence:", t.DbFilename) + } // Only print HTTP port if explicitly provided to avoid promoting the unstable HTTP API. if opts.FrontendHTTPPort > 0 { - cctx.Printer.Printlnf("%-17s %v:%v", "Temporal HTTP:", toFriendlyIp(opts.FrontendIP), opts.FrontendHTTPPort) + cctx.Printer.Printlnf("%-21s %v:%v", "Temporal HTTP:", toFriendlyIp(opts.FrontendIP), opts.FrontendHTTPPort) } if !t.Headless { - cctx.Printer.Printlnf("%-17s http://%v:%v%v", "Temporal UI:", toFriendlyIp(opts.UIIP), opts.UIPort, opts.PublicPath) + cctx.Printer.Printlnf("%-21s http://%v:%v%v", "Temporal UI:", toFriendlyIp(opts.UIIP), opts.UIPort, opts.PublicPath) } - cctx.Printer.Printlnf("%-17s http://%v:%v/metrics", "Temporal Metrics:", toFriendlyIp(opts.FrontendIP), opts.MetricsPort) + cctx.Printer.Printlnf("%-21s http://%v:%v/metrics", "Temporal Metrics:", toFriendlyIp(opts.FrontendIP), opts.MetricsPort) <-cctx.Done() if !t.Parent.Parent.LogLevel.ChangedFromDefault { // The server routinely emits various warnings on shutdown. diff --git a/internal/temporalcli/commands.server_test.go b/internal/temporalcli/commands.server_test.go index 8c25558d9..26b92cf60 100644 --- a/internal/temporalcli/commands.server_test.go +++ b/internal/temporalcli/commands.server_test.go @@ -271,6 +271,93 @@ func TestServer_StartDev_WithSearchAttributes(t *testing.T) { } } +func TestServer_StartDev_BannerPersistenceInMemory(t *testing.T) { + h := NewCommandHarness(t) + defer h.Close() + + port := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) + httpPort := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) + resCh := make(chan *CommandResult, 1) + go func() { + resCh <- h.Execute("server", "start-dev", "-p", port, "--http-port", httpPort, "--headless") + }() + + // Wait until the server is dial-able, then cancel + var cl client.Client + h.EventuallyWithT(func(t *assert.CollectT) { + select { + case res := <-resCh: + require.NoError(t, res.Err) + require.Fail(t, "got early server result") + default: + } + var err error + cl, err = client.Dial(client.Options{HostPort: "127.0.0.1:" + port}) + assert.NoError(t, err) + }, 3*time.Second, 200*time.Millisecond) + defer cl.Close() + + h.CancelContext() + var res *CommandResult + select { + case <-time.After(20 * time.Second): + h.Fail("didn't cleanup after 20 seconds") + case res = <-resCh: + h.NoError(res.Err) + } + out := res.Stdout.String() + h.Contains(out, "Temporal Persistence:") + h.Contains(out, "in-memory") +} + +func TestServer_StartDev_BannerPersistenceFile(t *testing.T) { + h := NewCommandHarness(t) + defer h.Close() + + port := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) + httpPort := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) + // Use os.TempDir with an explicit defer-remove so Windows file-lock cleanup + // (os.RemoveAll in t.TempDir) does not race with a still-open SQLite handle. + dbFilename := filepath.Join(os.TempDir(), "devserver-banner-"+t.Name()+".sqlite") + t.Cleanup(func() { + _ = os.Remove(dbFilename) + _ = os.Remove(dbFilename + "-shm") + _ = os.Remove(dbFilename + "-wal") + }) + resCh := make(chan *CommandResult, 1) + go func() { + resCh <- h.Execute("server", "start-dev", "-p", port, "--http-port", httpPort, + "--headless", "--db-filename", dbFilename) + }() + + var cl client.Client + // File-backed server takes longer to start due to SQLite initialization. + h.EventuallyWithT(func(t *assert.CollectT) { + select { + case res := <-resCh: + require.NoError(t, res.Err) + require.Fail(t, "got early server result") + default: + } + var err error + cl, err = client.Dial(client.Options{HostPort: "127.0.0.1:" + port}) + assert.NoError(t, err) + }, 15*time.Second, 200*time.Millisecond) + defer cl.Close() + + h.CancelContext() + var res *CommandResult + select { + case <-time.After(20 * time.Second): + h.Fail("didn't cleanup after 20 seconds") + case res = <-resCh: + h.NoError(res.Err) + } + out := res.Stdout.String() + h.Contains(out, "Temporal Persistence:") + h.Contains(out, dbFilename) +} + type testLogger struct { t *testing.T } From 441d92b51d4f5f024186516964e040275dd2e0fe Mon Sep 17 00:00:00 2001 From: Bitalizer <23104115+bitalizer@users.noreply.github.com> Date: Sun, 31 May 2026 03:42:01 +0300 Subject: [PATCH 16/23] fix: skip CountWorkflow in batch operations when --yes is set (#1012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #838. Implements the design @cretz proposed in the issue thread: when `--yes` bypasses the confirmation prompt, skip the `CountWorkflowExecutions` request entirely. ## What was changed - [internal/temporalcli/commands.workflow.go](internal/temporalcli/commands.workflow.go) — `SingleWorkflowOrBatchOptions.workflowExecOrBatch`: `CountWorkflow` only runs in the `!s.Yes` branch. - [internal/temporalcli/commands.workflow_reset.go](internal/temporalcli/commands.workflow_reset.go) — same pattern in `TemporalWorkflowResetCommand`'s batch path. When `--yes` skips the count, the prompt text changes from `Start batch against approximately N workflow(s)? y/N` to `Start batch against workflows matching query ""? y/N`, so the output (which `promptYes` still prints in the auto-confirm path) stays informative. The non-`--yes` interactive flow is untouched: it still counts, still prompts, still prints the same `approximately N workflow(s)` message. ## Why `workflowExecOrBatch` (terminate / signal / cancel) and the reset command both unconditionally call `CountWorkflowExecutions` before starting a batch. The result is only used to fill in the `approximately N workflow(s)` confirmation. When `--yes` is set, the prompt is skipped — but the count call still runs, and on clusters where the visibility API is timing out it fails the entire batch start. The original report is from a Postgres-backed cluster where the batch query itself works but the count times out; users can't start batch jobs they otherwise have permission to run. ## How was this tested Added `TestWorkflow_Terminate_BatchWorkflow_SkipsCountWhenYes` that: 1. Installs a unary gRPC interceptor that counts `CountWorkflowExecutionsRequest` and `StartBatchOperationRequest` calls. 2. Starts one workflow, then runs `workflow terminate --query ... --yes`. 3. Asserts: 0 `CountWorkflow` calls, 1 `StartBatchOperation` call, prompt text contains `matching query` and not `approximately`. The interceptor pattern matches the existing `testTerminateBatchWorkflow` helper. Ran the new test plus a sample of the existing batch tests locally; they pass. (`TestWorkflow_Terminate_BatchWorkflowSuccess` flakes locally on the unrelated `Completed` assertion both with and without my changes — pre-existing timing issue, not caused by this PR.) --------- Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 7e5ba380c9a4a117333045a3dace75555b906aa1) --- internal/temporalcli/commands.workflow.go | 18 ++++--- .../temporalcli/commands.workflow_reset.go | 17 +++++-- .../commands.workflow_reset_test.go | 39 +++++++++++++++ .../temporalcli/commands.workflow_test.go | 48 ++++++++++++++++++- 4 files changed, 110 insertions(+), 12 deletions(-) diff --git a/internal/temporalcli/commands.workflow.go b/internal/temporalcli/commands.workflow.go index 20ebec8d1..e03047714 100644 --- a/internal/temporalcli/commands.workflow.go +++ b/internal/temporalcli/commands.workflow.go @@ -569,13 +569,19 @@ func (s *SingleWorkflowOrBatchOptions) workflowExecOrBatch( return nil, nil, fmt.Errorf("cannot set run ID when query is set") } - // Count the workflows that will be affected - count, err := cl.CountWorkflow(cctx, &workflowservice.CountWorkflowExecutionsRequest{Query: s.Query}) - if err != nil { - return nil, nil, fmt.Errorf("failed counting workflows from query: %w", err) + // The count is only used in the confirmation prompt; skip the request when --yes + // bypasses it, so batch jobs can still proceed if the visibility API is timing out. + var promptMessage string + if s.Yes { + promptMessage = fmt.Sprintf("Start batch against workflows matching query %q? y/N", s.Query) + } else { + count, err := cl.CountWorkflow(cctx, &workflowservice.CountWorkflowExecutionsRequest{Query: s.Query}) + if err != nil { + return nil, nil, fmt.Errorf("failed counting workflows from query: %w", err) + } + promptMessage = fmt.Sprintf("Start batch against approximately %v workflow(s)? y/N", count.Count) } - yes, err := cctx.promptYes( - fmt.Sprintf("Start batch against approximately %v workflow(s)? y/N", count.Count), s.Yes) + yes, err := cctx.promptYes(promptMessage, s.Yes) if err != nil { return nil, nil, err } else if !yes { diff --git a/internal/temporalcli/commands.workflow_reset.go b/internal/temporalcli/commands.workflow_reset.go index 4c33065ce..aee402891 100644 --- a/internal/temporalcli/commands.workflow_reset.go +++ b/internal/temporalcli/commands.workflow_reset.go @@ -134,12 +134,19 @@ func (c *TemporalWorkflowResetCommand) runBatchResetWithPostOps(cctx *CommandCon PostResetOperations: postOps, }, } - count, err := cl.CountWorkflow(cctx, &workflowservice.CountWorkflowExecutionsRequest{Query: c.Query}) - if err != nil { - return fmt.Errorf("failed counting workflows from query: %w", err) + // The count is only used in the confirmation prompt; skip the request when --yes + // bypasses it, so batch jobs can still proceed if the visibility API is timing out. + var promptMessage string + if c.Yes { + promptMessage = fmt.Sprintf("Start batch against workflows matching query %q? y/N", c.Query) + } else { + count, err := cl.CountWorkflow(cctx, &workflowservice.CountWorkflowExecutionsRequest{Query: c.Query}) + if err != nil { + return fmt.Errorf("failed counting workflows from query: %w", err) + } + promptMessage = fmt.Sprintf("Start batch against approximately %v workflow(s)? y/N", count.Count) } - yes, err := cctx.promptYes( - fmt.Sprintf("Start batch against approximately %v workflow(s)? y/N", count.Count), c.Yes) + yes, err := cctx.promptYes(promptMessage, c.Yes) if err != nil { return err } diff --git a/internal/temporalcli/commands.workflow_reset_test.go b/internal/temporalcli/commands.workflow_reset_test.go index 33b3cc525..5dc87604c 100644 --- a/internal/temporalcli/commands.workflow_reset_test.go +++ b/internal/temporalcli/commands.workflow_reset_test.go @@ -927,3 +927,42 @@ func (sut *batchResetTestData) getWorkflowHistory() ([]*history.HistoryEvent, er return events, nil } + +func (s *SharedServerSuite) TestWorkflow_ResetBatch_SkipsCountWhenYes() { + s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { + ctx.Done().Receive(ctx, nil) + return nil, ctx.Err() + }) + + searchAttr := "keyword-" + uuid.NewString() + _, err := s.Client.ExecuteWorkflow( + s.Context, + client.StartWorkflowOptions{ + TaskQueue: s.Worker().Options.TaskQueue, + SearchAttributes: map[string]any{"CustomKeywordField": searchAttr}, + }, + DevWorkflow, + "ignored", + ) + s.NoError(err) + s.Eventually(func() bool { + resp, err := s.Client.ListWorkflow(s.Context, &workflowservice.ListWorkflowExecutionsRequest{ + Query: "CustomKeywordField = '" + searchAttr + "'", + }) + s.NoError(err) + return len(resp.Executions) == 1 + }, 3*time.Second, 100*time.Millisecond) + + res := s.Execute( + "workflow", "reset", + "--address", s.Address(), + "--query", "CustomKeywordField = '"+searchAttr+"'", + "--type", "FirstWorkflowTask", + "--reason", "skip-count-test", + "--yes", + ) + s.NoError(res.Err) + + s.NotContains(res.Stdout.String(), "approximately") + s.Contains(res.Stdout.String(), "matching query") +} diff --git a/internal/temporalcli/commands.workflow_test.go b/internal/temporalcli/commands.workflow_test.go index 20d6d256b..aedf331f7 100644 --- a/internal/temporalcli/commands.workflow_test.go +++ b/internal/temporalcli/commands.workflow_test.go @@ -7,6 +7,7 @@ import ( "math/rand" "regexp" "strconv" + "strings" "sync" "time" @@ -250,7 +251,7 @@ func (s *SharedServerSuite) TestWorkflow_Delete_BatchWorkflowSuccess() { "-y", ) s.NoError(res.Err) - s.Contains(res.Stdout.String(), "Start batch against approximately 2 workflow(s)") + s.Contains(res.Stdout.String(), "Start batch against workflows matching query") s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") @@ -387,6 +388,51 @@ func (s *SharedServerSuite) TestWorkflow_Terminate_BatchWorkflowSuccess_JSON() { s.NotEmpty(jsonRes["batchJobId"]) } +func (s *SharedServerSuite) TestWorkflow_Terminate_BatchWorkflow_SkipsCountWhenYes() { + s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { + ctx.Done().Receive(ctx, nil) + return nil, ctx.Err() + }) + + searchAttr := "keyword-" + uuid.NewString() + run, err := s.Client.ExecuteWorkflow( + s.Context, + client.StartWorkflowOptions{ + TaskQueue: s.Worker().Options.TaskQueue, + SearchAttributes: map[string]any{"CustomKeywordField": searchAttr}, + }, + DevWorkflow, + "ignored", + ) + s.NoError(err) + s.Eventually(func() bool { + resp, err := s.Client.ListWorkflow(s.Context, &workflowservice.ListWorkflowExecutionsRequest{ + Query: "CustomKeywordField = '" + searchAttr + "'", + }) + s.NoError(err) + return len(resp.Executions) == 1 + }, 3*time.Second, 100*time.Millisecond) + + res := s.Execute( + "workflow", "terminate", + "--address", s.Address(), + "--query", "CustomKeywordField = '"+searchAttr+"'", + "--reason", "skip-count-test", + "--yes", + ) + s.NoError(res.Err) + + // Prompt text should show the query, not the count + s.NotContains(res.Stdout.String(), "approximately") + s.Contains(res.Stdout.String(), "matching query") + + // Confirm workflow was terminated + s.Eventually(func() bool { + err := run.Get(s.Context, nil) + return err != nil && strings.Contains(err.Error(), "terminated") + }, 5*time.Second, 100*time.Millisecond) +} + func (s *SharedServerSuite) testTerminateBatchWorkflow( total int, rps float32, From a38aef438980f1a57d90a38cf49897ab9a8042a1 Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:07:03 -0500 Subject: [PATCH 17/23] fix: use allow instead of ignore for dependency-type in dependabot config (#1057) ## Summary - `dependency-type` is only valid inside `allow`, not `ignore` in dependabot.yml - Replaces the invalid `ignore` entry with `allow: [{dependency-type: direct}]` to achieve the same goal of skipping indirect/transitive dependency updates - Adds a CI workflow using `check-jsonschema` to validate `dependabot.yml` on PRs and pushes to main - Fixes the Dependabot config validation failure introduced in #1044 ## Test plan - [ ] Verify the `.github/dependabot.yml` validation check passes on this PR - [ ] Verify `check-jsonschema --builtin-schema vendor.dependabot .github/dependabot.yml` passes locally (cherry picked from commit 8bb57b77ad55235f9b0f6d9f09e350057f53cdb8) --- .github/dependabot.yml | 4 ++-- .github/workflows/validate-dependabot.yml | 29 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/validate-dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4ebbf0d4e..7226d8733 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,9 +6,9 @@ updates: interval: weekly cooldown: default-days: 14 + allow: + - dependency-type: direct ignore: - - dependency-name: "*" - dependency-type: indirect # Temporal dependencies are managed manually to ensure coordinated upgrades across all temporal packages - dependency-name: "go.temporal.io/*" diff --git a/.github/workflows/validate-dependabot.yml b/.github/workflows/validate-dependabot.yml new file mode 100644 index 000000000..d9010aa50 --- /dev/null +++ b/.github/workflows/validate-dependabot.yml @@ -0,0 +1,29 @@ +name: Validate Dependabot Config +on: + pull_request: + paths: + - '.github/dependabot.yml' + push: + branches: + - main + paths: + - '.github/dependabot.yml' + +permissions: + contents: read + +jobs: + validate: + name: Validate Dependabot Config + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + with: + python-version: '3.12' + + - name: Validate dependabot.yml + run: | + pip install check-jsonschema==0.37.2 + check-jsonschema --builtin-schema vendor.dependabot .github/dependabot.yml From fc2ab388904d3a0e372a6d7aee9c2859d9b01d3f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:57:27 -0500 Subject: [PATCH 18/23] Bump the github-actions group with 2 updates (#1080) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 2 updates: [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) and [actions/create-github-app-token](https://github.com/actions/create-github-app-token). Updates `goreleaser/goreleaser-action` from 7.2.1 to 7.2.2
Release notes

Sourced from goreleaser/goreleaser-action's releases.

v7.2.2

What's Changed

New Contributors

Full Changelog: https://github.com/goreleaser/goreleaser-action/compare/v7...v7.2.2

Commits

Updates `actions/create-github-app-token` from 3.1.1 to 3.2.0
Release notes

Sourced from actions/create-github-app-token's releases.

v3.2.0

3.2.0 (2026-05-12)

Features

  • add support for enterprise-level GitHub Apps (#263) (952a2a7)
  • support full repository names in repositories input (#372) (85eb8dd)

Bug Fixes

  • deps: bump @​actions/core from 3.0.0 to 3.0.1 in the production-dependencies group (#364) (43e5c34)
  • validate private-key input (#376) (f24bbd8)
Changelog

Sourced from actions/create-github-app-token's changelog.

Changelog

3.2.0 (2026-05-12)

Features

  • add support for enterprise-level GitHub Apps (#263) (952a2a7)
  • support full repository names in repositories input (#372) (85eb8dd)

Bug Fixes

  • deps: bump @​actions/core from 3.0.0 to 3.0.1 in the production-dependencies group (#364) (43e5c34)
  • validate private-key input (#376) (f24bbd8)
Commits
  • bcd2ba4 chore(main): release 3.2.0 (#370)
  • f24bbd8 fix: validate private-key input (#376)
  • 363531b docs: capitalize Git as a proper noun in README (#374)
  • fd28011 docs: update procedure to configure Git (#287)
  • 85eb8dd feat: support full repository names in repositories input (#372)
  • c9aabb8 build(deps-dev): bump yaml from 2.8.3 to 2.8.4 in the development-dependencie...
  • e02e816 build(deps-dev): bump undici from 7.24.6 to 8.2.0 (#366)
  • 8d835bf build(deps-dev): bump esbuild from 0.27.4 to 0.28.0 in the development-depend...
  • 952a2a7 feat: add support for enterprise-level GitHub Apps (#263)
  • 43e5c34 fix(deps): bump @​actions/core from 3.0.0 to 3.0.1 in the production-dependenc...
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit dd0524a8e1f04e9cd6ddff0f1b2b0dc211d5025c) --- .github/workflows/release.yml | 2 +- .github/workflows/trigger-docs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 24ed5570e..ec10831b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,7 +47,7 @@ jobs: run: echo "go=$(go version | cut -d ' ' -f 3)" >> "$GITHUB_OUTPUT" - name: Run GoReleaser - uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1 + uses: goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2 with: version: v2.12.7 args: release diff --git a/.github/workflows/trigger-docs.yml b/.github/workflows/trigger-docs.yml index 40b4a2de4..a6124050c 100644 --- a/.github/workflows/trigger-docs.yml +++ b/.github/workflows/trigger-docs.yml @@ -39,7 +39,7 @@ jobs: - name: Generate token id: generate_token - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: app-id: ${{ secrets.TEMPORAL_CICD_APP_ID }} private-key: ${{ secrets.TEMPORAL_CICD_PRIVATE_KEY }} From 664b7a4483d1c791c6dd72bd7bed9dbf6f918069 Mon Sep 17 00:00:00 2001 From: Nasit Sarwar Sony Date: Wed, 3 Jun 2026 17:16:59 -0700 Subject: [PATCH 19/23] Prefix dev server cluster ID with 'dev-server-' (#1059) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Related issues Closes #609 ## What changed? Prefix generated dev server cluster IDs with `dev-server-` for better identification. Previously cluster IDs were plain UUIDs with no indication they came from a dev server. ## Checklist **Stability** - [x] Breaking changes are marked with 💥 in the PR title and release notes **Design** - [x] This feature does not depend on Cloud-only APIs or behavior (it works against an OSS server) **Tests** - [x] Added functional test(s) — existing tests cover this change ## Manual tests **Setup** ``` temporal server start-dev --headless ``` **Happy path** ``` $ temporal operator cluster describe ClusterId dev-server- ``` **Error case** N/A Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 2ae37a368a8c9f15c78e2563a8961901af1a826b) --- internal/temporalcli/commands.server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/temporalcli/commands.server.go b/internal/temporalcli/commands.server.go index bd6bed6af..bd45f911a 100644 --- a/internal/temporalcli/commands.server.go +++ b/internal/temporalcli/commands.server.go @@ -196,7 +196,7 @@ func persistentClusterID() string { file := defaultDeprecatedEnvConfigFile("temporalio", "version-info") if file == "" { // No file, can do nothing here - return uuid.NewString() + return "dev-server-" + uuid.NewString() } // Try to get existing first env, _ := readDeprecatedEnvConfigFile(file) @@ -204,7 +204,7 @@ func persistentClusterID() string { return id } // Create and try to write - id := uuid.NewString() + id := "dev-server-" + uuid.NewString() _ = writeDeprecatedEnvConfigFile(file, map[string]map[string]string{"default": {"cluster-id": id}}) return id } From ba8452748d117f71526d48a3409daedc68867b26 Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Thu, 4 Jun 2026 11:38:12 -0500 Subject: [PATCH 20/23] feat: auto-generate deprecation warnings from YAML config (#941) ## Related issues Closes #673 ## What changed? Auto-generate deprecation warnings from a `deprecated: true` YAML flag instead of manually writing CAUTION boxes in command descriptions. Deprecation warnings are also printed to stderr at runtime so users see them when invoking deprecated commands, without breaking JSON output. ### Before Deprecation required manually adding a CAUTION box to the description and `(Deprecated)` to the summary: ```yaml - name: temporal task-queue get-build-id-reachability summary: Show Build ID availability (Deprecated) description: | ` `` +-----------------------------------------------------------------------------+ | CAUTION: This command is deprecated and will be removed in a later release. | +-----------------------------------------------------------------------------+ ` `` Show if a given Build ID can be used for new, existing, or closed Workflows... ``` No runtime warning was shown when invoking the command. ### After Set `deprecated: true` and optionally `deprecation-message`. The CAUTION box, `(Deprecated)` summary suffix, and stderr runtime warning are all auto-generated: ```yaml - name: temporal task-queue get-build-id-reachability deprecated: true description: | Show if a given Build ID can be used for new, existing, or closed Workflows... - name: temporal task-queue versioning deprecated: true deprecation-message: This API has been deprecated by Worker Deployment. description: | Provides commands to add, list, remove, or replace... ``` Runtime stderr output: ``` $ temporal task-queue get-build-id-reachability --task-queue foo --build-id bar warning: this command is deprecated and will be removed in a later release ... ``` JSON output is not affected (warning goes to stderr only): ``` $ temporal task-queue get-build-ids -o json --task-queue foo 2>/dev/null [{"buildIds":["1.0"],"defaultForSet":"1.0","isDefaultSet":true}] ``` ## Checklist **Stability** - [x] Breaking changes are marked with :boom: in the PR title and release notes - [x] Changes to JSON output (`-o json` / `-o jsonl`) are treated as breaking changes **Design** - [x] This feature does not depend on Cloud-only APIs or behavior (it works against an OSS server) **Help text** (see style guide at the top of `commands.yaml`) - [x] Summaries use sentence case and have no trailing period - [x] Long descriptions end with a period and include at least one example invocation **Behavior** - [x] Results go to stdout; errors and warnings go to stderr - [x] Error messages are lowercase with no trailing punctuation **Tests** - [x] Added functional test(s) (`SharedServerSuite`) - [x] Added unit test(s) (`func TestXxx`) where applicable ## Manual tests **Setup** ``` temporal server start-dev --headless ``` **Happy path -- stderr warning on deprecated command** ``` $ temporal task-queue get-build-ids \ --task-queue YourTaskQueue warning: this command is deprecated and will be removed in a later release ... ``` **Happy path -- JSON output not polluted** ``` $ temporal task-queue get-build-ids \ -o json \ --task-queue YourTaskQueue 2>/dev/null [...] ``` **Happy path -- help text shows CAUTION box** ``` $ temporal task-queue get-build-id-reachability --help +-----------------------------------------------------------------------------+ | CAUTION: This command is deprecated and will be removed in a later release. | +-----------------------------------------------------------------------------+ Show if a given Build ID can be used for new, existing, or closed Workflows... ``` **Happy path -- custom deprecation message** ``` $ temporal task-queue versioning --help +-------------------------------------------------------------+ | CAUTION: This API has been deprecated by Worker Deployment. | +-------------------------------------------------------------+ ... ``` (cherry picked from commit e20ca61959d938196e0c5bc7a44da13905e9025d) --- internal/commandsgen/code.go | 18 ++- internal/commandsgen/parse.go | 28 +++- internal/commandsgen/parse_test.go | 48 ++++++ internal/temporalcli/commands.gen.go | 66 +++++--- internal/temporalcli/commands.go | 5 + .../commands.taskqueue_build_id_test.go | 42 +++++ internal/temporalcli/commands.yaml | 143 +++++------------- 7 files changed, 222 insertions(+), 128 deletions(-) create mode 100644 internal/commandsgen/parse_test.go diff --git a/internal/commandsgen/code.go b/internal/commandsgen/code.go index fb500751b..84cff3bbc 100644 --- a/internal/commandsgen/code.go +++ b/internal/commandsgen/code.go @@ -233,12 +233,22 @@ func (c *Command) writeCode(w *codeWriter) error { } else { w.writeLinef("s.Command.Args = %v.NoArgs", w.importCobra()) } - if c.IgnoreMissingEnv { + if c.IgnoreMissingEnv || c.Deprecated { w.writeLinef("s.Command.Annotations = make(map[string]string)") - w.writeLinef("s.Command.Annotations[\"ignoresMissingEnv\"] = \"true\"") + if c.IgnoreMissingEnv { + w.writeLinef("s.Command.Annotations[\"ignoresMissingEnv\"] = \"true\"") + } } - if c.Deprecated != "" { - w.writeLinef("s.Command.Deprecated = %q", c.Deprecated) + // Note: We intentionally don't set s.Command.Deprecated here because Cobra + // prints deprecation warnings to stdout, which breaks JSON output. Instead, + // the deprecation warning is prepended to the description/help text and + // printed to stderr via the annotation below. + if c.Deprecated { + msg := c.DeprecationMessage + if msg == "" { + msg = defaultDeprecationMessage + } + w.writeLinef("s.Command.Annotations[\"deprecationWarning\"] = %q", msg) } // Add subcommands for _, subCommand := range subCommands { diff --git a/internal/commandsgen/parse.go b/internal/commandsgen/parse.go index 2083a033d..831b86902 100644 --- a/internal/commandsgen/parse.go +++ b/internal/commandsgen/parse.go @@ -39,8 +39,9 @@ type ( Description string `yaml:"description"` DescriptionPlain string DescriptionHighlighted string - Deprecated string `yaml:"deprecated"` - HasInit bool `yaml:"has-init"` + Deprecated bool `yaml:"deprecated"` + DeprecationMessage string `yaml:"deprecation-message"` + HasInit bool `yaml:"has-init"` ExactArgs int `yaml:"exact-args"` MaximumArgs int `yaml:"maximum-args"` IgnoreMissingEnv bool `yaml:"ignores-missing-env"` @@ -137,6 +138,22 @@ var markdownInlineCodeRegex = regexp.MustCompile("`([^`]+)`") const ansiReset = "\033[0m" const ansiBold = "\033[1m" +const defaultDeprecationMessage = "This command is deprecated and will be removed in a later release." + +// generateDeprecationBox creates a formatted CAUTION box for deprecated commands. +// If message is empty, uses the default deprecation message. +func generateDeprecationBox(message string) string { + if message == "" { + message = defaultDeprecationMessage + } + content := "CAUTION: " + message + // Calculate box width (content + 2 spaces padding + 2 border chars) + boxWidth := len(content) + 4 + border := "+" + strings.Repeat("-", boxWidth-2) + "+" + middle := "| " + content + " |" + return "```\n" + border + "\n" + middle + "\n" + border + "\n```\n\n" +} + func (o OptionSets) processSection() error { if o.Name == "" { return fmt.Errorf("missing option set name") @@ -172,6 +189,13 @@ func (c *Command) processSection() error { return fmt.Errorf("missing description for command: %s", c.FullName) } + // Auto-handle deprecation: prepend warning box to description and append + // "(Deprecated)" to summary. + if c.Deprecated { + c.Description = generateDeprecationBox(c.DeprecationMessage) + c.Description + c.Summary += " (Deprecated)" + } + if len(c.NamePath) == 2 { if c.Docs.Keywords == nil { return fmt.Errorf("missing keywords for root command: %s", c.FullName) diff --git a/internal/commandsgen/parse_test.go b/internal/commandsgen/parse_test.go new file mode 100644 index 000000000..eb77a83e1 --- /dev/null +++ b/internal/commandsgen/parse_test.go @@ -0,0 +1,48 @@ +package commandsgen + +import "testing" + +func TestGenerateDeprecationBox(t *testing.T) { + tests := []struct { + name string + message string + expected string + }{ + { + name: "default message when empty", + message: "", + expected: "```\n" + + "+-----------------------------------------------------------------------------+\n" + + "| CAUTION: This command is deprecated and will be removed in a later release. |\n" + + "+-----------------------------------------------------------------------------+\n" + + "```\n\n", + }, + { + name: "custom message", + message: "Use the new API instead.", + expected: "```\n" + + "+-----------------------------------+\n" + + "| CAUTION: Use the new API instead. |\n" + + "+-----------------------------------+\n" + + "```\n\n", + }, + { + name: "short custom message", + message: "Removed.", + expected: "```\n" + + "+-------------------+\n" + + "| CAUTION: Removed. |\n" + + "+-------------------+\n" + + "```\n\n", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := generateDeprecationBox(tt.message) + if got != tt.expected { + t.Errorf("generateDeprecationBox(%q) =\n%q\nwant:\n%q", tt.message, got, tt.expected) + } + }) + } +} diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 5186d70e3..4985840e1 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -2678,6 +2678,8 @@ func NewTemporalTaskQueueGetBuildIdReachabilityCommand(cctx *CommandContext, par s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nShow if a given Build ID can be used for new, existing, or closed Workflows\nin Namespaces that support Worker versioning:\n\n```\ntemporal task-queue get-build-id-reachability \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\nYou can specify the `--build-id` and `--task-queue` flags multiple times. If\n`--task-queue` is omitted, the command checks Build ID reachability against\nall Task Queues." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringArrayVar(&s.BuildId, "build-id", nil, "One or more Build ID strings. Can be passed multiple times.") s.ReachabilityType = cliext.NewFlagStringEnum([]string{"open", "closed", "existing"}, "existing") s.Command.Flags().Var(&s.ReachabilityType, "reachability-type", "Reachability filter. `open`: reachable by one or more open workflows. `closed`: reachable by one or more closed workflows. `existing`: reachable by either. New Workflow Executions reachable by a Build ID are always reported. Accepted values: open, closed, existing.") @@ -2709,6 +2711,8 @@ func NewTemporalTaskQueueGetBuildIdsCommand(cctx *CommandContext, parent *Tempor s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nFetch sets of compatible Build IDs for specified Task Queues and display their\ninformation:\n\n```\ntemporal task-queue get-build-ids \\\n --task-queue YourTaskQueue\n```\n\nThis command is limited to Namespaces that support Worker versioning." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "task-queue") s.Command.Flags().IntVar(&s.MaxSets, "max-sets", 0, "Max return count. Use 1 for default major version. Use 0 for all sets.") @@ -2764,6 +2768,8 @@ func NewTemporalTaskQueueUpdateBuildIdsCommand(cctx *CommandContext, parent *Tem s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nAdd or change a Task Queue's compatible Build IDs for Namespaces using Worker\nversioning:\n\n```\ntemporal task-queue update-build-ids [subcommands] [options] \\\n --task-queue YourTaskQueue\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.AddCommand(&NewTemporalTaskQueueUpdateBuildIdsAddNewCompatibleCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueUpdateBuildIdsAddNewDefaultCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueUpdateBuildIdsPromoteIdInSetCommand(cctx, &s).Command) @@ -2826,6 +2832,8 @@ func NewTemporalTaskQueueUpdateBuildIdsAddNewDefaultCommand(cctx *CommandContext s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nCreate a new Task Queue Build ID set, add a Build ID to it, and make it the\noverall Task Queue default. The new set will be incompatible with previous\nsets and versions.\n\n```\ntemporal task-queue update-build-ids add-new-default \\\n --task-queue YourTaskQueue \\\n --build-id \"YourNewBuildId\"\n```\n\n```\n+------------------------------------------------------------------------+\n| NOTICE: This command is limited to Namespaces that support Worker |\n| versioning. Worker versioning is experimental. Versioning commands are |\n| subject to change. |\n+------------------------------------------------------------------------+\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Build ID to be added. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") @@ -2857,6 +2865,8 @@ func NewTemporalTaskQueueUpdateBuildIdsPromoteIdInSetCommand(cctx *CommandContex s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nEstablish an existing Build ID as the default in its Task Queue set. New tasks\ncompatible with this set will now be dispatched to this ID:\n\n```\ntemporal task-queue update-build-ids promote-id-in-set \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\n```\n+------------------------------------------------------------------------+\n| NOTICE: This command is limited to Namespaces that support Worker |\n| versioning. Worker versioning is experimental. Versioning commands are |\n| subject to change. |\n+------------------------------------------------------------------------+\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Build ID to set as default. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") @@ -2888,6 +2898,8 @@ func NewTemporalTaskQueueUpdateBuildIdsPromoteSetCommand(cctx *CommandContext, p s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nPromote a Build ID set to be the default on a Task Queue. Identify the set by\nproviding a Build ID within it. If the set is already the default, this\ncommand has no effect:\n\n```\ntemporal task-queue update-build-ids promote-set \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\n```\n+------------------------------------------------------------------------+\n| NOTICE: This command is limited to Namespaces that support Worker |\n| versioning. Worker versioning is experimental. Versioning commands are |\n| subject to change. |\n+------------------------------------------------------------------------+\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Build ID within the promoted set. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") @@ -2912,11 +2924,13 @@ func NewTemporalTaskQueueVersioningCommand(cctx *CommandContext, parent *Tempora s.Command.Use = "versioning" s.Command.Short = "Manage Task Queue Build ID handling (Deprecated)" if hasHighlighting { - s.Command.Long = "\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m\n\nProvides commands to add, list, remove, or replace Worker Build ID assignment\nand redirect rules associated with Task Queues:\n\n\x1b[1mtemporal task-queue versioning [subcommands] [options] \\\n --task-queue YourTaskQueue\x1b[0m\n\nTask Queues support the following versioning rules and policies:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nProvides commands to add, list, remove, or replace Worker Build ID assignment\nand redirect rules associated with Task Queues:\n\n\x1b[1mtemporal task-queue versioning [subcommands] [options] \\\n --task-queue YourTaskQueue\x1b[0m\n\nTask Queues support the following versioning rules and policies:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." } else { - s.Command.Long = "```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```\n\nProvides commands to add, list, remove, or replace Worker Build ID assignment\nand redirect rules associated with Task Queues:\n\n```\ntemporal task-queue versioning [subcommands] [options] \\\n --task-queue YourTaskQueue\n```\n\nTask Queues support the following versioning rules and policies:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nProvides commands to add, list, remove, or replace Worker Build ID assignment\nand redirect rules associated with Task Queues:\n\n```\ntemporal task-queue versioning [subcommands] [options] \\\n --task-queue YourTaskQueue\n```\n\nTask Queues support the following versioning rules and policies:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.AddCommand(&NewTemporalTaskQueueVersioningAddRedirectRuleCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueVersioningCommitBuildIdCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueVersioningDeleteAssignmentRuleCommand(cctx, &s).Command) @@ -2945,11 +2959,13 @@ func NewTemporalTaskQueueVersioningAddRedirectRuleCommand(cctx *CommandContext, s.Command.Use = "add-redirect-rule [flags]" s.Command.Short = "Add Task Queue redirect rules (Deprecated)" if hasHighlighting { - s.Command.Long = "Add a new redirect rule for a given Task Queue. You may add at most one\nredirect rule for each distinct source build ID:\n\n\x1b[1mtemporal task-queue versioning add-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourSourceBuildID\" \\\n --target-build-id \"YourTargetBuildID\"\x1b[0m\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nAdd a new redirect rule for a given Task Queue. You may add at most one\nredirect rule for each distinct source build ID:\n\n\x1b[1mtemporal task-queue versioning add-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourSourceBuildID\" \\\n --target-build-id \"YourTargetBuildID\"\x1b[0m" } else { - s.Command.Long = "Add a new redirect rule for a given Task Queue. You may add at most one\nredirect rule for each distinct source build ID:\n\n```\ntemporal task-queue versioning add-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourSourceBuildID\" \\\n --target-build-id \"YourTargetBuildID\"\n```\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nAdd a new redirect rule for a given Task Queue. You may add at most one\nredirect rule for each distinct source build ID:\n\n```\ntemporal task-queue versioning add-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourSourceBuildID\" \\\n --target-build-id \"YourTargetBuildID\"\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.SourceBuildId, "source-build-id", "", "Source build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "source-build-id") s.Command.Flags().StringVar(&s.TargetBuildId, "target-build-id", "", "Target build ID. Required.") @@ -2978,11 +2994,13 @@ func NewTemporalTaskQueueVersioningCommitBuildIdCommand(cctx *CommandContext, pa s.Command.Use = "commit-build-id [flags]" s.Command.Short = "Complete Build ID rollout (Deprecated)" if hasHighlighting { - s.Command.Long = "Complete a Build ID's rollout and clean up unnecessary rules that might have\nbeen created during a gradual rollout:\n\n\x1b[1mtemporal task-queue versioning commit-build-id \\\n --task-queue YourTaskQueue\n --build-id \"YourBuildId\"\x1b[0m\n\nThis command automatically applies the following atomic changes:\n\n- Adds an unconditional assignment rule for the target Build ID at the\n end of the list.\n- Removes all previously added assignment rules to the given target\n Build ID.\n- Removes any unconditional assignment rules for other Build IDs.\n\nRejects requests when there have been no recent pollers for this Build ID.\nThis prevents committing invalid Build IDs. Use the \x1b[1m--force\x1b[0m option to\noverride this validation.\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nComplete a Build ID's rollout and clean up unnecessary rules that might have\nbeen created during a gradual rollout:\n\n\x1b[1mtemporal task-queue versioning commit-build-id \\\n --task-queue YourTaskQueue\n --build-id \"YourBuildId\"\x1b[0m\n\nThis command automatically applies the following atomic changes:\n\n- Adds an unconditional assignment rule for the target Build ID at the\n end of the list.\n- Removes all previously added assignment rules to the given target\n Build ID.\n- Removes any unconditional assignment rules for other Build IDs.\n\nRejects requests when there have been no recent pollers for this Build ID.\nThis prevents committing invalid Build IDs. Use the \x1b[1m--force\x1b[0m option to\noverride this validation." } else { - s.Command.Long = "Complete a Build ID's rollout and clean up unnecessary rules that might have\nbeen created during a gradual rollout:\n\n```\ntemporal task-queue versioning commit-build-id \\\n --task-queue YourTaskQueue\n --build-id \"YourBuildId\"\n```\n\nThis command automatically applies the following atomic changes:\n\n- Adds an unconditional assignment rule for the target Build ID at the\n end of the list.\n- Removes all previously added assignment rules to the given target\n Build ID.\n- Removes any unconditional assignment rules for other Build IDs.\n\nRejects requests when there have been no recent pollers for this Build ID.\nThis prevents committing invalid Build IDs. Use the `--force` option to\noverride this validation.\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nComplete a Build ID's rollout and clean up unnecessary rules that might have\nbeen created during a gradual rollout:\n\n```\ntemporal task-queue versioning commit-build-id \\\n --task-queue YourTaskQueue\n --build-id \"YourBuildId\"\n```\n\nThis command automatically applies the following atomic changes:\n\n- Adds an unconditional assignment rule for the target Build ID at the\n end of the list.\n- Removes all previously added assignment rules to the given target\n Build ID.\n- Removes any unconditional assignment rules for other Build IDs.\n\nRejects requests when there have been no recent pollers for this Build ID.\nThis prevents committing invalid Build IDs. Use the `--force` option to\noverride this validation." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Target build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().BoolVar(&s.Force, "force", false, "Bypass recent-poller validation.") @@ -3010,11 +3028,13 @@ func NewTemporalTaskQueueVersioningDeleteAssignmentRuleCommand(cctx *CommandCont s.Command.Use = "delete-assignment-rule [flags]" s.Command.Short = "Removes a Task Queue assignment rule (Deprecated)" if hasHighlighting { - s.Command.Long = "Deletes a rule identified by its index in the Task Queue's list of assignment\nrules.\n\n\x1b[1mtemporal task-queue versioning delete-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index YourIntegerRuleIndex\x1b[0m\n\nBy default, the Task Queue must retain one unconditional rule, such as \"no\nhint filter\" or \"percentage\". Otherwise, the delete operation is rejected.\nUse the \x1b[1m--force\x1b[0m option to override this validation.\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nDeletes a rule identified by its index in the Task Queue's list of assignment\nrules.\n\n\x1b[1mtemporal task-queue versioning delete-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index YourIntegerRuleIndex\x1b[0m\n\nBy default, the Task Queue must retain one unconditional rule, such as \"no\nhint filter\" or \"percentage\". Otherwise, the delete operation is rejected.\nUse the \x1b[1m--force\x1b[0m option to override this validation." } else { - s.Command.Long = "Deletes a rule identified by its index in the Task Queue's list of assignment\nrules.\n\n```\ntemporal task-queue versioning delete-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index YourIntegerRuleIndex\n```\n\nBy default, the Task Queue must retain one unconditional rule, such as \"no\nhint filter\" or \"percentage\". Otherwise, the delete operation is rejected.\nUse the `--force` option to override this validation.\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nDeletes a rule identified by its index in the Task Queue's list of assignment\nrules.\n\n```\ntemporal task-queue versioning delete-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index YourIntegerRuleIndex\n```\n\nBy default, the Task Queue must retain one unconditional rule, such as \"no\nhint filter\" or \"percentage\". Otherwise, the delete operation is rejected.\nUse the `--force` option to override this validation." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().IntVarP(&s.RuleIndex, "rule-index", "i", 0, "Position of the assignment rule to be replaced. Requests for invalid indices will fail. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "rule-index") s.Command.Flags().BoolVar(&s.Force, "force", false, "Bypass one-unconditional-rule validation.") @@ -3041,11 +3061,13 @@ func NewTemporalTaskQueueVersioningDeleteRedirectRuleCommand(cctx *CommandContex s.Command.Use = "delete-redirect-rule [flags]" s.Command.Short = "Removes Build-ID routing rule (Deprecated)" if hasHighlighting { - s.Command.Long = "Deletes the routing rule for the given source Build ID.\n\n\x1b[1mtemporal task-queue versioning delete-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourBuildId\"\x1b[0m\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nDeletes the routing rule for the given source Build ID.\n\n\x1b[1mtemporal task-queue versioning delete-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourBuildId\"\x1b[0m" } else { - s.Command.Long = "Deletes the routing rule for the given source Build ID.\n\n```\ntemporal task-queue versioning delete-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourBuildId\"\n```\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nDeletes the routing rule for the given source Build ID.\n\n```\ntemporal task-queue versioning delete-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourBuildId\"\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.SourceBuildId, "source-build-id", "", "Source Build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "source-build-id") s.Command.Flags().BoolVarP(&s.Yes, "yes", "y", false, "Don't prompt to confirm.") @@ -3069,11 +3091,13 @@ func NewTemporalTaskQueueVersioningGetRulesCommand(cctx *CommandContext, parent s.Command.Use = "get-rules [flags]" s.Command.Short = "Fetch Worker Build ID assignments and redirect rules (Deprecated)" if hasHighlighting { - s.Command.Long = "Retrieve all the Worker Build ID assignments and redirect rules associated\nwith a Task Queue:\n\n\x1b[1mtemporal task-queue versioning get-rules \\\n --task-queue YourTaskQueue\x1b[0m\n\nTask Queues support the following versioning rules:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID.\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nRetrieve all the Worker Build ID assignments and redirect rules associated\nwith a Task Queue:\n\n\x1b[1mtemporal task-queue versioning get-rules \\\n --task-queue YourTaskQueue\x1b[0m\n\nTask Queues support the following versioning rules:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." } else { - s.Command.Long = "Retrieve all the Worker Build ID assignments and redirect rules associated\nwith a Task Queue:\n\n```\ntemporal task-queue versioning get-rules \\\n --task-queue YourTaskQueue\n```\n\nTask Queues support the following versioning rules:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID.\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nRetrieve all the Worker Build ID assignments and redirect rules associated\nwith a Task Queue:\n\n```\ntemporal task-queue versioning get-rules \\\n --task-queue YourTaskQueue\n```\n\nTask Queues support the following versioning rules:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { cctx.Options.Fail(err) @@ -3098,11 +3122,13 @@ func NewTemporalTaskQueueVersioningInsertAssignmentRuleCommand(cctx *CommandCont s.Command.Use = "insert-assignment-rule [flags]" s.Command.Short = "Add an assignment rule at a index (Deprecated)" if hasHighlighting { - s.Command.Long = "Inserts a new assignment rule for this Task Queue. Rules are evaluated in\norder, starting from index 0. The first applicable rule is applied, and the\nrest ignored:\n\n\x1b[1mtemporal task-queue versioning insert-assignment-rule \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\x1b[0m\n\nIf you do not specify a \x1b[1m--rule-index\x1b[0m, this command inserts at index 0.\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nInserts a new assignment rule for this Task Queue. Rules are evaluated in\norder, starting from index 0. The first applicable rule is applied, and the\nrest ignored:\n\n\x1b[1mtemporal task-queue versioning insert-assignment-rule \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\x1b[0m\n\nIf you do not specify a \x1b[1m--rule-index\x1b[0m, this command inserts at index 0." } else { - s.Command.Long = "Inserts a new assignment rule for this Task Queue. Rules are evaluated in\norder, starting from index 0. The first applicable rule is applied, and the\nrest ignored:\n\n```\ntemporal task-queue versioning insert-assignment-rule \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\nIf you do not specify a `--rule-index`, this command inserts at index 0.\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nInserts a new assignment rule for this Task Queue. Rules are evaluated in\norder, starting from index 0. The first applicable rule is applied, and the\nrest ignored:\n\n```\ntemporal task-queue versioning insert-assignment-rule \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\nIf you do not specify a `--rule-index`, this command inserts at index 0." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Target Build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().IntVarP(&s.RuleIndex, "rule-index", "i", 0, "Insertion position. Ranges from 0 (insert at start) to count (append). Any number greater than the count is treated as \"append\".") @@ -3133,11 +3159,13 @@ func NewTemporalTaskQueueVersioningReplaceAssignmentRuleCommand(cctx *CommandCon s.Command.Use = "replace-assignment-rule [flags]" s.Command.Short = "Update assignment rule at index (Deprecated)" if hasHighlighting { - s.Command.Long = "Change an assignment rule for this Task Queue. By default, this enforces one\nunconditional rule (no hint filter or percentage). Otherwise, the operation\nwill be rejected. Set \x1b[1mforce\x1b[0m to true to bypass this validation.\n\n\x1b[1mtemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\"\x1b[0m\n\nTo assign multiple assignment rules to a single Build ID, use\n'insert-assignment-rule'.\n\nTo update the percent:\n\n\x1b[1mtemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\" \\\n --percentage AnIntegerPercent\x1b[0m\n\nPercent may vary between 0 and 100 (default).\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nChange an assignment rule for this Task Queue. By default, this enforces one\nunconditional rule (no hint filter or percentage). Otherwise, the operation\nwill be rejected. Set \x1b[1mforce\x1b[0m to true to bypass this validation.\n\n\x1b[1mtemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\"\x1b[0m\n\nTo assign multiple assignment rules to a single Build ID, use\n'insert-assignment-rule'.\n\nTo update the percent:\n\n\x1b[1mtemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\" \\\n --percentage AnIntegerPercent\x1b[0m\n\nPercent may vary between 0 and 100 (default)." } else { - s.Command.Long = "Change an assignment rule for this Task Queue. By default, this enforces one\nunconditional rule (no hint filter or percentage). Otherwise, the operation\nwill be rejected. Set `force` to true to bypass this validation.\n\n```\ntemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\"\n```\n\nTo assign multiple assignment rules to a single Build ID, use\n'insert-assignment-rule'.\n\nTo update the percent:\n\n```\ntemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\" \\\n --percentage AnIntegerPercent\n```\n\nPercent may vary between 0 and 100 (default).\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nChange an assignment rule for this Task Queue. By default, this enforces one\nunconditional rule (no hint filter or percentage). Otherwise, the operation\nwill be rejected. Set `force` to true to bypass this validation.\n\n```\ntemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\"\n```\n\nTo assign multiple assignment rules to a single Build ID, use\n'insert-assignment-rule'.\n\nTo update the percent:\n\n```\ntemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\" \\\n --percentage AnIntegerPercent\n```\n\nPercent may vary between 0 and 100 (default)." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Target Build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().IntVarP(&s.RuleIndex, "rule-index", "i", 0, "Position of the assignment rule to be replaced. Requests for invalid indices will fail. Required.") @@ -3168,11 +3196,13 @@ func NewTemporalTaskQueueVersioningReplaceRedirectRuleCommand(cctx *CommandConte s.Command.Use = "replace-redirect-rule [flags]" s.Command.Short = "Change the target for a Build ID's redirect (Deprecated)" if hasHighlighting { - s.Command.Long = "Updates a Build ID's redirect rule on a Task Queue by replacing its target\nBuild ID:\n\n\x1b[1mtemporal task-queue versioning replace-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id YourSourceBuildId \\\n --target-build-id YourNewTargetBuildId\x1b[0m\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nUpdates a Build ID's redirect rule on a Task Queue by replacing its target\nBuild ID:\n\n\x1b[1mtemporal task-queue versioning replace-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id YourSourceBuildId \\\n --target-build-id YourNewTargetBuildId\x1b[0m" } else { - s.Command.Long = "Updates a Build ID's redirect rule on a Task Queue by replacing its target\nBuild ID:\n\n```\ntemporal task-queue versioning replace-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id YourSourceBuildId \\\n --target-build-id YourNewTargetBuildId\n```\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nUpdates a Build ID's redirect rule on a Task Queue by replacing its target\nBuild ID:\n\n```\ntemporal task-queue versioning replace-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id YourSourceBuildId \\\n --target-build-id YourNewTargetBuildId\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.SourceBuildId, "source-build-id", "", "Source Build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "source-build-id") s.Command.Flags().StringVar(&s.TargetBuildId, "target-build-id", "", "Target Build ID. Required.") diff --git a/internal/temporalcli/commands.go b/internal/temporalcli/commands.go index 116de4800..ecb52515d 100644 --- a/internal/temporalcli/commands.go +++ b/internal/temporalcli/commands.go @@ -497,6 +497,11 @@ func (c *TemporalCommand) initCommand(cctx *CommandContext) { } cctx.ActuallyRanCommand = true + // Print deprecation warning to stderr so it doesn't break JSON output + if msg, ok := cmd.Annotations["deprecationWarning"]; ok { + fmt.Fprintf(cctx.Options.Stderr, "warning: %s\n", strings.ToLower(strings.TrimRight(msg, "."))) + } + if cctx.Options.DeprecatedEnvConfig.EnvConfigName != "default" { if _, ok := cctx.DeprecatedEnvConfigValues[cctx.Options.DeprecatedEnvConfig.EnvConfigName]; !ok { if _, ok := cmd.Annotations["ignoresMissingEnv"]; !ok { diff --git a/internal/temporalcli/commands.taskqueue_build_id_test.go b/internal/temporalcli/commands.taskqueue_build_id_test.go index 365bf9e1b..fb137ac28 100644 --- a/internal/temporalcli/commands.taskqueue_build_id_test.go +++ b/internal/temporalcli/commands.taskqueue_build_id_test.go @@ -180,3 +180,45 @@ func (s *SharedServerSuite) TestTaskQueue_BuildId() { }, }, jsonReachOut) } + +func (s *SharedServerSuite) TestDeprecatedCommand_WarnsOnStderr() { + taskQueue := uuid.NewString() + + // Run a deprecated command + res := s.Execute( + "task-queue", "update-build-ids", "add-new-default", + "--address", s.Address(), + "--task-queue", taskQueue, + "--build-id", "1.0", + ) + s.NoError(res.Err) + + // Deprecation warning must appear on stderr + stderr := res.Stderr.String() + s.Contains(stderr, "warning:") + s.Contains(stderr, "deprecated") + + // Warning must not appear on stdout (would break JSON consumers) + s.NotContains(res.Stdout.String(), "warning:") +} + +func (s *SharedServerSuite) TestDeprecatedCommand_WarnsOnStderr_JSON() { + taskQueue := uuid.NewString() + + // Run a deprecated command with JSON output + res := s.Execute( + "task-queue", "get-build-ids", + "-o", "json", + "--address", s.Address(), + "--task-queue", taskQueue, + ) + s.NoError(res.Err) + + // Deprecation warning must appear on stderr even with JSON output + stderr := res.Stderr.String() + s.Contains(stderr, "warning:") + s.Contains(stderr, "deprecated") + + // Stdout must be valid JSON, not polluted by warnings + s.NotContains(res.Stdout.String(), "warning:") +} diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 1d383d179..7d67822a2 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -2870,14 +2870,9 @@ commands: configuration for the task queue. - name: temporal task-queue get-build-id-reachability - summary: Show Build ID availability (Deprecated) + summary: Show Build ID availability + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Show if a given Build ID can be used for new, existing, or closed Workflows in Namespaces that support Worker versioning: @@ -2917,14 +2912,9 @@ commands: Can be passed multiple times. - name: temporal task-queue get-build-ids - summary: Fetch Build ID versions (Deprecated) + summary: Fetch Build ID versions + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Fetch sets of compatible Build IDs for specified Task Queues and display their information: @@ -2966,14 +2956,9 @@ commands: short: t - name: temporal task-queue update-build-ids - summary: Manage Build IDs (Deprecated) + summary: Manage Build IDs + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Add or change a Task Queue's compatible Build IDs for Namespaces using Worker versioning: @@ -3018,14 +3003,9 @@ commands: description: Set the expanded Build ID set as the Task Queue default. - name: temporal task-queue update-build-ids add-new-default - summary: Set new default Build ID set (Deprecated) + summary: Set new default Build ID set + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Create a new Task Queue Build ID set, add a Build ID to it, and make it the overall Task Queue default. The new set will be incompatible with previous sets and versions. @@ -3055,14 +3035,9 @@ commands: short: t - name: temporal task-queue update-build-ids promote-id-in-set - summary: Set Build ID as set default (Deprecated) + summary: Set Build ID as set default + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Establish an existing Build ID as the default in its Task Queue set. New tasks compatible with this set will now be dispatched to this ID: @@ -3091,14 +3066,9 @@ commands: short: t - name: temporal task-queue update-build-ids promote-set - summary: Promote Build ID set (Deprecated) + summary: Promote Build ID set + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Promote a Build ID set to be the default on a Task Queue. Identify the set by providing a Build ID within it. If the set is already the default, this command has no effect: @@ -3128,14 +3098,10 @@ commands: short: t - name: temporal task-queue versioning - summary: Manage Task Queue Build ID handling (Deprecated) + summary: Manage Task Queue Build ID handling + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` - Provides commands to add, list, remove, or replace Worker Build ID assignment and redirect rules associated with Task Queues: @@ -3162,7 +3128,9 @@ commands: required: true - name: temporal task-queue versioning add-redirect-rule - summary: Add Task Queue redirect rules (Deprecated) + summary: Add Task Queue redirect rules + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Add a new redirect rule for a given Task Queue. You may add at most one redirect rule for each distinct source build ID: @@ -3173,12 +3141,6 @@ commands: --source-build-id "YourSourceBuildID" \ --target-build-id "YourTargetBuildID" ``` - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: source-build-id type: string @@ -3194,7 +3156,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning commit-build-id - summary: Complete Build ID rollout (Deprecated) + summary: Complete Build ID rollout + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Complete a Build ID's rollout and clean up unnecessary rules that might have been created during a gradual rollout: @@ -3216,12 +3180,6 @@ commands: Rejects requests when there have been no recent pollers for this Build ID. This prevents committing invalid Build IDs. Use the `--force` option to override this validation. - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: build-id type: string @@ -3236,7 +3194,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning delete-assignment-rule - summary: Removes a Task Queue assignment rule (Deprecated) + summary: Removes a Task Queue assignment rule + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Deletes a rule identified by its index in the Task Queue's list of assignment rules. @@ -3250,12 +3210,6 @@ commands: By default, the Task Queue must retain one unconditional rule, such as "no hint filter" or "percentage". Otherwise, the delete operation is rejected. Use the `--force` option to override this validation. - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: rule-index type: int @@ -3273,7 +3227,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning delete-redirect-rule - summary: Removes Build-ID routing rule (Deprecated) + summary: Removes Build-ID routing rule + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Deletes the routing rule for the given source Build ID. @@ -3282,12 +3238,6 @@ commands: --task-queue YourTaskQueue \ --source-build-id "YourBuildId" ``` - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: source-build-id type: string @@ -3299,7 +3249,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning get-rules - summary: Fetch Worker Build ID assignments and redirect rules (Deprecated) + summary: Fetch Worker Build ID assignments and redirect rules + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Retrieve all the Worker Build ID assignments and redirect rules associated with a Task Queue: @@ -3319,14 +3271,11 @@ commands: Build ID. You may add at most one redirect rule for each source Build ID. Redirect rules require that a target Build ID is fully compatible with the source Build ID. - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` - name: temporal task-queue versioning insert-assignment-rule - summary: Add an assignment rule at a index (Deprecated) + summary: Add an assignment rule at a index + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Inserts a new assignment rule for this Task Queue. Rules are evaluated in order, starting from index 0. The first applicable rule is applied, and the @@ -3339,12 +3288,6 @@ commands: ``` If you do not specify a `--rule-index`, this command inserts at index 0. - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: build-id type: string @@ -3369,7 +3312,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning replace-assignment-rule - summary: Update assignment rule at index (Deprecated) + summary: Update assignment rule at index + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Change an assignment rule for this Task Queue. By default, this enforces one unconditional rule (no hint filter or percentage). Otherwise, the operation @@ -3396,12 +3341,6 @@ commands: ``` Percent may vary between 0 and 100 (default). - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: build-id type: string @@ -3428,7 +3367,9 @@ commands: description: Bypass the validation that one unconditional rule remains. - name: temporal task-queue versioning replace-redirect-rule - summary: Change the target for a Build ID's redirect (Deprecated) + summary: Change the target for a Build ID's redirect + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Updates a Build ID's redirect rule on a Task Queue by replacing its target Build ID: @@ -3439,12 +3380,6 @@ commands: --source-build-id YourSourceBuildId \ --target-build-id YourNewTargetBuildId ``` - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: source-build-id type: string From 69b2a24c4ce60fbf7eadcfa44ef0c9fa71199197 Mon Sep 17 00:00:00 2001 From: Sean Kane Date: Mon, 15 Jun 2026 08:49:27 -0600 Subject: [PATCH 21/23] fix: tls is not added for profiles without tls (#1089) ## Related issues Closes #1077 ## What changed? The helper to iterate over `envConfigPropsToFieldNames` has a side effect if the parent is nil and `failIfParentNotFound=true` it will set `confProfile.TLS = &envconvfig.ClientConfigTLS{}`. When the `k=="tls"` check happens, `confProfile.TLS != nil` and it will emit `tls: true`. Map iteration is non-deterministic and there are 9 `tls.*` keys vs 1 `tls` key so the mutation is more likely to happen before checking the `tls` key. ## Checklist **Tests** - [X] Added unit test Co-authored-by: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> (cherry picked from commit 79e9f8f23cd63a244c2606fa04ad4b6ce7327b4c) --- internal/temporalcli/commands.config.go | 8 ++++++- internal/temporalcli/commands.config_test.go | 24 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/internal/temporalcli/commands.config.go b/internal/temporalcli/commands.config.go index e8a423dd0..0b88bfaac 100644 --- a/internal/temporalcli/commands.config.go +++ b/internal/temporalcli/commands.config.go @@ -111,13 +111,19 @@ func (c *TemporalConfigGetCommand) run(cctx *CommandContext, _ []string) error { } return cctx.Printer.PrintStructured(tomlConf.Profiles[profileName], printer.StructuredOptions{}) } else { + // Capture whether TLS is configured before the loop below. Looking up + // any "tls.*" property via reflectEnvConfigProp lazily initializes + // confProfile.TLS to a non-nil empty struct, which would otherwise make + // TLS appear configured when it is not (#1077). + tlsConfigured := confProfile.TLS != nil + // Get every property individually as a property-value pair except zero // vals var props []prop for k := range envConfigPropsToFieldNames { // TLS is a special case if k == "tls" { - if confProfile.TLS != nil { + if tlsConfigured { props = append(props, prop{Property: "tls", Value: true}) } continue diff --git a/internal/temporalcli/commands.config_test.go b/internal/temporalcli/commands.config_test.go index 5cc89ec86..9375402bf 100644 --- a/internal/temporalcli/commands.config_test.go +++ b/internal/temporalcli/commands.config_test.go @@ -151,6 +151,30 @@ disable_host_verification = true`)) } } +func TestConfig_Get_NoTLSWhenUnconfigured(t *testing.T) { + // Regression test for #1077: `config get` (table output) must not display + // "tls true" for a profile that has no TLS configuration. + h := NewCommandHarness(t) + defer h.Close() + + f, err := os.CreateTemp("", "") + h.NoError(err) + defer os.Remove(f.Name()) + _, err = f.Write([]byte(` +[profile.devtest] +address = "localhost:17233" +namespace = "default"`)) + f.Close() + h.NoError(err) + h.Options.EnvLookup = EnvLookupMap{"TEMPORAL_CONFIG_FILE": f.Name(), "TEMPORAL_PROFILE": "devtest"} + + res := h.Execute("config", "get") + h.NoError(res.Err) + h.Contains(res.Stdout.String(), "localhost:17233") + // No TLS section was configured, so "tls" should not appear at all. + h.NotContains(res.Stdout.String(), "tls") +} + func TestConfig_TLS_Boolean(t *testing.T) { h := NewCommandHarness(t) defer h.Close() From fc67a996591057f3ca2182f071ae95e8dc7049fc Mon Sep 17 00:00:00 2001 From: Jessica Laughlin Date: Wed, 24 Jun 2026 20:42:18 -0400 Subject: [PATCH 22/23] Clarify activity pause timeout behavior (#1099) ## What changed? Clarifies `temporal activity pause --help` to state that pausing an Activity does not stop or extend the Activity's Schedule-To-Close Timeout. Regenerated `internal/temporalcli/commands.gen.go` from `internal/temporalcli/commands.yaml`. ## Why? The existing help explains that Pause prevents future retries and does not interrupt some in-flight attempts, but it does not mention that Schedule-To-Close continues running while paused. That can surprise operators because "pause" can imply the Activity is safe from timing out. This adds the timeout caveat and points users to `temporal activity update-options` before a long pause. ## Testing ```bash go run ./cmd/gen-commands \ -input internal/temporalcli/commands.yaml \ -pkg temporalcli \ -context "*CommandContext" \ > internal/temporalcli/commands.gen.go go run ./cmd/temporal activity pause --help go test ./cmd/gen-commands ``` Verified the help output includes the Schedule-To-Close warning. (cherry picked from commit 8c3e3b2e07b5e0faa590ef73ea6d9ab1a07561f7) --- internal/temporalcli/commands.gen.go | 4 ++-- internal/temporalcli/commands.yaml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 4985840e1..4acec6e5d 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -735,9 +735,9 @@ func NewTemporalActivityPauseCommand(cctx *CommandContext, parent *TemporalActiv s.Command.Use = "pause [flags]" s.Command.Short = "Pause an Activity" if hasHighlighting { - s.Command.Long = "Pause an Activity. Not supported for Standalone Activities.\n\nIf the Activity is not currently running (e.g. because it previously\nfailed), it will not be run again until it is unpaused.\n\nHowever, if the Activity is currently running, it will run until the next\ntime it fails, completes, or times out, at which point the pause will kick in.\n\nIf the Activity is on its last retry attempt and fails, the failure will\nbe returned to the caller, just as if the Activity had not been paused.\n\nSpecify the Activity and Workflow IDs:\n\n\x1b[1mtemporal activity pause \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\x1b[0m\n\nTo later unpause the activity, see unpause. You may also want to\nreset the activity to unpause it while also starting it from the beginning." + s.Command.Long = "Pause an Activity. Not supported for Standalone Activities.\n\nIf the Activity is not currently running (e.g. because it previously\nfailed), it will not be run again until it is unpaused.\n\nHowever, if the Activity is currently running, it will run until the next\ntime it fails, completes, or times out, at which point the pause will kick in.\n\nPause does not stop or extend the Activity's Schedule-To-Close Timeout.\nA paused Activity can still time out. Use \x1b[1mtemporal activity update-options\x1b[0m\nto extend timeout settings before a long pause.\n\nIf the Activity is on its last retry attempt and fails, the failure will\nbe returned to the caller, just as if the Activity had not been paused.\n\nSpecify the Activity and Workflow IDs:\n\n\x1b[1mtemporal activity pause \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\x1b[0m\n\nTo later unpause the activity, see unpause. You may also want to\nreset the activity to unpause it while also starting it from the beginning." } else { - s.Command.Long = "Pause an Activity. Not supported for Standalone Activities.\n\nIf the Activity is not currently running (e.g. because it previously\nfailed), it will not be run again until it is unpaused.\n\nHowever, if the Activity is currently running, it will run until the next\ntime it fails, completes, or times out, at which point the pause will kick in.\n\nIf the Activity is on its last retry attempt and fails, the failure will\nbe returned to the caller, just as if the Activity had not been paused.\n\nSpecify the Activity and Workflow IDs:\n\n```\ntemporal activity pause \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\n```\n\nTo later unpause the activity, see unpause. You may also want to\nreset the activity to unpause it while also starting it from the beginning." + s.Command.Long = "Pause an Activity. Not supported for Standalone Activities.\n\nIf the Activity is not currently running (e.g. because it previously\nfailed), it will not be run again until it is unpaused.\n\nHowever, if the Activity is currently running, it will run until the next\ntime it fails, completes, or times out, at which point the pause will kick in.\n\nPause does not stop or extend the Activity's Schedule-To-Close Timeout.\nA paused Activity can still time out. Use `temporal activity update-options`\nto extend timeout settings before a long pause.\n\nIf the Activity is on its last retry attempt and fails, the failure will\nbe returned to the caller, just as if the Activity had not been paused.\n\nSpecify the Activity and Workflow IDs:\n\n```\ntemporal activity pause \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\n```\n\nTo later unpause the activity, see unpause. You may also want to\nreset the activity to unpause it while also starting it from the beginning." } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVarP(&s.ActivityId, "activity-id", "a", "", "The Activity ID to pause. Required.") diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 7d67822a2..3014f6848 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -471,6 +471,10 @@ commands: However, if the Activity is currently running, it will run until the next time it fails, completes, or times out, at which point the pause will kick in. + Pause does not stop or extend the Activity's Schedule-To-Close Timeout. + A paused Activity can still time out. Use `temporal activity update-options` + to extend timeout settings before a long pause. + If the Activity is on its last retry attempt and fails, the failure will be returned to the caller, just as if the Activity had not been paused. From 7095f121dcfcd21527c9bfd67dd3bd60b8e8193d Mon Sep 17 00:00:00 2001 From: "alex.stanfield" <13949480+chaptersix@users.noreply.github.com> Date: Thu, 2 Jul 2026 11:28:31 -0500 Subject: [PATCH 23/23] ci: update API key test endpoint to ca-central-1 Backports the CI endpoint fix from #1087 (us-east-1 -> ca-central-1) without the server-dependent Nexus command changes. Fixes the 'Request unauthorized' failure in the cloud API key test steps. --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2314b3ef3..6a5df116d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -108,7 +108,7 @@ jobs: - name: Test cloud API key env var if: ${{ matrix.cloudTestTarget && env.HAS_SECRETS == 'true' }} env: - TEMPORAL_ADDRESS: us-east-1.aws.api.temporal.io:7233 + TEMPORAL_ADDRESS: ca-central-1.aws.api.temporal.io:7233 TEMPORAL_NAMESPACE: ${{ vars.TEMPORAL_CLIENT_NAMESPACE }} TEMPORAL_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} shell: bash @@ -117,7 +117,7 @@ jobs: - name: Test cloud API key arg if: ${{ matrix.cloudTestTarget && env.HAS_SECRETS == 'true' }} env: - TEMPORAL_ADDRESS: us-east-1.aws.api.temporal.io:7233 + TEMPORAL_ADDRESS: ca-central-1.aws.api.temporal.io:7233 TEMPORAL_NAMESPACE: ${{ vars.TEMPORAL_CLIENT_NAMESPACE }} shell: bash run: go run ./cmd/temporal workflow list --limit 2 --api-key ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }}