From 60e8b8d4411c2177fa302f96275fac193d0ea381 Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:44:56 -0500 Subject: [PATCH 01/15] test: fix concurrent start test assertions (#1153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What changed? Supersedes #1094, whose contributor is no longer responding. This branch replays Nanook’s two authored commits unchanged so their contribution remains credited, then adds a small maintainer follow-up to restore the successful `client.Dial` guard and preserve the accepted cleanup/logging review suggestions. ## Validation - `go test ./internal/temporalcli -run '^TestServer_StartDev_ConcurrentStarts$' -count=1` ## Notes The original PR remains open and untouched. --------- Co-authored-by: Nanook (cherry picked from commit b646628f6da904772afef7cbed6de5abdb3f52a0) --- internal/temporalcli/commands.server_test.go | 84 +++++++++++++++----- internal/temporalcli/commands_test.go | 18 +++-- 2 files changed, 79 insertions(+), 23 deletions(-) diff --git a/internal/temporalcli/commands.server_test.go b/internal/temporalcli/commands.server_test.go index 26b92cf60..f00501844 100644 --- a/internal/temporalcli/commands.server_test.go +++ b/internal/temporalcli/commands.server_test.go @@ -2,6 +2,7 @@ package temporalcli_test import ( "context" + "fmt" "net" "os" "path/filepath" @@ -139,58 +140,105 @@ func startDevServerAndRunSimpleTest(t *testing.T, args []string, dialAddress str } func TestServer_StartDev_ConcurrentStarts(t *testing.T) { - startOne := func() { - h := NewCommandHarness(t) - defer h.Close() + h := NewCommandHarness(t) + defer h.Close() + + startOne := func() error { + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() // Start in background, then wait for client to be able to connect 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", "--log-level", "never") + resCh <- h.ExecuteWithContext(ctx, "server", "start-dev", "-p", port, "--http-port", httpPort, "--headless", "--log-level", "never") }() // Try to connect for a bit while checking for error var cl client.Client - h.EventuallyWithT(func(t *assert.CollectT) { + var lastDialErr error + ticker := time.NewTicker(200 * time.Millisecond) + defer ticker.Stop() + timeout := time.NewTimer(3 * time.Second) + defer timeout.Stop() + + waitForServer: + for { + select { + case res := <-resCh: + return concurrentStartCommandResultError("got early server result", res) + case <-ticker.C: + var err error + cl, err = client.Dial(client.Options{HostPort: "127.0.0.1:" + port, Logger: testLogger{t: t}}) + if err == nil { + break waitForServer + } + lastDialErr = err + case <-timeout.C: + break waitForServer + } + } + if cl == nil { + cancel() select { + case <-time.After(20 * time.Second): + return fmt.Errorf("server was not reachable after 3 seconds: %w; also did not clean up within 20 seconds", lastDialErr) case res := <-resCh: - require.NoError(t, res.Err) - require.Fail(t, "got early server result") - default: + if res.Err != nil { + return fmt.Errorf( + "server was not reachable after 3 seconds: %w; cleanup failed: %w", + lastDialErr, + res.Err, + ) + } + return fmt.Errorf("server was not reachable after 3 seconds: %w", lastDialErr) } - var err error - cl, err = client.Dial(client.Options{HostPort: "127.0.0.1:" + port, Logger: testLogger{t: h.t}}) - assert.NoError(t, err) - }, 3*time.Second, 200*time.Millisecond) + } defer cl.Close() // Send an interrupt by cancelling context - h.CancelContext() + cancel() select { case <-time.After(20 * time.Second): - h.Fail("didn't cleanup after 20 seconds") + return fmt.Errorf("didn't cleanup after 20 seconds") case res := <-resCh: - h.NoError(res.Err) + if res.Err != nil { + return concurrentStartCommandResultError("server returned error", res) + } } + return nil } - // Start 40 dev server instances, with 8 concurrent executions + // Start 40 dev server instances, with 6 concurrent executions. instanceCounter := atomic.Int32{} instanceCounter.Store(40) + errCh := make(chan error, 40) wg := &sync.WaitGroup{} for i := 0; i < 6; i++ { wg.Add(1) go func() { + defer wg.Done() for instanceCounter.Add(-1) >= 0 { - startOne() + if err := startOne(); err != nil { + errCh <- err + } } - wg.Done() }() } wg.Wait() + close(errCh) + for err := range errCh { + require.NoError(t, err) + } +} + +func concurrentStartCommandResultError(msg string, res *CommandResult) error { + if res.Err != nil { + return fmt.Errorf("%s: %w (stdout: %q, stderr: %q)", msg, res.Err, res.Stdout.String(), res.Stderr.String()) + } + return fmt.Errorf("%s (stdout: %q, stderr: %q)", msg, res.Stdout.String(), res.Stderr.String()) } func TestServer_StartDev_WithSearchAttributes(t *testing.T) { diff --git a/internal/temporalcli/commands_test.go b/internal/temporalcli/commands_test.go index b77732bcb..9feb1c9c6 100644 --- a/internal/temporalcli/commands_test.go +++ b/internal/temporalcli/commands_test.go @@ -151,11 +151,23 @@ type CommandResult struct { } func (h *CommandHarness) Execute(args ...string) *CommandResult { + ctx, cancel := context.WithCancel(h.Context) + h.t.Cleanup(cancel) + defer cancel() + return h.execute(ctx, &h.Stdin, args...) +} + +func (h *CommandHarness) ExecuteWithContext(ctx context.Context, args ...string) *CommandResult { + var stdin bytes.Buffer + return h.execute(ctx, &stdin, args...) +} + +func (h *CommandHarness) execute(ctx context.Context, stdin io.Reader, args ...string) *CommandResult { // Copy options, update as needed res := &CommandResult{} options := h.Options // Set stdio - options.Stdin = &h.Stdin + options.Stdin = stdin options.Stdout = &res.Stdout options.Stderr = &res.Stderr // Set args @@ -175,10 +187,6 @@ func (h *CommandHarness) Execute(args ...string) *CommandResult { res.Err = err } - // Run - ctx, cancel := context.WithCancel(h.Context) - h.t.Cleanup(cancel) - defer cancel() h.t.Logf("Calling: %v", strings.Join(args, " ")) temporalcli.Execute(ctx, options) if res.Stdout.Len() > 0 { From 88f39762f42e2e65937e4e248fe26db27844e918 Mon Sep 17 00:00:00 2001 From: mani-j9 Date: Wed, 5 Aug 2026 13:11:14 -0700 Subject: [PATCH 02/15] Gate AWS Lambda role/external-id requirement behind --aws-lambda-skip-role-and-external-id (#1140) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What changed? ### Why `create-version` and `update-version-compute-config` currently require `--aws-lambda-assume-role-arn` and `--aws-lambda-assume-role-external-id` whenever `--aws-lambda-function-arn` is set. The Temporal server governs whether these are actually mandatory via the global `require_role_and_external_id` setting (default `true`), so a role-less config is valid against servers where that setting is disabled β€” e.g. local dev against LocalStack. CLI's validation needs to be relaxed to allow role less config creation. ### How This PR allows the role less config by adding a new CLI parameter `--aws-lambda-skip-role-and-external-id`. By default the CLI keeps requiring both(role and id) fields and fails fast with an actionable client-side error that names the missing flag. Passing the flag specifically opts out of the client-side check and defers entirely to the server's policy. ### Testing With flag set as default true ``` mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment create-version --address 127.0.0.1:7333 --deployment-name test-deploy --build-id b-noRole \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 Error: missing required AWS Lambda provider detail: role mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment create-version --address 127.0.0.1:7333 --deployment-name test-deploy --build-id b-noEid \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-assume-role-arn arn:aws:iam::123456789012:role/MyServiceRole Error: missing required AWS Lambda provider detail: role_external_id mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment create-version --address 127.0.0.1:7333 --deployment-name test-deploy --build-id b-skipRole \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-assume-role-arn arn:aws:iam::123456789012:role/MyServiceRole \ --aws-lambda-skip-role-and-external-id Error: AWS Lambda provider detail "role" must not be set when --aws-lambda-skip-role-and-external-id is passed mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment create-version --address 127.0.0.1:7333 --deployment-name test-deploy --build-id b-skipOnly \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-skip-role-and-external-id Error: error creating worker deployment version: no Worker Deployment found with name 'test-deploy'; does your Worker Deployment have pollers? mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment create-version --address 127.0.0.1:7333 --deployment-name test-deploy --build-id b-skipEid \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-assume-role-external-id external-id \ --aws-lambda-skip-role-and-external-id Error: AWS Lambda provider detail "role_external_id" must not be set when --aws-lambda-skip-role-and-external-id is passed mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment update-version-compute-config --address 127.0.0.1:7333 --deployment-name test-deploy --build-id b-skipRole \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-assume-role-arn arn:aws:iam::123456789012:role/MyServiceRole \ --aws-lambda-skip-role-and-external-id Error: AWS Lambda provider detail "role" must not be set when --aws-lambda-skip-role-and-external-id is passed mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment update-version-compute-config --address 127.0.0.1:7333 --deployment-name test-deploy --build-id b-skipOnly \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-skip-role-and-external-id Error: error updating worker deployment version compute config: build ID 'b-skipOnly' not found in Worker Deployment 'test-deploy' mani@manis-MacBook-Pro temporal-cli % ``` for when the local server's require_role_and_external_id flag is set to false ``` mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment create-version --address 127.0.0.1:7333 --deployment-name skip-demo --build-id skip-test-1 \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-skip-role-and-external-id Error: error creating worker deployment version: no Worker Deployment found with name 'skip-demo'; does your Worker Deployment have pollers? mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment create-version --address 127.0.0.1:7333 --deployment-name skip-demo --build-id role-test-1 \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-assume-role-arn arn:aws:iam::123456789012:role/MyServiceRole \ --aws-lambda-assume-role-external-id external-id Error: error creating worker deployment version: no Worker Deployment found with name 'skip-demo'; does your Worker Deployment have pollers? mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment create-version --address 127.0.0.1:7333 --deployment-name skip-demo --build-id skip-neg-1 \ --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 \ --aws-lambda-assume-role-arn arn:aws:iam::123456789012:role/MyServiceRole \ --aws-lambda-skip-role-and-external-id Error: AWS Lambda provider detail "role" must not be set when --aws-lambda-skip-role-and-external-id is passed mani@manis-MacBook-Pro temporal-cli % ./temporal worker deployment update-version-compute-config --address 127.0.0.1:7333 --deployment-name skip-demo --build-id poller-build --aws-lambda-function-arn arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:1 --aws-lambda-skip-role-and-external-id Error: error updating worker deployment version compute config: default: lambda GetFunction failed: operation error Lambda: GetFunction, get identity: get credentials: failed to refresh cached credentials, no EC2 IMDS role found, operation error ec2imds: GetMetadata, request canceled, context deadline exceeded mani@manis-MacBook-Pro temporal-cli % ``` --------- Co-authored-by: Claude Opus 4.8 (cherry picked from commit 6e3ae1e97279b1b523e68e621e79783ffdfa6188) --- internal/temporalcli/commands.gen.go | 58 ++++++++++--------- .../temporalcli/commands.worker.deployment.go | 22 ++++++- .../commands.worker.deployment_test.go | 32 ++++++++++ internal/temporalcli/commands.yaml | 26 +++++++-- 4 files changed, 104 insertions(+), 34 deletions(-) diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index f1456dbdb..0913b11c4 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -3304,17 +3304,18 @@ type TemporalWorkerDeploymentCreateVersionCommand struct { Parent *TemporalWorkerDeploymentCommand Command cobra.Command DeploymentVersionOptions - AwsLambdaFunctionArn string - AwsLambdaAssumeRoleArn string - AwsLambdaAssumeRoleExternalId string - GcpCloudRunProject string - GcpCloudRunRegion string - GcpCloudRunWorkerPool string - GcpCloudRunServiceAccount string - GcpCloudRunMinInstances int - GcpCloudRunMaxInstances int - GcpCloudRunInitialInstances int - GcpCloudRunUtilizationTarget float32 + AwsLambdaFunctionArn string + AwsLambdaAssumeRoleArn string + AwsLambdaAssumeRoleExternalId string + AwsLambdaSkipRoleAndExternalId bool + GcpCloudRunProject string + GcpCloudRunRegion string + GcpCloudRunWorkerPool string + GcpCloudRunServiceAccount string + GcpCloudRunMinInstances int + GcpCloudRunMaxInstances int + GcpCloudRunInitialInstances int + GcpCloudRunUtilizationTarget float32 } func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, parent *TemporalWorkerDeploymentCommand) *TemporalWorkerDeploymentCreateVersionCommand { @@ -3330,8 +3331,9 @@ func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, paren } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") - s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleArn, "aws-lambda-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is specified.") - s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleExternalId, "aws-lambda-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-lambda-function-arn is specified.") + s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleArn, "aws-lambda-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is specified, and must be omitted when --aws-lambda-skip-role-and-external-id is passed.") + s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleExternalId, "aws-lambda-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-lambda-function-arn is specified, and must be omitted when --aws-lambda-skip-role-and-external-id is passed.") + s.Command.Flags().BoolVar(&s.AwsLambdaSkipRoleAndExternalId, "aws-lambda-skip-role-and-external-id", false, "When --aws-lambda-function-arn is specified, --aws-lambda-assume-role-arn and --aws-lambda-assume-role-external-id are required unless this flag is passed, in which case both must be omitted.") s.Command.Flags().StringVar(&s.GcpCloudRunProject, "gcp-cloud-run-project", "", "GCP project ID hosting the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunRegion, "gcp-cloud-run-region", "", "Region of the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunWorkerPool, "gcp-cloud-run-worker-pool", "", "GCP Cloud Run worker pool name to scale when there are no active pollers for task queue targets in the Worker Deployment.") @@ -3643,18 +3645,19 @@ type TemporalWorkerDeploymentUpdateVersionComputeConfigCommand struct { Parent *TemporalWorkerDeploymentCommand Command cobra.Command DeploymentVersionOptions - AwsLambdaFunctionArn string - AwsLambdaAssumeRoleArn string - AwsLambdaAssumeRoleExternalId string - GcpCloudRunProject string - GcpCloudRunRegion string - GcpCloudRunWorkerPool string - GcpCloudRunServiceAccount string - GcpCloudRunMinInstances int - GcpCloudRunMaxInstances int - GcpCloudRunInitialInstances int - GcpCloudRunUtilizationTarget float32 - Remove bool + AwsLambdaFunctionArn string + AwsLambdaAssumeRoleArn string + AwsLambdaAssumeRoleExternalId string + AwsLambdaSkipRoleAndExternalId bool + GcpCloudRunProject string + GcpCloudRunRegion string + GcpCloudRunWorkerPool string + GcpCloudRunServiceAccount string + GcpCloudRunMinInstances int + GcpCloudRunMaxInstances int + GcpCloudRunInitialInstances int + GcpCloudRunUtilizationTarget float32 + Remove bool } func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandContext, parent *TemporalWorkerDeploymentCommand) *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand { @@ -3670,8 +3673,9 @@ func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandC } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") - s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleArn, "aws-lambda-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is specified.") - s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleExternalId, "aws-lambda-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-lambda-function-arn is specified.") + s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleArn, "aws-lambda-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is specified, and must be omitted when --aws-lambda-skip-role-and-external-id is passed.") + s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleExternalId, "aws-lambda-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-lambda-function-arn is specified, and must be omitted when --aws-lambda-skip-role-and-external-id is passed.") + s.Command.Flags().BoolVar(&s.AwsLambdaSkipRoleAndExternalId, "aws-lambda-skip-role-and-external-id", false, "When --aws-lambda-function-arn is specified, --aws-lambda-assume-role-arn and --aws-lambda-assume-role-external-id are required unless this flag is passed, in which case both must be omitted.") s.Command.Flags().StringVar(&s.GcpCloudRunProject, "gcp-cloud-run-project", "", "GCP project ID hosting the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunRegion, "gcp-cloud-run-region", "", "Region of the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunWorkerPool, "gcp-cloud-run-worker-pool", "", "GCP Cloud Run worker pool name to scale when there are no active pollers for task queue targets in the Worker Deployment.") diff --git a/internal/temporalcli/commands.worker.deployment.go b/internal/temporalcli/commands.worker.deployment.go index 2f788ae2f..593f00e39 100644 --- a/internal/temporalcli/commands.worker.deployment.go +++ b/internal/temporalcli/commands.worker.deployment.go @@ -989,8 +989,19 @@ func (c *TemporalWorkerDeploymentManagerIdentityUnsetCommand) run(cctx *CommandC return nil } -func validateAWSLambdaProviderDetails(details map[string]any) error { - for _, key := range []string{"arn", "role", "role_external_id"} { +func validateAWSLambdaProviderDetails(details map[string]any, skipRoleAndExternalID bool) error { + if _, ok := details["arn"]; !ok { + return fmt.Errorf("missing required AWS Lambda provider detail: arn") + } + if skipRoleAndExternalID { + for _, key := range []string{"role", "role_external_id"} { + if _, ok := details[key]; ok { + return fmt.Errorf("AWS Lambda provider detail %q must not be set when --aws-lambda-skip-role-and-external-id is passed", key) + } + } + return nil + } + for _, key := range []string{"role", "role_external_id"} { if _, ok := details[key]; !ok { return fmt.Errorf("missing required AWS Lambda provider detail: %s", key) } @@ -1004,6 +1015,7 @@ func awsLambdaProviderDetailsPayload( functionARN string, assumeRoleARN string, assumeRoleExternalID string, + skipRoleAndExternalID bool, ) (*commonpb.Payload, error) { // Map keys from temporal-auto-scaled-workers: // https://github.com/temporalio/temporal-auto-scaled-workers/blob/c4a7e69b6504365d7e5326b0b8e6cd95e3293f96/wci/workflow/compute_provider/aws_lambda.go#L16-L20 @@ -1016,7 +1028,7 @@ func awsLambdaProviderDetailsPayload( if assumeRoleExternalID != "" { providerDetails["role_external_id"] = assumeRoleExternalID } - err := validateAWSLambdaProviderDetails(providerDetails) + err := validateAWSLambdaProviderDetails(providerDetails, skipRoleAndExternalID) if err != nil { return nil, err } @@ -1069,6 +1081,7 @@ func computeProviderConfig( awsLambdaFunctionARN string, awsLambdaAssumeRoleARN string, awsLambdaAssumeRoleExternalID string, + awsLambdaSkipRoleAndExternalID bool, gcpCloudRunProject string, gcpCloudRunRegion string, gcpCloudRunWorkerPool string, @@ -1086,6 +1099,7 @@ func computeProviderConfig( awsLambdaFunctionARN, awsLambdaAssumeRoleARN, awsLambdaAssumeRoleExternalID, + awsLambdaSkipRoleAndExternalID, ) return "aws-lambda", p, err case gcpCloudRunWorkerPool != "": @@ -1230,6 +1244,7 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, c.AwsLambdaFunctionArn, c.AwsLambdaAssumeRoleArn, c.AwsLambdaAssumeRoleExternalId, + c.AwsLambdaSkipRoleAndExternalId, c.GcpCloudRunProject, c.GcpCloudRunRegion, c.GcpCloudRunWorkerPool, @@ -1329,6 +1344,7 @@ func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) run(cctx *Co c.AwsLambdaFunctionArn, c.AwsLambdaAssumeRoleArn, c.AwsLambdaAssumeRoleExternalId, + c.AwsLambdaSkipRoleAndExternalId, c.GcpCloudRunProject, c.GcpCloudRunRegion, c.GcpCloudRunWorkerPool, diff --git a/internal/temporalcli/commands.worker.deployment_test.go b/internal/temporalcli/commands.worker.deployment_test.go index e1a7115b4..1eca97f6f 100644 --- a/internal/temporalcli/commands.worker.deployment_test.go +++ b/internal/temporalcli/commands.worker.deployment_test.go @@ -1315,6 +1315,38 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { s.Error(res.Err) s.ErrorContains(res.Err, "missing required AWS Lambda provider detail: role") + // --aws-lambda-skip-role-and-external-id bypasses the client-side check, so + // the request reaches the server, which enforces its own + // require_role_and_external_id policy (enabled by default here). + skipRoleAndIDBuildID := uuid.NewString() + + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", skipRoleAndIDBuildID, + "--aws-lambda-function-arn", invokeARN, + "--aws-lambda-skip-role-and-external-id", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, `AWS Lambda compute provider requires "role" to be configured`) + + // --aws-lambda-skip-role-and-external-id and the role/external-id flags are + // mutually exclusive: passing both is rejected client-side. + skipWithRoleBuildID := uuid.NewString() + + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", skipWithRoleBuildID, + "--aws-lambda-function-arn", invokeARN, + "--aws-lambda-assume-role-arn", assumeRoleARN, + "--aws-lambda-skip-role-and-external-id", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "--aws-lambda-skip-role-and-external-id") + // --gcp-cloud-run-worker-pool requires project, region, and // service-account; the first missing detail key is reported. missingGCPProjectBuildID := uuid.NewString() diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 7ca3f075f..90032071a 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -1132,14 +1132,23 @@ commands: AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is - specified. + specified, and must be omitted when + --aws-lambda-skip-role-and-external-id is passed. - name: aws-lambda-assume-role-external-id type: string description: | Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required - when --aws-lambda-function-arn is specified. + when --aws-lambda-function-arn is specified, and must be omitted when + --aws-lambda-skip-role-and-external-id is passed. + - name: aws-lambda-skip-role-and-external-id + type: bool + description: | + When --aws-lambda-function-arn is specified, + --aws-lambda-assume-role-arn and --aws-lambda-assume-role-external-id + are required unless this flag is passed, in which case both must be + omitted. - name: gcp-cloud-run-project type: string description: | @@ -1483,14 +1492,23 @@ commands: AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is - specified. + specified, and must be omitted when + --aws-lambda-skip-role-and-external-id is passed. - name: aws-lambda-assume-role-external-id type: string description: | Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required - when --aws-lambda-function-arn is specified. + when --aws-lambda-function-arn is specified, and must be omitted when + --aws-lambda-skip-role-and-external-id is passed. + - name: aws-lambda-skip-role-and-external-id + type: bool + description: | + When --aws-lambda-function-arn is specified, + --aws-lambda-assume-role-arn and --aws-lambda-assume-role-external-id + are required unless this flag is passed, in which case both must be + omitted. - name: gcp-cloud-run-project type: string description: | From 0894176dcac607fc34e2af808e176e111aefacf7 Mon Sep 17 00:00:00 2001 From: Jeri Lane Date: Mon, 10 Aug 2026 16:06:11 -0700 Subject: [PATCH 03/15] Delegate help and completion to extensions when applicable (#1137) Shell completion always sets __complete as the first argument, so to delegate to extensions, `temporal __complete cloud n` needs to be rewritten as `temporal-cloud __complete n`. `help` can be invoked the same way ## Related issues CLDDX-150 ## What changed? * `tryExecuteExtension` delegates `help` and shell completion to extensions that match the remaining arguments * extensions are registered during shell completion, so "temporal " will show them ## Checklist **Design** * [x] This feature does not depend on Cloud-only APIs or behavior (it works against an OSS server) **Tests** * [x] Added unit test(s) (`func TestXxx`) where applicable ## Manual tests **Setup** Install at least one extension, like the [cloud cli](https://github.com/temporalio/cloud-cli#quick-install) (`brew install temporalio/prerelease/temporal-cloud` on Mac or Linux with homebrew installed) **Happy path** ``` $ temporal activity -- Operate on Activity Executions batch -- Manage running batch jobs cloud -- An extension command located at /opt/homebrew/bin/temporal-cloud completion -- Generate the autocompletion script for the specified shell config -- Manage config files (EXPERIMENTAL) env -- Manage environments help -- Help about any command nexus -- Start, list, and operate on Nexus Operations operator -- Manage Temporal deployments schedule -- Perform operations on Schedules server -- Run Temporal Server task-queue -- Manage Task Queues worker -- Read or update Worker state workflow -- Start, list, and operate on Workflows $ temporal cl temporal cloud $ temporal cloud account -- Manage Temporal Cloud account apikey -- Manage Temporal Cloud API keys async-operation -- Manage async operations connectivity -- Manage Temporal Cloud connectivity rules custom-role -- [Experimental] Manage Temporal Cloud custom roles help -- Help about any command login -- Authenticate with Temporal Cloud logout -- Clear Temporal Cloud authentication credentials namespace -- Manage Temporal Cloud namespaces nexus -- Manage Temporal Cloud Nexus Operations region -- Manage Temporal Cloud regions service-account -- Manage Temporal Cloud service accounts user -- Manage Temporal Cloud users user-group -- Manage Temporal Cloud user groups whoami -- Display the current authenticated identity ``` ``` $ temporal help --all ... Available Commands: activity Operate on Activity Executions batch Manage running batch jobs cloud An extension command located at /opt/homebrew/bin/temporal-cloud completion Generate the autocompletion script for the specified shell config Manage config files (EXPERIMENTAL) env Manage environments help Help about any command nexus Start, list, and operate on Nexus Operations operator Manage Temporal deployments schedule Perform operations on Schedules server Run Temporal Server task-queue Manage Task Queues worker Read or update Worker state workflow Start, list, and operate on Workflows ... temporal help cloud The Temporal Cloud CLI provides commands for managing and operating Temporal Cloud resources, including namespaces, users, and account settings. Example: temporal cloud namespace get --namespace my-namespace.my-account Usage: temporal cloud [command] Available Commands: account Manage Temporal Cloud account apikey Manage Temporal Cloud API keys async-operation Manage async operations connectivity Manage Temporal Cloud connectivity rules custom-role [Experimental] Manage Temporal Cloud custom roles help Help about any command login Authenticate with Temporal Cloud logout Clear Temporal Cloud authentication credentials namespace Manage Temporal Cloud namespaces nexus Manage Temporal Cloud Nexus Operations region Manage Temporal Cloud regions service-account Manage Temporal Cloud service accounts user Manage Temporal Cloud users user-group Manage Temporal Cloud user groups whoami Display the current authenticated identity ``` **Error case** ``` temporal z ``` (no completions shown) Behavior remains unchanged when an unknown argument is passed to `temporal help`: ``` $ temporal help zzz The Temporal CLI manages, monitors, and debugs Temporal apps. It lets you run a local Temporal Service, start Workflow Executions, pass messages to running Workflows, inspect state, and more. ... ``` --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> (cherry picked from commit ba78f94afc9d80b308a7a53aa7b087b30f0fe9ad) --- internal/temporalcli/commands.extension.go | 73 ++++++++++++++++--- .../temporalcli/commands.extension_test.go | 35 +++++++++ internal/temporalcli/commands.go | 6 ++ internal/temporalcli/commands.help.go | 69 +++++++++++++----- internal/temporalcli/commands.help_test.go | 33 ++++++++- 5 files changed, 186 insertions(+), 30 deletions(-) diff --git a/internal/temporalcli/commands.extension.go b/internal/temporalcli/commands.extension.go index b00900810..702f2f6ac 100644 --- a/internal/temporalcli/commands.extension.go +++ b/internal/temporalcli/commands.extension.go @@ -36,8 +36,20 @@ func (err ExtensionNonZeroExit) Unwrap() error { // tryExecuteExtension tries to execute an extension command if the command is not a built-in command. // It returns an error if the extension command fails, and a boolean indicating whether an extension was executed. func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bool) { + // Special commands like "help" and "__complete" should be set aside and delegated to an extension command that matches + // the rest of the given arguments. "temporal help my-extension" should be rewritten to "temporal-my-extension help" + // Some of these commands, like "__complete" used for shell completion, don't actually get registered by Cobra until + // just before they're invoked, so we need to split out the delegatable commands before trying to Find() a matching + // subcommand. + delegatableCommands, nonDelegatedArgs := splitDelegatedCommands(cctx.Options.Args) + + // If a completion command (used for generating the script that invokes "__complete") already exists or has been + // explicitly disabled, this will do nothing, but if neither of those cases are true, we want to make sure + // this command is registered so extensions can't shadow it. + tcmd.Command.InitDefaultCompletionCmd() + // Find the deepest matching built-in command and remaining args. - foundCmd, remainingArgs, findErr := tcmd.Command.Find(cctx.Options.Args) + foundCmd, remainingArgs, findErr := tcmd.Command.Find(nonDelegatedArgs) // Cobra normally adds --help/-h before parsing, but extension dispatch // pre-parses flags before Cobra's execution path runs. We Initialize it so that @@ -58,6 +70,7 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo // Search for an extension executable. cmdPrefix := strings.Fields(foundCmd.CommandPath()) + extPath, extArgs := lookupExtension(cmdPrefix, extArgs) // Parse CLI args that need validation. @@ -71,6 +84,16 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo return nil, false } + if len(delegatableCommands) > 0 && isCompletionCommand(delegatableCommands[0]) && len(extArgs) == 0 { + // __complete always expects at least one argument, the last of which is the current subcommand + // or argument to expand, with an empty string matching all possibilities. + // ["temporal", "__complete", "activity"] means this cli should return any subcommands and extensions that + // match "activity", whereas ["temporal", "__complete", "activity", ""] means we should show what's available + // on the activity subcommand. The same logic applies to extension commands, so even if we matched an extension, + // if there are no further args, it's still this cli's responsibility to respond to the completion request. + return nil, false + } + // Apply --command-timeout if set. ctx := cctx.Context if timeout := tcmd.CommandTimeout.Duration(); timeout > 0 { @@ -79,7 +102,9 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo defer cancel() } - cmd := exec.CommandContext(ctx, extPath, append(cliPassArgs, extArgs...)...) + rebuiltArgs := slices.Concat(delegatableCommands, cliPassArgs, extArgs) + + cmd := exec.CommandContext(ctx, extPath, rebuiltArgs...) cmd.Stdin, cmd.Stdout, cmd.Stderr = cctx.Options.Stdin, cctx.Options.Stdout, cctx.Options.Stderr if err := cmd.Run(); err != nil { if ctx.Err() != nil { @@ -94,6 +119,38 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo return nil, true } +// splitDelegatedCommands separates out commands that should be delegated to an extension +// from the rest of the args given. These commands are inherently position-dependent, so they're +// only treated specially when they're at the start of the list of arguments. +// +// The resulting slices should be treated as read-only. Do not append to or otherwise mutate them. +func splitDelegatedCommands(args []string) ([]string, []string) { + if len(args) == 0 { + return args, args + } + + if args[0] == "help" { + // "help __complete" never delegates, whatever comes after, so we can just mark "help" as delegatable and see what matches + return args[:1], args[1:] + } + + if isCompletionCommand(args[0]) { + if len(args) > 1 && args[1] == "help" { + // "__complete help" is what happens when a user types "temporal help", so it should delegate both. This allows + // shell completion to display the available help topics available from an extension + return args[:2], args[2:] + } + + return args[:1], args[1:] + } + + return []string{}, args +} + +func isCompletionCommand(arg string) bool { + return arg == cobra.ShellCompRequestCmd || arg == cobra.ShellCompNoDescRequestCmd +} + func groupArgs(foundCmd *cobra.Command, args []string) (cliParseArgs, cliPassArgs, extArgs []string) { seenPos := false for i := 0; i < len(args); i++ { @@ -199,10 +256,9 @@ func lookupExtension(cmdPrefix, extArgs []string) (string, []string) { } // discoverExtensions scans the PATH for executables with the "temporal-" prefix -// and returns their command parts (without the prefix). -func discoverExtensions() [][]string { - var extensions [][]string - seen := make(map[string]bool) +// and returns their commands (without the prefix) mapped to the executable path +func discoverExtensions() map[string]string { + extensions := make(map[string]string) for _, dir := range filepath.SplitList(os.Getenv("PATH")) { if dir == "" { @@ -230,12 +286,11 @@ func discoverExtensions() [][]string { path := extensionBinaryToCommandPath(baseName) key := strings.Join(path, "/") - if seen[key] { + if extensions[key] != "" { continue } - seen[key] = true - extensions = append(extensions, path) + extensions[key] = filepath.Join(dir, entry.Name()) } } return extensions diff --git a/internal/temporalcli/commands.extension_test.go b/internal/temporalcli/commands.extension_test.go index 2590be0cc..5af0f1540 100644 --- a/internal/temporalcli/commands.extension_test.go +++ b/internal/temporalcli/commands.extension_test.go @@ -62,6 +62,30 @@ func TestExtension_PrefersMostSpecificExtension(t *testing.T) { assert.Equal(t, "Args: temporal-foo-bar \n", res.Stdout.String()) } +func TestExtension_CompleteShowsExtensions(t *testing.T) { + h := newExtensionHarness(t) + h.createExtension("temporal-foo", codeEchoArgs) + + res := h.Execute("__complete", "") + assert.Regexp(t, `foo\s+An extension command located at .*[\\/]temporal-foo`, res.Stdout.String()) +} + +func TestExtension_CompleteDoesNotDelegateWithoutAdditionalArgs(t *testing.T) { + h := newExtensionHarness(t) + h.createExtension("temporal-foo", codeEchoArgs) + + res := h.Execute("__complete", "foo") + assert.Regexp(t, `foo\s+An extension command located at .*[\\/]temporal-foo`, res.Stdout.String()) +} + +func TestExtension_InvokesComplete(t *testing.T) { + h := newExtensionHarness(t) + h.createExtension("temporal-foo", codeEchoArgs) + + res := h.Execute("__complete", "foo", "") + assert.Equal(t, "Args: temporal-foo __complete \n", res.Stdout.String()) +} + func TestExtension_ConvertsDashToUnderscoreInLookup(t *testing.T) { h := newExtensionHarness(t) h.createExtension("temporal-foo-bar_baz", codeEchoArgs) @@ -79,6 +103,7 @@ func TestExtension_DoesNotOverrideBuiltinCommand(t *testing.T) { h := newExtensionHarness(t) h.createExtension("temporal-workflow", codeEchoArgs) h.createExtension("temporal-workflow-list", codeEchoArgs) + h.createExtension("temporal-completion", codeEchoArgs) t.Run("root command", func(t *testing.T) { res := h.Execute("workflow", "--help") @@ -102,6 +127,16 @@ func TestExtension_DoesNotOverrideBuiltinCommand(t *testing.T) { assert.NoError(t, res.Err) } }) + + t.Run("__complete (shell completion)", func(t *testing.T) { + res := h.Execute("__complete", "") + assert.NotContains(t, res.Stdout.String(), "temporal-workflow") + }) + + t.Run("completion script generation", func(t *testing.T) { + res := h.Execute("completion", "zsh") + assert.NotContains(t, res.Stdout.String(), "temporal-completion") + }) } func TestExtension_Flags(t *testing.T) { diff --git a/internal/temporalcli/commands.go b/internal/temporalcli/commands.go index eaf08dbdf..d270b52ba 100644 --- a/internal/temporalcli/commands.go +++ b/internal/temporalcli/commands.go @@ -387,6 +387,12 @@ func Execute(ctx context.Context, options CommandOptions) { return } + if !cctx.ActuallyRanCommand && len(cctx.Options.Args) > 0 && isCompletionCommand(cctx.Options.Args[0]) { + // Completion was requested, but we didn't match an extension and delegate. Register all extension + // commands so things like "temporal cl" will expand to "temporal cloud" + registerExtensionCommands(&cmd.Command) + } + // Run builtin command if no extension handled the command. if !cctx.ActuallyRanCommand { err = cmd.Command.ExecuteContext(cctx) diff --git a/internal/temporalcli/commands.help.go b/internal/temporalcli/commands.help.go index f821473bc..205f98468 100644 --- a/internal/temporalcli/commands.help.go +++ b/internal/temporalcli/commands.help.go @@ -1,10 +1,13 @@ package temporalcli import ( + "cmp" + "fmt" "slices" "strings" "github.com/spf13/cobra" + "golang.org/x/exp/maps" ) // customizeHelpCommand adds the --all/-a flag to Cobra's built-in help command @@ -53,36 +56,64 @@ func customizeHelpCommand(rootCmd *cobra.Command) { } // registerExtensionCommands adds discovered extensions as placeholder commands -// so they appear in the default help output. It filters extensions based on -// the current command's path in the hierarchy. +// so they appear in shell completion and the default help output. It filters extensions +// based on the current command's path in the hierarchy. func registerExtensionCommands(cmd *cobra.Command) { cmdPath := strings.Fields(cmd.CommandPath()) - seen := make(map[string]bool) - for _, ext := range discoverExtensions() { + // When built-in subcommands are nested under other subcommands (e.g. `temporal activity cancel`), + // they're guaranteed to have a defined parent (`temporal activity`, which has the parent `temporal`). + // Extension subcommands can also be nested, but when `temporal foo bar` is created via an executable + // named temporal-foo-bar, there's no guarantee that the `temporal foo` command exists. For the full + // command to show up in help and completion, placeholders must exist at every level. + extensionsAndExecutables := discoverExtensions() + + extensionKeys := maps.Keys(extensionsAndExecutables) + + // Shorter command paths first ensures the paths shown in the short description of placeholder commands + // point to the closest match + slices.SortFunc(extensionKeys, func(a, b string) int { + return cmp.Compare(strings.Count(a, "/"), strings.Count(b, "/")) + }) + for _, extKey := range extensionKeys { + ext := strings.Split(extKey, "/") // Extension must be deeper than current command and share the same prefix if len(ext) <= len(cmdPath) || !slices.Equal(ext[:len(cmdPath)], cmdPath) { continue } - // Get the next level command name - nextPart := ext[len(cmdPath)] + extPath := ext[len(cmdPath):] - // Skip if already added - if seen[nextPart] { - continue - } + parent := cmd + executablePath := extensionsAndExecutables[extKey] - // Skip if a built-in command exists - if found, _, _ := cmd.Find([]string{nextPart}); found != cmd { - continue + for i, nextPart := range extPath { + if found, _, _ := parent.Find([]string{nextPart}); found != parent { + // Because we order extensions by depth, we can trust that any command that already exists already + // has the most correct definition for its depth. + parent = found + continue + } + + var short string + + if i == len(extPath)-1 { + short = fmt.Sprintf("An extension command located at %s", executablePath) + } else { + short = fmt.Sprintf("Extension commands under %s", strings.Join(ext[:len(cmdPath)+i+1], " ")) + } + + newCmd := &cobra.Command{ + Use: nextPart, + // Short descriptions must be unique at a given level because otherwise shell completion will + // group all commands with the same description on the same line + Short: short, + DisableFlagParsing: true, + Run: func(*cobra.Command, []string) {}, + } + parent.AddCommand(newCmd) + parent = newCmd } - seen[nextPart] = true - cmd.AddCommand(&cobra.Command{ - Use: nextPart, - DisableFlagParsing: true, - Run: func(*cobra.Command, []string) {}, - }) } } diff --git a/internal/temporalcli/commands.help_test.go b/internal/temporalcli/commands.help_test.go index b3cd10c00..a73d48a61 100644 --- a/internal/temporalcli/commands.help_test.go +++ b/internal/temporalcli/commands.help_test.go @@ -84,8 +84,8 @@ func TestHelp_AllFlag_ShowsExtensions(t *testing.T) { require.NoError(t, os.Rename(fooPath, fooPath+".bak")) require.NoError(t, os.Rename(fooBarPath, fooBarPath+".bak")) } else { - require.NoError(t, os.Chmod(fooPath, 0644)) - require.NoError(t, os.Chmod(fooBarPath, 0644)) + require.NoError(t, os.Chmod(fooPath, 0o644)) + require.NoError(t, os.Chmod(fooBarPath, 0o644)) } res = h.Execute("help", "--all") assert.NotContains(t, res.Stdout.String(), "foo") @@ -119,3 +119,32 @@ func TestHelp_AllFlag_FirstInPathWins(t *testing.T) { assert.Equal(t, "first\n", res.Stdout.String()) assert.NoError(t, res.Err) } + +func TestHelp_AllFlag_ShorterCommandPathWinsi(t *testing.T) { + // bin1/temporal-foo + // bin2/temporal-foo-bar + // bin1/something-nested + // bin2/something + // bin1/uncommon-prefix + h := newExtensionHarness(t) + binDir1 := h.binDir + binDir2 := t.TempDir() + + // Set PATH with binDir1 before binDir2 + oldPath := os.Getenv("PATH") + os.Setenv("PATH", binDir1+string(os.PathListSeparator)+binDir2+string(os.PathListSeparator)+oldPath) + t.Cleanup(func() { os.Setenv("PATH", oldPath) }) + + h.createExtension("temporal-foo", codeEchoArgs) + h.createExtension("temporal-something-nested", codeEchoArgs) + h.createExtension("temporal-sharedprefix-one", codeEchoArgs) + h.binDir = binDir2 + h.createExtension("temporal-foo-bar", codeEchoArgs) + h.createExtension("temporal-something", codeEchoArgs) + h.createExtension("temporal-sharedprefix-two", codeEchoArgs) + + res := h.Execute("help", "--all") + assert.Regexp(t, `foo\s+An extension command located at .*[\\/]temporal-foo`, res.Stdout.String()) + assert.Regexp(t, `something\s+An extension command located at .*[\\/]temporal-something`, res.Stdout.String()) + assert.Contains(t, res.Stdout.String(), "Extension commands under temporal sharedprefix") +} From 76b1c24a418db96584dfad44f23e1f99fbf0bbbf Mon Sep 17 00:00:00 2001 From: Sean Bollin Date: Tue, 25 Aug 2026 15:36:18 +0000 Subject: [PATCH 04/15] Adding --gcp-cloud-run-scale-down-stabilization-duration flag (#1167) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Related issues Closes: https://temporalio.atlassian.net/browse/COM-241 ## What changed? Adds `--gcp-cloud-run-scale-down-stabilization-duration` to `temporal worker deployment create-version` and `temporal worker deployment update-version-compute-config`. **UX difference:** the GCP Cloud Run scaler's scale-down stabilization window was previously hard-coded to 90s, so a worker pool running long or bursty activities could be scaled down out from under in-flight work. Users can now configure it: ``` # before: not settable β€” always 90s # after: temporal worker deployment create-version ... \ --gcp-cloud-run-scale-down-stabilization-duration 10m # hold capacity 10m after demand ``` Details: - The flag is a **duration** (`90s`, `5m`, `10m`), matching the CLI's convention for time-valued flags (`cliext.FlagDuration`, like `--schedule-to-close-timeout`, `--retention`). It joins the existing all-or-none GCP Cloud Run scaler group, so `--gcp-cloud-run-min-instances`, `--gcp-cloud-run-max-instances`, `--gcp-cloud-run-initial-instances`, `--gcp-cloud-run-utilization-target`, and `--gcp-cloud-run-scale-down-stabilization-duration` must all be set together. - Behavior: after the scaler last saw unmet task demand, it waits this long before it may scale the pool down. Defaults to `90s` when unset; `0s` disables the wait. - The CLI converts the duration to milliseconds and sends it under the rate-based scaler's existing `no_sync_quiet_ms` config key, which the server (WCI) already validates and applies β€” **no server-side change is required**. - `describe-version` surfaces the value as a duration string (JSON `scaleDownStabilization`, e.g. `"5m 0s"`, formatted the same way as schedule durations; the text summary shows the same). ## Checklist **Stability** - [x] Breaking changes are marked with πŸ’₯ in the PR title and release notes β€” *no breaking changes; the flag joins an as-yet-unreleased flag group* - [x] Changes to JSON output (`-o json` / `-o jsonl`) are treated as breaking changes β€” *`describe-version` gains an additive `scaleDownStabilizationMs` field; the GCP scaler JSON block is not in a tagged release yet, so no released output 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 β€” *no new commands; flag added to existing commands* - [x] New flags are named after the API concept, not the implementation mechanism β€” *`scale-down-stabilization-duration` names the behavior (cf. k8s HPA "stabilization window"), not the internal `no_sync_quiet_ms` key* - [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 β€” *no alias* - [x] Experimental features are marked with `(Experimental)` in `commands.yaml` β€” *both commands already carry a "This is an experimental feature" note* **Help text** (see style guide at the top of `commands.yaml`) - [x] All flags shown in help text and examples are implemented and functional β€” *the GCP examples include all five flags so they stay copy-pasteable* - [x] Summaries use sentence case and have no trailing period β€” *no new command summaries* - [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`) β€” group/negative/sub-millisecond/wrong-provider cases in `TestCreateWorkerDeploymentVersion_Errors`; carried in `...UpdateModes` - [x] Added unit test(s) (`func TestXxx`) β€” `TestGCPCloudRunScalerDetails`, `TestFormatComputeConfigProto_ScalerBounds` ## Manual tests **Setup** ``` temporal server start-dev --headless ``` > A full `--gcp-cloud-run-*` create also needs a real Cloud Run worker pool + > service account (the server validates the provider). The error-path checks > below run entirely against the dev server (they fail client-side, before the > RPC). The runtime effect was verified separately via an in-process WCI > integration test. **Happy path** ``` $ temporal worker deployment create-version \ --deployment-name YourDeployment \ --build-id YourBuildId \ --gcp-cloud-run-project YourGcpProject \ --gcp-cloud-run-region us-central1 \ --gcp-cloud-run-worker-pool YourWorkerPool \ --gcp-cloud-run-service-account YourServiceAccount@YourGcpProject.iam.gserviceaccount.com \ --gcp-cloud-run-min-instances 0 \ --gcp-cloud-run-max-instances 10 \ --gcp-cloud-run-initial-instances 2 \ --gcp-cloud-run-utilization-target 0.8 \ --gcp-cloud-run-scale-down-stabilization-duration 5m Successfully created worker deployment version $ temporal worker deployment describe-version \ --deployment-name YourDeployment \ --build-id YourBuildId # summary: gcp-cloud-run (min 0, initial 2, max 10, utilization 0.8, scale-down-stabilization 5m 0s) # --output json includes "scaleDownStabilization": "5m 0s" on the scaler ``` **Error case** ``` # incomplete group (all five must be set together): $ temporal worker deployment create-version \ --deployment-name YourDeployment --build-id YourBuildId \ --gcp-cloud-run-project YourGcpProject --gcp-cloud-run-region us-central1 \ --gcp-cloud-run-worker-pool YourWorkerPool \ --gcp-cloud-run-service-account YourServiceAccount@YourGcpProject.iam.gserviceaccount.com \ --gcp-cloud-run-scale-down-stabilization-duration 5m Error: --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must be set together $ echo $? 1 # negative (incl. sub-millisecond, which must not silently truncate to 0): $ temporal worker deployment create-version \ --deployment-name YourDeployment --build-id YourBuildId \ --gcp-cloud-run-project YourGcpProject --gcp-cloud-run-region us-central1 \ --gcp-cloud-run-worker-pool YourWorkerPool \ --gcp-cloud-run-service-account YourServiceAccount@YourGcpProject.iam.gserviceaccount.com \ --gcp-cloud-run-min-instances 0 --gcp-cloud-run-max-instances 10 \ --gcp-cloud-run-initial-instances 2 --gcp-cloud-run-utilization-target 0.8 \ --gcp-cloud-run-scale-down-stabilization-duration=-1us Error: --gcp-cloud-run-scale-down-stabilization-duration cannot be negative # sub-millisecond precision is rejected rather than silently rounded: $ temporal worker deployment create-version ... \ --gcp-cloud-run-scale-down-stabilization-duration 500us Error: --gcp-cloud-run-scale-down-stabilization-duration must be a whole number of milliseconds # on a non-GCP provider: $ temporal worker deployment create-version \ --deployment-name YourDeployment --build-id YourBuildId \ --aws-lambda-function-arn YourFunctionArn \ --aws-lambda-skip-role-and-external-id \ --gcp-cloud-run-scale-down-stabilization-duration 5m Error: the Cloud Run scaling flags are only valid with --gcp-cloud-run-worker-pool ``` **Composition** ``` # Raise the stabilization window on an existing version (all five flags are # re-supplied, since they are one all-or-none group), then confirm via describe. $ temporal worker deployment update-version-compute-config \ --deployment-name YourDeployment --build-id YourBuildId \ --gcp-cloud-run-worker-pool YourWorkerPool \ --gcp-cloud-run-min-instances 0 --gcp-cloud-run-max-instances 10 \ --gcp-cloud-run-initial-instances 2 --gcp-cloud-run-utilization-target 0.8 \ --gcp-cloud-run-scale-down-stabilization-duration 10m Successfully updated worker deployment version compute config $ temporal worker deployment describe-version \ --deployment-name YourDeployment --build-id YourBuildId --output json # scaler now shows "scaleDownStabilization": "10m 0s" ``` Co-authored-by: Claude Opus 4.8 (cherry picked from commit fb00858ff3e03bd285de02394482a5a7557aa9ee) --- internal/temporalcli/commands.gen.go | 80 +++++++------- .../temporalcli/commands.worker.deployment.go | 101 +++++++++++------- ...ommands.worker.deployment.internal_test.go | 100 +++++++++++++---- .../commands.worker.deployment_test.go | 93 +++++++++++++++- internal/temporalcli/commands.yaml | 90 +++++++++++----- 5 files changed, 335 insertions(+), 129 deletions(-) diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 0913b11c4..d9e809a40 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -3304,18 +3304,19 @@ type TemporalWorkerDeploymentCreateVersionCommand struct { Parent *TemporalWorkerDeploymentCommand Command cobra.Command DeploymentVersionOptions - AwsLambdaFunctionArn string - AwsLambdaAssumeRoleArn string - AwsLambdaAssumeRoleExternalId string - AwsLambdaSkipRoleAndExternalId bool - GcpCloudRunProject string - GcpCloudRunRegion string - GcpCloudRunWorkerPool string - GcpCloudRunServiceAccount string - GcpCloudRunMinInstances int - GcpCloudRunMaxInstances int - GcpCloudRunInitialInstances int - GcpCloudRunUtilizationTarget float32 + AwsLambdaFunctionArn string + AwsLambdaAssumeRoleArn string + AwsLambdaAssumeRoleExternalId string + AwsLambdaSkipRoleAndExternalId bool + GcpCloudRunProject string + GcpCloudRunRegion string + GcpCloudRunWorkerPool string + GcpCloudRunServiceAccount string + GcpCloudRunMinInstances int + GcpCloudRunMaxInstances int + GcpCloudRunInitialInstances int + GcpCloudRunUtilizationTarget float32 + GcpCloudRunScaleDownStabilizationDuration cliext.FlagDuration } func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, parent *TemporalWorkerDeploymentCommand) *TemporalWorkerDeploymentCreateVersionCommand { @@ -3325,9 +3326,9 @@ func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, paren s.Command.Use = "create-version [flags]" s.Command.Short = "Create a new Worker Deployment Version" if hasHighlighting { - s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n\x1b[1mtemporal worker deployment create-version [options]\x1b[0m\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\x1b[0m\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n\x1b[1mtemporal worker deployment create-version [options]\x1b[0m\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\x1b[0m\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." } else { - s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n```\ntemporal worker deployment create-version [options]\n```\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\n```\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\n```\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n```\ntemporal worker deployment create-version [options]\n```\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\n```\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") @@ -3338,10 +3339,12 @@ func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, paren s.Command.Flags().StringVar(&s.GcpCloudRunRegion, "gcp-cloud-run-region", "", "Region of the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunWorkerPool, "gcp-cloud-run-worker-pool", "", "GCP Cloud Run worker pool name to scale when there are no active pollers for task queue targets in the Worker Deployment.") s.Command.Flags().StringVar(&s.GcpCloudRunServiceAccount, "gcp-cloud-run-service-account", "", "Customer GCP service account the Temporal server impersonates to manage the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") - s.Command.Flags().IntVar(&s.GcpCloudRunMinInstances, "gcp-cloud-run-min-instances", 0, "Minimum number of Cloud Run worker pool instances the scaler will maintain. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool.") - s.Command.Flags().IntVar(&s.GcpCloudRunMaxInstances, "gcp-cloud-run-max-instances", 0, "Maximum number of Cloud Run worker pool instances the scaler may scale up to. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Defaults to 30 when unset. Only valid with --gcp-cloud-run-worker-pool.") - s.Command.Flags().IntVar(&s.GcpCloudRunInitialInstances, "gcp-cloud-run-initial-instances", 0, "Number of Cloud Run worker pool instances the scaler starts with. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together, and this value must be between the min and max (inclusive). Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool.") - s.Command.Flags().Float32Var(&s.GcpCloudRunUtilizationTarget, "gcp-cloud-run-utilization-target", 0, "Target average worker utilization the scaler aims for, as a fraction in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Lower values keep more spare capacity per worker. Defaults to 0.8 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunMinInstances, "gcp-cloud-run-min-instances", 0, "Minimum number of Cloud Run worker pool instances the scaler will maintain. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together. Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunMaxInstances, "gcp-cloud-run-max-instances", 0, "Maximum number of Cloud Run worker pool instances the scaler may scale up to. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together. Defaults to 30 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunInitialInstances, "gcp-cloud-run-initial-instances", 0, "Number of Cloud Run worker pool instances the scaler starts with. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together, and this value must be between the min and max (inclusive). Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().Float32Var(&s.GcpCloudRunUtilizationTarget, "gcp-cloud-run-utilization-target", 0, "Target average worker utilization the scaler aims for, as a fraction in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together. Lower values keep more spare capacity per worker. Defaults to 0.8 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.GcpCloudRunScaleDownStabilizationDuration = 0 + s.Command.Flags().Var(&s.GcpCloudRunScaleDownStabilizationDuration, "gcp-cloud-run-scale-down-stabilization-duration", "Duration the scaler waits after it last saw unmet task demand before it may scale the Cloud Run worker pool down. Raise this to keep the pool from scaling down before long-running or bursty activities finish. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together. A value of 0s disables the wait. Defaults to 90s when unset. Only valid with --gcp-cloud-run-worker-pool.") s.DeploymentVersionOptions.BuildFlags(s.Command.Flags()) s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { @@ -3645,19 +3648,20 @@ type TemporalWorkerDeploymentUpdateVersionComputeConfigCommand struct { Parent *TemporalWorkerDeploymentCommand Command cobra.Command DeploymentVersionOptions - AwsLambdaFunctionArn string - AwsLambdaAssumeRoleArn string - AwsLambdaAssumeRoleExternalId string - AwsLambdaSkipRoleAndExternalId bool - GcpCloudRunProject string - GcpCloudRunRegion string - GcpCloudRunWorkerPool string - GcpCloudRunServiceAccount string - GcpCloudRunMinInstances int - GcpCloudRunMaxInstances int - GcpCloudRunInitialInstances int - GcpCloudRunUtilizationTarget float32 - Remove bool + AwsLambdaFunctionArn string + AwsLambdaAssumeRoleArn string + AwsLambdaAssumeRoleExternalId string + AwsLambdaSkipRoleAndExternalId bool + GcpCloudRunProject string + GcpCloudRunRegion string + GcpCloudRunWorkerPool string + GcpCloudRunServiceAccount string + GcpCloudRunMinInstances int + GcpCloudRunMaxInstances int + GcpCloudRunInitialInstances int + GcpCloudRunUtilizationTarget float32 + GcpCloudRunScaleDownStabilizationDuration cliext.FlagDuration + Remove bool } func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandContext, parent *TemporalWorkerDeploymentCommand) *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand { @@ -3667,9 +3671,9 @@ func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandC s.Command.Use = "update-version-compute-config [flags]" s.Command.Short = "Update compute configuration for a Version" if hasHighlighting { - s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\x1b[0m\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\x1b[0m\n\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\x1b[0m\n\nTo update only the scaling settings on an existing GCP Cloud Run Worker\nDeployment Version, supply the four scaler flags without the provider\nfields (all four must be set together):\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\x1b[0m\n\nProvider fields are only required when changing the compute provider.\nSwitching the provider resets the scaling settings for the new provider.\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\x1b[0m\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\x1b[0m\n\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nTo update only the scaling settings on an existing GCP Cloud Run Worker\nDeployment Version, supply the five scaler flags without the provider\nfields (all five must be set together):\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nProvider fields are only required when changing the compute provider.\nSwitching the provider resets the scaling settings for the new provider.\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." } else { - s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\n```\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\n```\n\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\n```\n\nTo update only the scaling settings on an existing GCP Cloud Run Worker\nDeployment Version, supply the four scaler flags without the provider\nfields (all four must be set together):\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\n```\n\nProvider fields are only required when changing the compute provider.\nSwitching the provider resets the scaling settings for the new provider.\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\n```\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\n```\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\n```\n\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nTo update only the scaling settings on an existing GCP Cloud Run Worker\nDeployment Version, supply the five scaler flags without the provider\nfields (all five must be set together):\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nProvider fields are only required when changing the compute provider.\nSwitching the provider resets the scaling settings for the new provider.\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\n```\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") @@ -3680,10 +3684,12 @@ func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandC s.Command.Flags().StringVar(&s.GcpCloudRunRegion, "gcp-cloud-run-region", "", "Region of the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunWorkerPool, "gcp-cloud-run-worker-pool", "", "GCP Cloud Run worker pool name to scale when there are no active pollers for task queue targets in the Worker Deployment.") s.Command.Flags().StringVar(&s.GcpCloudRunServiceAccount, "gcp-cloud-run-service-account", "", "Customer GCP service account the Temporal server impersonates to manage the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") - s.Command.Flags().IntVar(&s.GcpCloudRunMinInstances, "gcp-cloud-run-min-instances", 0, "Minimum number of Cloud Run worker pool instances the scaler will maintain. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. If omitted, the version's existing scaling settings are left unchanged. Only valid with --gcp-cloud-run-worker-pool.") - s.Command.Flags().IntVar(&s.GcpCloudRunMaxInstances, "gcp-cloud-run-max-instances", 0, "Maximum number of Cloud Run worker pool instances the scaler may scale up to. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. If omitted, the version's existing scaling settings are left unchanged. Only valid with --gcp-cloud-run-worker-pool.") - s.Command.Flags().IntVar(&s.GcpCloudRunInitialInstances, "gcp-cloud-run-initial-instances", 0, "Number of Cloud Run worker pool instances the scaler starts with. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together, and this value must be between the min and max (inclusive). If omitted, the version's existing scaling settings are left unchanged. Only valid with --gcp-cloud-run-worker-pool.") - s.Command.Flags().Float32Var(&s.GcpCloudRunUtilizationTarget, "gcp-cloud-run-utilization-target", 0, "Target average worker utilization the scaler aims for, as a fraction in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Lower values keep more spare capacity per worker. If omitted, the version's existing scaling settings are left unchanged. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunMinInstances, "gcp-cloud-run-min-instances", 0, "Minimum number of Cloud Run worker pool instances the scaler will maintain. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together. If omitted, the version's existing scaling settings are left unchanged. Only applies to a GCP Cloud Run worker pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunMaxInstances, "gcp-cloud-run-max-instances", 0, "Maximum number of Cloud Run worker pool instances the scaler may scale up to. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together. If omitted, the version's existing scaling settings are left unchanged. Only applies to a GCP Cloud Run worker pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunInitialInstances, "gcp-cloud-run-initial-instances", 0, "Number of Cloud Run worker pool instances the scaler starts with. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together, and this value must be between the min and max (inclusive). If omitted, the version's existing scaling settings are left unchanged. Only applies to a GCP Cloud Run worker pool.") + s.Command.Flags().Float32Var(&s.GcpCloudRunUtilizationTarget, "gcp-cloud-run-utilization-target", 0, "Target average worker utilization the scaler aims for, as a fraction in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together. Lower values keep more spare capacity per worker. If omitted, the version's existing scaling settings are left unchanged. Only applies to a GCP Cloud Run worker pool.") + s.GcpCloudRunScaleDownStabilizationDuration = 0 + s.Command.Flags().Var(&s.GcpCloudRunScaleDownStabilizationDuration, "gcp-cloud-run-scale-down-stabilization-duration", "Duration the scaler waits after it last saw unmet task demand before it may scale the Cloud Run worker pool down. Raise this to keep the pool from scaling down before long-running or bursty activities finish. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must all be set together. A value of 0s disables the wait. If omitted, the version's existing scaling settings are left unchanged. Only applies to a GCP Cloud Run worker pool.") s.Command.Flags().BoolVar(&s.Remove, "remove", false, "Removes any compute configuration associated with this Worker Deployment Version.") s.DeploymentVersionOptions.BuildFlags(s.Command.Flags()) s.Command.Run = func(c *cobra.Command, args []string) { diff --git a/internal/temporalcli/commands.worker.deployment.go b/internal/temporalcli/commands.worker.deployment.go index 593f00e39..6bc5f07f7 100644 --- a/internal/temporalcli/commands.worker.deployment.go +++ b/internal/temporalcli/commands.worker.deployment.go @@ -136,11 +136,12 @@ type formattedComputeConfigProvider struct { } type formattedComputeConfigScaler struct { - Type string `json:"type"` - MinInstances *int64 `json:"minInstances,omitempty"` - MaxInstances *int64 `json:"maxInstances,omitempty"` - InitialInstances *int64 `json:"initialInstances,omitempty"` - UtilizationTarget *float64 `json:"utilizationTarget,omitempty"` + Type string `json:"type"` + MinInstances *int64 `json:"minInstances,omitempty"` + MaxInstances *int64 `json:"maxInstances,omitempty"` + InitialInstances *int64 `json:"initialInstances,omitempty"` + UtilizationTarget *float64 `json:"utilizationTarget,omitempty"` + ScaleDownStabilization string `json:"scaleDownStabilization,omitempty"` } func drainageStatusToStr(drainage client.WorkerDeploymentVersionDrainageStatus) (string, error) { @@ -393,6 +394,7 @@ const ( scalerKeyMaxCount = "max_count" scalerKeyInitialCount = "initial_count" scalerKeyUtilizationTarget = "utilization_target" + scalerKeyNoSyncQuietMs = "no_sync_quiet_ms" ) // scalerCountFromMap reads an integer worker-count value from a decoded scaler @@ -427,10 +429,11 @@ func scalerFloatFromMap(m map[string]any, key string) (float64, bool) { // scalerSettings holds the rate-based scaler settings surfaced for display. Each // field is nil when the corresponding key is absent from the scaler details. type scalerSettings struct { - minInstances *int64 - maxInstances *int64 - initialInstances *int64 - utilizationTarget *float64 + minInstances *int64 + maxInstances *int64 + initialInstances *int64 + utilizationTarget *float64 + scaleDownStabilization *time.Duration } // decodeScalerSettings extracts the rate-based scaler settings from a @@ -459,6 +462,12 @@ func decodeScalerSettings(s *computepb.ComputeScaler) scalerSettings { if v, ok := scalerFloatFromMap(m, scalerKeyUtilizationTarget); ok { out.utilizationTarget = &v } + if v, ok := scalerCountFromMap(m, scalerKeyNoSyncQuietMs); ok { + // The WCI key is stored in milliseconds; surface it as a duration to match + // how the CLI renders other duration fields (e.g. ApproximateBacklogAge). + d := time.Duration(v) * time.Millisecond + out.scaleDownStabilization = &d + } return out } @@ -490,6 +499,9 @@ func formatComputeConfigProto(cc *computepb.ComputeConfig) *formattedComputeConf fs.MaxInstances = set.maxInstances fs.InitialInstances = set.initialInstances fs.UtilizationTarget = set.utilizationTarget + if set.scaleDownStabilization != nil { + fs.ScaleDownStabilization = formatDuration(*set.scaleDownStabilization) + } sg.Scaler = fs } sgs[name] = sg @@ -540,7 +552,8 @@ func computeConfigSummaryStr(cc *computepb.ComputeConfig) string { } summary := p.GetType() // Append whichever scaler settings are present so the one-line summary - // reflects the configured limits (ordered min, initial, max, utilization). + // reflects the configured limits (ordered min, initial, max, utilization, + // scale-down-stabilization). set := decodeScalerSettings(sg.GetScaler()) parts := []string{} if set.minInstances != nil { @@ -555,6 +568,9 @@ func computeConfigSummaryStr(cc *computepb.ComputeConfig) string { if set.utilizationTarget != nil { parts = append(parts, fmt.Sprintf("utilization %g", *set.utilizationTarget)) } + if set.scaleDownStabilization != nil { + parts = append(parts, fmt.Sprintf("scale-down-stabilization %s", formatDuration(*set.scaleDownStabilization))) + } if len(parts) > 0 { summary = fmt.Sprintf("%s (%s)", summary, strings.Join(parts, ", ")) } @@ -1134,50 +1150,41 @@ func scalerTypeForProvider(providerType string) (string, error) { return "", fmt.Errorf("no scaler mapping for compute provider %q", providerType) } -// gcpCloudRunScalerDetails builds the ComputeScaler.Details payload from the GCP -// Cloud Run scaling flags. It carries two independent groups of rate-based scaler -// settings: -// - the instance-count group (min_count/max_count/initial_count), which is -// all-or-none and must satisfy min <= initial <= max, and -// - utilization_target, a standalone fraction in (0, 1]. -// -// The *Set booleans come from cobra's Flags().Changed, so an omitted flag stays -// distinct from an explicit 0. Returns a nil payload when nothing is set, leaving -// WCI's defaults (min 0, max 30, initial 0, utilization_target 0.8) in effect. -// Every setting is GCP Cloud Run only; any use with another provider is rejected. -// Config keys mirror the WCI rate-based scaler: -// https://github.com/temporalio/temporal-auto-scaled-workers/blob/main/wci/workflow/scaling_algorithm/rate_based.go // gcpScalerFlags holds the GCP Cloud Run scaling flag values together with // whether each was actually set (from cobra's Flags().Changed). Pairing each // value with its Set bool keeps an omitted flag distinct from an explicit 0 and // removes the positional-argument risk of passing the raw values around. type gcpScalerFlags struct { - min int - minSet bool - max int - maxSet bool - initial int - initialSet bool - utilization float32 - utilizationSet bool + min int + minSet bool + max int + maxSet bool + initial int + initialSet bool + utilization float32 + utilizationSet bool + scaleDownStabilization time.Duration + scaleDownStabilizationSet bool } func (f gcpScalerFlags) anySet() bool { - return f.minSet || f.maxSet || f.initialSet || f.utilizationSet + return f.minSet || f.maxSet || f.initialSet || f.utilizationSet || f.scaleDownStabilizationSet } func (f gcpScalerFlags) allSet() bool { - return f.minSet && f.maxSet && f.initialSet && f.utilizationSet + return f.minSet && f.maxSet && f.initialSet && f.utilizationSet && f.scaleDownStabilizationSet } // gcpCloudRunScalerDetails builds the ComputeScaler.Details payload from the GCP -// Cloud Run scaling flags (min/max/initial instance counts and utilization -// target). The four form a single all-or-none group: setting any one requires -// all four. That keeps the min<=initial<=max relationship self-contained and -// avoids comparing an explicit value against WCI's default for an unset sibling. -// Returns a nil payload when nothing is set, leaving WCI's defaults (min 0, -// max 30, initial 0, utilization_target 0.8) in effect. Every setting is GCP -// Cloud Run only; any use with another provider is rejected. +// Cloud Run scaling flags (min/max/initial instance counts, utilization target, +// and the scale-down stabilization duration). The five form a single all-or-none group: +// setting any one requires all five. That keeps the min<=initial<=max +// relationship self-contained, always writes the full scaler config so an update +// never leaves a stale sibling behind, and avoids comparing an explicit value +// against WCI's default for an unset sibling. Returns a nil payload when nothing +// is set, leaving WCI's defaults (min 0, max 30, initial 0, utilization_target +// 0.8, no_sync_quiet_ms 90000) in effect. Every setting is GCP Cloud Run only; +// any use with another provider is rejected. func gcpCloudRunScalerDetails(providerType string, f gcpScalerFlags) (*commonpb.Payload, error) { if !f.anySet() { return nil, nil @@ -1188,7 +1195,7 @@ func gcpCloudRunScalerDetails(providerType string, f gcpScalerFlags) (*commonpb. return nil, fmt.Errorf("the Cloud Run scaling flags are only valid with --gcp-cloud-run-worker-pool") } if !f.allSet() { - return nil, fmt.Errorf("--gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must be set together") + return nil, fmt.Errorf("--gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, --gcp-cloud-run-utilization-target, and --gcp-cloud-run-scale-down-stabilization-duration must be set together") } if f.min < 0 { return nil, fmt.Errorf("--gcp-cloud-run-min-instances cannot be negative") @@ -1207,11 +1214,21 @@ func gcpCloudRunScalerDetails(providerType string, f gcpScalerFlags) (*commonpb. if f.utilization <= 0 || f.utilization > 1 { return nil, fmt.Errorf("--gcp-cloud-run-utilization-target must be greater than 0 and at most 1") } + // Validate the raw duration before converting: Milliseconds() truncates + // toward zero, which would hide a negative sub-millisecond value (turning it + // into 0, disabling the wait) or silently round a fractional millisecond. + if f.scaleDownStabilization < 0 { + return nil, fmt.Errorf("--gcp-cloud-run-scale-down-stabilization-duration cannot be negative") + } + if f.scaleDownStabilization%time.Millisecond != 0 { + return nil, fmt.Errorf("--gcp-cloud-run-scale-down-stabilization-duration must be a whole number of milliseconds") + } details := map[string]any{ scalerKeyMinCount: f.min, scalerKeyMaxCount: f.max, scalerKeyInitialCount: f.initial, scalerKeyUtilizationTarget: f.utilization, + scalerKeyNoSyncQuietMs: f.scaleDownStabilization.Milliseconds(), } dc := converter.GetDefaultDataConverter() return dc.ToPayload(&details) @@ -1224,6 +1241,7 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) gcpScalerFlags() gcpScale max: c.GcpCloudRunMaxInstances, maxSet: f.Changed("gcp-cloud-run-max-instances"), initial: c.GcpCloudRunInitialInstances, initialSet: f.Changed("gcp-cloud-run-initial-instances"), utilization: c.GcpCloudRunUtilizationTarget, utilizationSet: f.Changed("gcp-cloud-run-utilization-target"), + scaleDownStabilization: c.GcpCloudRunScaleDownStabilizationDuration.Duration(), scaleDownStabilizationSet: f.Changed("gcp-cloud-run-scale-down-stabilization-duration"), } } @@ -1306,6 +1324,7 @@ func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) gcpScalerFla max: c.GcpCloudRunMaxInstances, maxSet: f.Changed("gcp-cloud-run-max-instances"), initial: c.GcpCloudRunInitialInstances, initialSet: f.Changed("gcp-cloud-run-initial-instances"), utilization: c.GcpCloudRunUtilizationTarget, utilizationSet: f.Changed("gcp-cloud-run-utilization-target"), + scaleDownStabilization: c.GcpCloudRunScaleDownStabilizationDuration.Duration(), scaleDownStabilizationSet: f.Changed("gcp-cloud-run-scale-down-stabilization-duration"), } } diff --git a/internal/temporalcli/commands.worker.deployment.internal_test.go b/internal/temporalcli/commands.worker.deployment.internal_test.go index 9db39ad77..18e0a6a08 100644 --- a/internal/temporalcli/commands.worker.deployment.internal_test.go +++ b/internal/temporalcli/commands.worker.deployment.internal_test.go @@ -2,6 +2,7 @@ package temporalcli import ( "testing" + "time" "github.com/stretchr/testify/require" computepb "go.temporal.io/api/compute/v1" @@ -44,46 +45,101 @@ func TestScalerTypeByProviderCoversAllProviders(t *testing.T) { } func TestGCPCloudRunScalerDetails(t *testing.T) { + // A fully-set, valid group; each case clones this and overrides one field so + // the all-or-none check passes and the case isolates a single value check. + valid := func() gcpScalerFlags { + return gcpScalerFlags{ + min: 1, minSet: true, + max: 10, maxSet: true, + initial: 5, initialSet: true, + utilization: 0.5, utilizationSet: true, + scaleDownStabilization: 90 * time.Second, scaleDownStabilizationSet: true, + } + } + // Nothing set -> nil payload so WCI defaults apply (min 0, max 30, - // initial 0, utilization_target 0.8). + // initial 0, utilization_target 0.8, no_sync_quiet_ms 90000). p, err := gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{}) require.NoError(t, err) require.Nil(t, p) - // Any scaler flag alongside a non-GCP provider is rejected. Covers both an - // instance-count flag and the utilization flag. + // Any scaler flag alongside a non-GCP provider is rejected. Covers an + // instance-count flag, the utilization flag, and the no-sync flag. _, err = gcpCloudRunScalerDetails("aws-lambda", gcpScalerFlags{minSet: true}) require.ErrorContains(t, err, "only valid with --gcp-cloud-run-worker-pool") _, err = gcpCloudRunScalerDetails("aws-lambda", gcpScalerFlags{utilization: 0.5, utilizationSet: true}) require.ErrorContains(t, err, "only valid with --gcp-cloud-run-worker-pool") + _, err = gcpCloudRunScalerDetails("aws-lambda", gcpScalerFlags{scaleDownStabilization: time.Second, scaleDownStabilizationSet: true}) + require.ErrorContains(t, err, "only valid with --gcp-cloud-run-worker-pool") - // All four settings are one all-or-none group: any partial set is rejected. + // All five settings are one all-or-none group: any partial set is rejected. _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 5, minSet: true}) require.ErrorContains(t, err, "must be set together") _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{utilization: 0.5, utilizationSet: true}) // utilization alone require.ErrorContains(t, err, "must be set together") - _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{ // trio set, utilization missing - min: 1, minSet: true, max: 3, maxSet: true, initial: 2, initialSet: true, - }) + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{scaleDownStabilization: time.Second, scaleDownStabilizationSet: true}) // scale-down-stabilization-duration alone require.ErrorContains(t, err, "must be set together") + // The four instance/utilization flags without scale-down-stabilization-duration are also + // rejected: scale-down-stabilization-duration is part of the same all-or-none group. + missingStabilization := valid() + missingStabilization.scaleDownStabilization, missingStabilization.scaleDownStabilizationSet = 0, false + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", missingStabilization) + require.ErrorContains(t, err, "must be set together") + + // Value checks, with the whole group set so the group check passes first. + neg := valid() + neg.min, neg.initial = -1, 0 + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", neg) + require.ErrorContains(t, err, "--gcp-cloud-run-min-instances cannot be negative") - // Value checks, with all four set so the group check passes first. - _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: -1, minSet: true, max: 3, maxSet: true, initial: 0, initialSet: true, utilization: 0.5, utilizationSet: true}) - require.ErrorContains(t, err, "cannot be negative") - _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 0, minSet: true, max: 0, maxSet: true, initial: 0, initialSet: true, utilization: 0.5, utilizationSet: true}) + maxTooLow := valid() + maxTooLow.min, maxTooLow.max, maxTooLow.initial = 0, 0, 0 + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", maxTooLow) require.ErrorContains(t, err, "--gcp-cloud-run-max-instances must be at least 1") - _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 5, minSet: true, max: 3, maxSet: true, initial: 4, initialSet: true, utilization: 0.5, utilizationSet: true}) + + minGtMax := valid() + minGtMax.min, minGtMax.max, minGtMax.initial = 5, 3, 4 + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", minGtMax) require.ErrorContains(t, err, "cannot exceed") - _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 2, minSet: true, max: 10, maxSet: true, initial: 15, initialSet: true, utilization: 0.5, utilizationSet: true}) + + initialOOR := valid() + initialOOR.min, initialOOR.max, initialOOR.initial = 2, 10, 15 + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", initialOOR) require.ErrorContains(t, err, "must be between") - _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 0, minSet: true, max: 10, maxSet: true, initial: 5, initialSet: true, utilization: 0, utilizationSet: true}) + + utilZero := valid() + utilZero.utilization = 0 + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", utilZero) require.ErrorContains(t, err, "must be greater than 0 and at most 1") - _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 0, minSet: true, max: 10, maxSet: true, initial: 5, initialSet: true, utilization: 1.5, utilizationSet: true}) + + utilHigh := valid() + utilHigh.utilization = 1.5 + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", utilHigh) require.ErrorContains(t, err, "must be greater than 0 and at most 1") - // All four set and valid -> payload decodes to the WCI rate-based keys. + negStabilization := valid() + negStabilization.scaleDownStabilization = -time.Second + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", negStabilization) + require.ErrorContains(t, err, "--gcp-cloud-run-scale-down-stabilization-duration cannot be negative") + + // A negative sub-millisecond value must be caught before Milliseconds() + // truncates it toward zero (which would send 0 and silently disable the wait). + negSubMs := valid() + negSubMs.scaleDownStabilization = -time.Microsecond + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", negSubMs) + require.ErrorContains(t, err, "--gcp-cloud-run-scale-down-stabilization-duration cannot be negative") + + // A positive sub-millisecond value is rejected rather than silently rounded. + subMs := valid() + subMs.scaleDownStabilization = 500 * time.Microsecond + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", subMs) + require.ErrorContains(t, err, "--gcp-cloud-run-scale-down-stabilization-duration must be a whole number of milliseconds") + + // Whole group set and valid -> payload decodes to the WCI rate-based keys. // JSON round-trips numbers as float64; WCI handles that on read. - p, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 1, minSet: true, max: 10, maxSet: true, initial: 5, initialSet: true, utilization: 0.5, utilizationSet: true}) + ok := valid() + ok.scaleDownStabilization = 120 * time.Second + p, err = gcpCloudRunScalerDetails("gcp-cloud-run", ok) require.NoError(t, err) require.NotNil(t, p) var details map[string]any @@ -92,6 +148,7 @@ func TestGCPCloudRunScalerDetails(t *testing.T) { require.Equal(t, float64(10), details[scalerKeyMaxCount]) require.Equal(t, float64(5), details[scalerKeyInitialCount]) require.Equal(t, float64(0.5), details[scalerKeyUtilizationTarget]) + require.Equal(t, float64(120000), details[scalerKeyNoSyncQuietMs]) } func TestFormatComputeConfigProto_ScalerBounds(t *testing.T) { @@ -101,6 +158,7 @@ func TestFormatComputeConfigProto_ScalerBounds(t *testing.T) { max: 10, maxSet: true, initial: 5, initialSet: true, utilization: 0.75, utilizationSet: true, + scaleDownStabilization: 120 * time.Second, scaleDownStabilizationSet: true, }) require.NoError(t, err) require.NotNil(t, scalerDetails) @@ -114,7 +172,7 @@ func TestFormatComputeConfigProto_ScalerBounds(t *testing.T) { }, } - // JSON/structured path surfaces min, max, initial, and utilization. + // JSON/structured path surfaces min, max, initial, utilization, and scale-down-stabilization. formatted := formatComputeConfigProto(cc) require.NotNil(t, formatted) sg, ok := formatted.ScalingGroups["default"] @@ -129,9 +187,10 @@ func TestFormatComputeConfigProto_ScalerBounds(t *testing.T) { require.Equal(t, int64(10), *sg.Scaler.MaxInstances) require.Equal(t, int64(5), *sg.Scaler.InitialInstances) require.Equal(t, float64(0.75), *sg.Scaler.UtilizationTarget) + require.Equal(t, "2m 0s", sg.Scaler.ScaleDownStabilization) - // Human-readable summary reflects the settings (min, initial, max, utilization). - require.Equal(t, "gcp-cloud-run (min 0, initial 5, max 10, utilization 0.75)", computeConfigSummaryStr(cc)) + // Human-readable summary reflects the settings (min, initial, max, utilization, scale-down-stabilization). + require.Equal(t, "gcp-cloud-run (min 0, initial 5, max 10, utilization 0.75, scale-down-stabilization 2m 0s)", computeConfigSummaryStr(cc)) // Without scaler details, the settings are nil and the summary is just the // provider (guards against printing zeroed-out values). @@ -150,5 +209,6 @@ func TestFormatComputeConfigProto_ScalerBounds(t *testing.T) { require.Nil(t, sg.Scaler.MaxInstances) require.Nil(t, sg.Scaler.InitialInstances) require.Nil(t, sg.Scaler.UtilizationTarget) + require.Empty(t, sg.Scaler.ScaleDownStabilization) require.Equal(t, "gcp-cloud-run", computeConfigSummaryStr(ccNoBounds)) } diff --git a/internal/temporalcli/commands.worker.deployment_test.go b/internal/temporalcli/commands.worker.deployment_test.go index 1eca97f6f..7bc7f740c 100644 --- a/internal/temporalcli/commands.worker.deployment_test.go +++ b/internal/temporalcli/commands.worker.deployment_test.go @@ -1470,7 +1470,7 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { s.Error(res.Err) s.ErrorContains(res.Err, "only valid with --gcp-cloud-run-worker-pool") - // A lone flag is rejected: all four scaling settings must be set together. + // A lone flag is rejected: all five scaling settings must be set together. res = s.Execute( "worker", "deployment", "create-version", "--address", s.Address(), @@ -1503,7 +1503,86 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { s.Error(res.Err) s.ErrorContains(res.Err, "must be set together") - // min cannot exceed max (all four set so the group check passes first). + // The instance counts and utilization-target without scale-down-stabilization-duration are + // also rejected: scale-down-stabilization-duration is part of the same all-or-none group. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "1", + "--gcp-cloud-run-max-instances", "3", + "--gcp-cloud-run-initial-instances", "2", + "--gcp-cloud-run-utilization-target", "0.5", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "must be set together") + + // scale-down-stabilization-duration cannot be negative (all five set so the group check + // passes first). + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "0", + "--gcp-cloud-run-max-instances", "10", + "--gcp-cloud-run-initial-instances", "5", + "--gcp-cloud-run-utilization-target", "0.5", + "--gcp-cloud-run-scale-down-stabilization-duration=-1s", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "--gcp-cloud-run-scale-down-stabilization-duration cannot be negative") + + // A negative sub-millisecond value is also rejected: it must be caught before + // the duration is truncated to whole milliseconds (which would send 0 and + // silently disable the wait instead). + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "0", + "--gcp-cloud-run-max-instances", "10", + "--gcp-cloud-run-initial-instances", "5", + "--gcp-cloud-run-utilization-target", "0.5", + "--gcp-cloud-run-scale-down-stabilization-duration=-1us", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "--gcp-cloud-run-scale-down-stabilization-duration cannot be negative") + + // A positive sub-millisecond value is rejected rather than silently rounded. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "0", + "--gcp-cloud-run-max-instances", "10", + "--gcp-cloud-run-initial-instances", "5", + "--gcp-cloud-run-utilization-target", "0.5", + "--gcp-cloud-run-scale-down-stabilization-duration", "500us", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "--gcp-cloud-run-scale-down-stabilization-duration must be a whole number of milliseconds") + + // min cannot exceed max (all five set so the group check passes first). res = s.Execute( "worker", "deployment", "create-version", "--address", s.Address(), @@ -1517,6 +1596,7 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { "--gcp-cloud-run-max-instances", "3", "--gcp-cloud-run-initial-instances", "4", "--gcp-cloud-run-utilization-target", "0.5", + "--gcp-cloud-run-scale-down-stabilization-duration", "90s", ) s.Error(res.Err) s.ErrorContains(res.Err, "cannot exceed") @@ -1535,6 +1615,7 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { "--gcp-cloud-run-max-instances", "0", "--gcp-cloud-run-initial-instances", "0", "--gcp-cloud-run-utilization-target", "0.5", + "--gcp-cloud-run-scale-down-stabilization-duration", "90s", ) s.Error(res.Err) s.ErrorContains(res.Err, "--gcp-cloud-run-max-instances must be at least 1") @@ -1553,6 +1634,7 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { "--gcp-cloud-run-max-instances", "10", "--gcp-cloud-run-initial-instances", "15", "--gcp-cloud-run-utilization-target", "0.5", + "--gcp-cloud-run-scale-down-stabilization-duration", "90s", ) s.Error(res.Err) s.ErrorContains(res.Err, "must be between") @@ -1572,6 +1654,7 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { "--gcp-cloud-run-max-instances", "10", "--gcp-cloud-run-initial-instances", "5", "--gcp-cloud-run-utilization-target", "1.5", + "--gcp-cloud-run-scale-down-stabilization-duration", "90s", ) s.Error(res.Err) s.ErrorContains(res.Err, "must be greater than 0 and at most 1") @@ -1640,7 +1723,7 @@ func (s *SharedServerSuite) TestUpdateWorkerDeploymentVersionComputeConfig_Updat serviceAccount := "customer-sa@my-gcp-project.iam.gserviceaccount.com" // Scaler-only update (no provider flags): the mask is just scaler.details, - // no provider is sent, and all four settings are carried. + // no provider is sent, and all five settings are carried. res := s.Execute( "worker", "deployment", "update-version-compute-config", "--address", s.Address(), @@ -1649,6 +1732,7 @@ func (s *SharedServerSuite) TestUpdateWorkerDeploymentVersionComputeConfig_Updat "--gcp-cloud-run-max-instances", "10", "--gcp-cloud-run-initial-instances", "5", "--gcp-cloud-run-utilization-target", "0.5", + "--gcp-cloud-run-scale-down-stabilization-duration", "2m", ) s.NoError(res.Err) req := takeCaptured() @@ -1663,6 +1747,7 @@ func (s *SharedServerSuite) TestUpdateWorkerDeploymentVersionComputeConfig_Updat s.Equal(float64(10), details["max_count"]) s.Equal(float64(5), details["initial_count"]) s.Equal(float64(0.5), details["utilization_target"]) + s.Equal(float64(120000), details["no_sync_quiet_ms"]) // Switching to AWS Lambda clears the (rate-based) scaler.details so they // don't linger under the no-sync scaler. @@ -1700,7 +1785,7 @@ func (s *SharedServerSuite) TestUpdateWorkerDeploymentVersionComputeConfig_Updat s.NotContains(sg.GetUpdateMask().GetPaths(), "scaler.details") s.Equal("gcp-cloud-run", sg.GetScalingGroup().GetProvider().GetType()) - // A scaler-only update still requires all four flags together. + // A scaler-only update still requires all five flags together. res = s.Execute( "worker", "deployment", "update-version-compute-config", "--address", s.Address(), diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 90032071a..f56fc3d0b 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -1108,7 +1108,8 @@ commands: --gcp-cloud-run-min-instances 1 \ --gcp-cloud-run-max-instances 3 \ --gcp-cloud-run-initial-instances 1 \ - --gcp-cloud-run-utilization-target 0.75 + --gcp-cloud-run-utilization-target 0.75 \ + --gcp-cloud-run-scale-down-stabilization-duration 5m ``` If a Worker Deployment Version with the supplied BuildID already exists, @@ -1175,24 +1176,27 @@ commands: description: | Minimum number of Cloud Run worker pool instances the scaler will maintain. Optional, but --gcp-cloud-run-min-instances, - --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and - --gcp-cloud-run-utilization-target must all be set together. Defaults + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together. Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool. - name: gcp-cloud-run-max-instances type: int description: | Maximum number of Cloud Run worker pool instances the scaler may scale up to. Optional, but --gcp-cloud-run-min-instances, - --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and - --gcp-cloud-run-utilization-target must all be set together. Defaults + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together. Defaults to 30 when unset. Only valid with --gcp-cloud-run-worker-pool. - name: gcp-cloud-run-initial-instances type: int description: | Number of Cloud Run worker pool instances the scaler starts with. Optional, but --gcp-cloud-run-min-instances, - --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and - --gcp-cloud-run-utilization-target must all be set together, and this + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together, and this value must be between the min and max (inclusive). Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool. - name: gcp-cloud-run-utilization-target @@ -1200,10 +1204,23 @@ commands: description: | Target average worker utilization the scaler aims for, as a fraction in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, - --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and - --gcp-cloud-run-utilization-target must all be set together. Lower + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together. Lower values keep more spare capacity per worker. Defaults to 0.8 when unset. Only valid with --gcp-cloud-run-worker-pool. + - name: gcp-cloud-run-scale-down-stabilization-duration + type: duration + description: | + Duration the scaler waits after it last saw unmet task demand + before it may scale the Cloud Run worker pool down. Raise this to + keep the pool from scaling down before long-running or bursty + activities finish. Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together. A + value of 0s disables the wait. Defaults to 90s when unset. Only + valid with --gcp-cloud-run-worker-pool. - name: temporal worker deployment describe-version summary: Show properties of a Worker Deployment Version @@ -1445,12 +1462,13 @@ commands: --gcp-cloud-run-min-instances 1 \ --gcp-cloud-run-max-instances 3 \ --gcp-cloud-run-initial-instances 1 \ - --gcp-cloud-run-utilization-target 0.75 + --gcp-cloud-run-utilization-target 0.75 \ + --gcp-cloud-run-scale-down-stabilization-duration 5m ``` To update only the scaling settings on an existing GCP Cloud Run Worker - Deployment Version, supply the four scaler flags without the provider - fields (all four must be set together): + Deployment Version, supply the five scaler flags without the provider + fields (all five must be set together): ``` temporal worker deployment update-version-compute-config \ @@ -1458,7 +1476,8 @@ commands: --gcp-cloud-run-min-instances 1 \ --gcp-cloud-run-max-instances 3 \ --gcp-cloud-run-initial-instances 1 \ - --gcp-cloud-run-utilization-target 0.75 + --gcp-cloud-run-utilization-target 0.75 \ + --gcp-cloud-run-scale-down-stabilization-duration 5m ``` Provider fields are only required when changing the compute provider. @@ -1535,39 +1554,56 @@ commands: description: | Minimum number of Cloud Run worker pool instances the scaler will maintain. Optional, but --gcp-cloud-run-min-instances, - --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and - --gcp-cloud-run-utilization-target must all be set together. If + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together. If omitted, the version's existing scaling settings are left unchanged. - Only valid with --gcp-cloud-run-worker-pool. + Only applies to a GCP Cloud Run worker pool. - name: gcp-cloud-run-max-instances type: int description: | Maximum number of Cloud Run worker pool instances the scaler may scale up to. Optional, but --gcp-cloud-run-min-instances, - --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and - --gcp-cloud-run-utilization-target must all be set together. If + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together. If omitted, the version's existing scaling settings are left unchanged. - Only valid with --gcp-cloud-run-worker-pool. + Only applies to a GCP Cloud Run worker pool. - name: gcp-cloud-run-initial-instances type: int description: | Number of Cloud Run worker pool instances the scaler starts with. Optional, but --gcp-cloud-run-min-instances, - --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and - --gcp-cloud-run-utilization-target must all be set together, and this + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together, and this value must be between the min and max (inclusive). If omitted, the - version's existing scaling settings are left unchanged. Only valid - with --gcp-cloud-run-worker-pool. + version's existing scaling settings are left unchanged. Only applies + to a GCP Cloud Run worker pool. - name: gcp-cloud-run-utilization-target type: float description: | Target average worker utilization the scaler aims for, as a fraction in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, - --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and - --gcp-cloud-run-utilization-target must all be set together. Lower + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together. Lower values keep more spare capacity per worker. If omitted, the version's - existing scaling settings are left unchanged. Only valid with - --gcp-cloud-run-worker-pool. + existing scaling settings are left unchanged. Only applies to a + GCP Cloud Run worker pool. + - name: gcp-cloud-run-scale-down-stabilization-duration + type: duration + description: | + Duration the scaler waits after it last saw unmet task demand + before it may scale the Cloud Run worker pool down. Raise this to + keep the pool from scaling down before long-running or bursty + activities finish. Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, + --gcp-cloud-run-utilization-target, and + --gcp-cloud-run-scale-down-stabilization-duration must all be set together. A + value of 0s disables the wait. If omitted, the version's existing + scaling settings are left unchanged. Only applies to a + GCP Cloud Run worker pool. - name: remove type: bool description: | From dfd40522de91201accc6de0615faa173d4dd7186 Mon Sep 17 00:00:00 2001 From: Jeri Lane Date: Tue, 25 Aug 2026 23:07:09 +0000 Subject: [PATCH 05/15] Fix cliext build, add it to CI workflow (#1176) ## Related issues None directly, it came up while I was looking at adding structure to some of the auth failures in the client I couldn't find anywhere that the cliext tests were getting executed, and attempting to build `cliext` by itself fails with ``` ./client.go:137:11: profile.Authority undefined (type "go.temporal.io/sdk/contrib/envconfig".ClientConfigProfile has no field or method Authority) ./config.oauth.go:144:20: assignment mismatch: 1 variable but envconfig.DefaultConfigFilePath returns 2 values ``` ## What changed * Added cliext tests to CI workflow (causes the above failures to surface in the CI workflow) * Updated `cliext/go.mod` to use the same versions (as applicable) of packages as are used in the main `go.mod` (fixes the failures) * Added `test` as a Makefile target (convenience when making changes across `cliext` and the main package) (cherry picked from commit bbd111c2a193ae6ccbad81ac89514b3a12984e51) --- .github/workflows/ci.yaml | 8 ++- Makefile | 4 ++ cliext/go.mod | 47 +++++++------- cliext/go.sum | 129 +++++++++++++++++++------------------- 4 files changed, 102 insertions(+), 86 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6a5df116d..0871f99fe 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -69,7 +69,13 @@ jobs: run: mkdir junit-xml - name: Test - run: gotestsum --junitfile junit-xml/${{matrix.os}}.xml -- ./... + shell: bash + run: | + gotestsum --junitfile junit-xml/${{matrix.os}}.xml -- ./... + ( + cd cliext + gotestsum --junitfile ../junit-xml/${{matrix.os}}-cliext.xml -- ./... + ) - name: Upload junit-xml artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/Makefile b/Makefile index 56e79a663..c92c37ed8 100644 --- a/Makefile +++ b/Makefile @@ -23,3 +23,7 @@ gen-docs: internal/temporalcli/commands.yaml cliext/option-sets.yaml build: go build ./cmd/temporal + +test: + (cd cliext && go test ./...) + go test ./... diff --git a/cliext/go.mod b/cliext/go.mod index 650a65840..19d62ff13 100644 --- a/cliext/go.mod +++ b/cliext/go.mod @@ -1,38 +1,41 @@ module github.com/temporalio/cli/cliext -go 1.25.0 +go 1.26.4 require ( github.com/BurntSushi/toml v1.4.0 - github.com/mattn/go-isatty v0.0.20 + github.com/mattn/go-isatty v0.0.23 github.com/spf13/pflag v1.0.10 - github.com/stretchr/testify v1.10.0 - go.temporal.io/sdk v1.41.0 - go.temporal.io/sdk/contrib/envconfig v1.0.0 - golang.org/x/oauth2 v0.34.0 - google.golang.org/grpc v1.79.3 + github.com/stretchr/testify v1.11.1 + go.temporal.io/sdk v1.46.1-0.20260720184640-f34dc3da35ab + go.temporal.io/sdk/contrib/envconfig v1.0.2 + golang.org/x/oauth2 v0.36.0 + google.golang.org/grpc v1.82.1 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/mock v1.6.0 // indirect + github.com/golang/mock v1.7.0-rc.1 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect + github.com/nexus-rpc/nexus-proto-annotations v0.1.0 // indirect github.com/nexus-rpc/sdk-go v0.6.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/robfig/cron v1.2.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect - go.temporal.io/api v1.62.2 // indirect - golang.org/x/net v0.48.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.39.0 // indirect - golang.org/x/text v0.32.0 // indirect - golang.org/x/time v0.3.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/protobuf v1.36.10 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/stretchr/objx v0.5.3 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect + go.temporal.io/api v1.63.5 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/cliext/go.sum b/cliext/go.sum index c29dfcadc..f62dadf7e 100644 --- a/cliext/go.sum +++ b/cliext/go.sum @@ -2,8 +2,8 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0 github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= @@ -12,117 +12,120 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= +github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ= +github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= +github.com/nexus-rpc/nexus-proto-annotations v0.1.0 h1:2fELd+9sqUtNu6Fg//pw8YFsxOvp8vZ8hfP0nHhNI80= +github.com/nexus-rpc/nexus-proto-annotations v0.1.0/go.mod h1:n3UjF1bPCW8llR8tHvbxJ+27yPWrhpo8w/Yg1IOuY0Y= github.com/nexus-rpc/sdk-go v0.6.0 h1:QRgnP2zTbxEbiyWG/aXH8uSC5LV/Mg1fqb19jb4DBlo= github.com/nexus-rpc/sdk-go v0.6.0/go.mod h1:FHdPfVQwRuJFZFTF0Y2GOAxCrbIBNrcPna9slkGKPYk= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= -go.temporal.io/api v1.62.2 h1:jFhIzlqNyJsJZTiCRQmTIMv6OTQ5BZ57z8gbgLGMaoo= -go.temporal.io/api v1.62.2/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= -go.temporal.io/sdk v1.41.0 h1:c9tayCQJDM5ZQdrqjGmjqk5ejxUtsEScJGF94sAVYpM= -go.temporal.io/sdk v1.41.0/go.mod h1:/InXQT5guZ6AizYzpmzr5avQ/GMgq1ZObcKlKE2AhTc= -go.temporal.io/sdk/contrib/envconfig v1.0.0 h1:1Q/swVgB4EW/p3k7rI9/4hpU4/DC57FSRbU90+UisXw= -go.temporal.io/sdk/contrib/envconfig v1.0.0/go.mod h1:Pj4N1lwUEvxap6quBm8GrVMSUMJhSZkVtxjt3AYnPPg= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= +go.temporal.io/api v1.63.5 h1:c11+kPYHkXXL3UiShPdbMD+xtvqGsbTibUA9ypmiCa4= +go.temporal.io/api v1.63.5/go.mod h1:SrlW2JMwVlDP4nRWSNznUFqnSHd+YeMDS1BkYo63HCQ= +go.temporal.io/sdk v1.46.1-0.20260720184640-f34dc3da35ab h1:iF9QRS220GeFyNWdiKx+jvQl6LAvZdXGStRCWnU5pZw= +go.temporal.io/sdk v1.46.1-0.20260720184640-f34dc3da35ab/go.mod h1:x3v/9ImVh469kiHspoq1xgLdPnetbfuCAm+Y1+sUtIo= +go.temporal.io/sdk/contrib/envconfig v1.0.2 h1:MGHfsuPUtsf7X9M6WYn3zYJj/mWsuYHnA1uuiL0KEuE= +go.temporal.io/sdk/contrib/envconfig v1.0.2/go.mod h1:MuMiH7hksps2uXnmKuAWaP9P6WbkSDy62kl64t1VJVg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= -golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 h1:zUWMZsvo/IJcD1t6MNCPO/azZTwz0TvwCBqr5aifoVY= +google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529/go.mod h1:a5OGAgyRr4lqco7AG9hQM9Fwh0N2ZV4grR0eXFEsXQg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 h1:XF8+t6QQiS0o9ArVan/HW8Q7cycNPGsJf6GA2nXxYAg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE= +google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 2a6450baf5fc7db415916ba20c3ac4ea40362889 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 14:36:33 +0000 Subject: [PATCH 06/15] chore(deps): bump the github-actions group with 3 updates (#1149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [actions/setup-go](https://github.com/actions/setup-go) and [actions/setup-python](https://github.com/actions/setup-python). Updates `actions/checkout` from 7.0.0 to 7.0.1
Release notes

Sourced from actions/checkout's releases.

v7.0.1

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v7...v7.0.1

Changelog

Sourced from actions/checkout's changelog.

Changelog

v7.0.1

v7.0.0

v6.0.3

v6.0.2

v6.0.1

v6.0.0

v5.0.1

v5.0.0

v4.3.1

v4.3.0

v4.2.2

v4.2.1

... (truncated)

Commits

Updates `actions/setup-go` from 6.5.0 to 7.0.0
Release notes

Sourced from actions/setup-go's releases.

v7.0.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/setup-go/compare/v6...v7.0.0

Commits

Updates `actions/setup-python` from 6.3.0 to 7.0.0
Release notes

Sourced from actions/setup-python's releases.

v7.0.0

What's Changed

Enhancements

Bug Fix

Dependency Upgrade

New Contributors

Full Changelog: https://github.com/actions/setup-python/compare/v6...v7.0.0

Commits

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 9fcadd6e7c9296a7eee1b53ee5e0b1c8c0ca3940) --- .github/workflows/build-and-publish-docker.yml | 2 +- .github/workflows/ci.yaml | 8 ++++---- .github/workflows/govulncheck.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- .github/workflows/validate-dependabot.yml | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-and-publish-docker.yml b/.github/workflows/build-and-publish-docker.yml index 502c103d1..021446f55 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0871f99fe..308059877 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: recursive - name: Setup Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index 7c49fd9df..0abb20f2a 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.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 ec10831b4..6d1bbb486 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: "go.mod" check-latest: true diff --git a/.github/workflows/validate-dependabot.yml b/.github/workflows/validate-dependabot.yml index d9010aa50..18054169f 100644 --- a/.github/workflows/validate-dependabot.yml +++ b/.github/workflows/validate-dependabot.yml @@ -17,9 +17,9 @@ jobs: name: Validate Dependabot Config runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: '3.12' From d2e419ad8256bfe7c2e45de7132080666b4f448f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 03:11:34 +0000 Subject: [PATCH 07/15] chore(deps): bump docker/login-action from 4.4.0 to 4.5.2 in the github-actions group (#1162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [docker/login-action](https://github.com/docker/login-action). Updates `docker/login-action` from 4.4.0 to 4.5.2
Release notes

Sourced from docker/login-action's releases.

v4.5.2

Full Changelog: https://github.com/docker/login-action/compare/v4.5.1...v4.5.2

v4.5.1

Full Changelog: https://github.com/docker/login-action/compare/v4.5.0...v4.5.1

v4.5.0

Full Changelog: https://github.com/docker/login-action/compare/v4.4.0...v4.5.0

Commits
  • 371161b Merge pull request #1058 from crazy-max/fix-dockerhub-oidc-error-handling
  • 5dc73df chore: update generated content
  • 2aa1ede surface Docker Hub OIDC error responses
  • abd2ef4 Merge pull request #1055 from crazy-max/test-registry-auth-oidc
  • d49d3a9 Merge pull request #1054 from crazy-max/oidc-missing-dhi
  • b58b17c test: cover Docker Hub OIDC with registry-auth
  • be646c2 chore: update generated content
  • d77c059 support dhi.io as Docker Hub OIDC registry
  • 06fb636 Merge pull request #1037 from docker/dependabot/npm_and_yarn/aws-sdk-dependen...
  • a8bc953 [dependabot skip] chore: update generated content
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/login-action&package-manager=github_actions&previous-version=4.4.0&new-version=4.5.2)](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 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 cf9312810c39b82d9d695f69cb5daa6f378ea1ef) --- .github/workflows/build-and-publish-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-publish-docker.yml b/.github/workflows/build-and-publish-docker.yml index 021446f55..b7ecd0c8f 100644 --- a/.github/workflows/build-and-publish-docker.yml +++ b/.github/workflows/build-and-publish-docker.yml @@ -89,7 +89,7 @@ jobs: - name: Log in to Docker Hub if: inputs.publish - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} From 7cf3e92f2371e07b6a546e376c32ca17ebe465bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:20:50 +0000 Subject: [PATCH 08/15] chore(deps): bump docker/login-action from 4.5.2 to 4.6.0 in the github-actions group (#1166) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [docker/login-action](https://github.com/docker/login-action). Updates `docker/login-action` from 4.5.2 to 4.6.0
Release notes

Sourced from docker/login-action's releases.

v4.6.0

Full Changelog: https://github.com/docker/login-action/compare/v4.5.2...v4.6.0

Commits
  • dbcb813 Merge pull request #1051 from docker/dependabot/npm_and_yarn/aws-sdk-dependen...
  • 5bcb015 [dependabot skip] chore: update generated content
  • b30b2f2 build(deps): bump the aws-sdk-dependencies group across 1 directory with 2 up...
  • 9087f1e Merge pull request #1057 from docker/dependabot/npm_and_yarn/js-yaml-5.2.2
  • 0009830 [dependabot skip] chore: update generated content
  • 2325523 build(deps): bump js-yaml from 5.2.1 to 5.2.2
  • 4ec1d4a Merge pull request #1056 from docker/dependabot/npm_and_yarn/postcss-8.5.22
  • 5fc99ba Merge pull request #1053 from docker/dependabot/github_actions/aws-actions/co...
  • e512bd5 Merge pull request #1052 from docker/dependabot/github_actions/codeql-actions...
  • a146c91 Merge pull request #1059 from crazy-max/harden-buildx-scope-paths
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/login-action&package-manager=github_actions&previous-version=4.5.2&new-version=4.6.0)](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 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 71115ab69b52c0a99d23879d116f9c4e9b812f30) --- .github/workflows/build-and-publish-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-publish-docker.yml b/.github/workflows/build-and-publish-docker.yml index b7ecd0c8f..74bbcae53 100644 --- a/.github/workflows/build-and-publish-docker.yml +++ b/.github/workflows/build-and-publish-docker.yml @@ -89,7 +89,7 @@ jobs: - name: Log in to Docker Hub if: inputs.publish - uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} From 9a0bf99b7026384e36c77bd9b15197326d8b0f41 Mon Sep 17 00:00:00 2001 From: Sean Kane Date: Wed, 5 Aug 2026 15:29:12 -0400 Subject: [PATCH 09/15] fix(activity): remove no-op reset-attempts flag (#1156) Context: https://github.com/temporalio/api/blob/main/temporal/api/workflowservice/v1/request_response.proto#L2355-L2429 `temporal activity reset --reset-attempts` was exposed despite the reset API having no corresponding request field. Reset always starts the Activity from attempt one. Removed the no-op flag from `activity reset`, stopped populating the unused batch field, clarified reset help text, and regenerated command bindings. - `go test ./internal/temporalcli -run 'TestSharedServerSuite/TestActivity' -count=1` - `gofmt` - `git diff --check` - [x] This change works against an OSS server. - [x] All remaining documented flags are functional. - [x] No JSON output changes. (cherry picked from commit 7d311e4b4cc6d9f87729279f93b3e33d2c5d4f5e) --- internal/temporalcli/commands.activity.go | 3 +-- internal/temporalcli/commands.gen.go | 8 ++------ internal/temporalcli/commands.yaml | 13 +++---------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/internal/temporalcli/commands.activity.go b/internal/temporalcli/commands.activity.go index 582f5d622..834303319 100644 --- a/internal/temporalcli/commands.activity.go +++ b/internal/temporalcli/commands.activity.go @@ -924,8 +924,7 @@ func (c *TemporalActivityResetCommand) run(cctx *CommandContext, args []string) } else { // batch operation resetActivitiesOperation := &batch.BatchOperationResetActivities{ Identity: c.Parent.Identity, - ResetAttempts: c.ResetAttempts, - ResetHeartbeat: c.ResetHeartbeats, + ResetHeartbeat: true, KeepPaused: c.KeepPaused, Jitter: durationpb.New(c.Jitter.Duration()), RestoreOriginalOptions: c.RestoreOriginalOptions, diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index d9e809a40..11629aa39 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -758,8 +758,6 @@ type TemporalActivityResetCommand struct { SingleActivityOrBatchOptions ActivityId string KeepPaused bool - ResetAttempts bool - ResetHeartbeats bool Jitter cliext.FlagDuration RestoreOriginalOptions bool } @@ -771,15 +769,13 @@ func NewTemporalActivityResetCommand(cctx *CommandContext, parent *TemporalActiv s.Command.Use = "reset [flags]" s.Command.Short = "Reset an Activity" if hasHighlighting { - s.Command.Long = "Reset an activity. Not supported for Standalone Activities.\nThis restarts the activity as if it were first being\nscheduled. That is, it will reset both the number of attempts and the\nactivity timeout, as well as, optionally, the\nheartbeat details.\n\nIf the activity may be executing (i.e. it has not yet timed out), the\nreset will take effect the next time it fails, heartbeats, or times out.\nIf is waiting for a retry (i.e. has failed or timed out), the reset\nwill apply immediately.\n\nIf the activity is already paused, it will be unpaused by default.\nYou can specify \x1b[1mkeep_paused\x1b[0m to prevent this.\n\nIf the activity is paused and the \x1b[1mkeep_paused\x1b[0m flag is not provided,\nit will be unpaused. If the activity is paused and \x1b[1mkeep_paused\x1b[0m flag\nis provided - it will stay paused.\n\nEither \x1b[1m--activity-id\x1b[0m (with \x1b[1m--workflow-id\x1b[0m) or \x1b[1m--query\x1b[0m must be specified.\n\n### Resetting activities that heartbeat {#reset-heartbeats}\n\nActivities that heartbeat will receive a Canceled failure\nthe next time they heartbeat after a reset.\n\nIf, in your Activity, you need to do any cleanup when an Activity is\nreset, handle this error and then re-throw it when you've cleaned up.\n\nIf the \x1b[1mreset_heartbeats\x1b[0m flag is set, the heartbeat details will also be cleared.\n\nSpecify the Activity and Workflow IDs:\n\n\x1b[1mtemporal activity reset \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\n --keep-paused\n --reset-heartbeats\x1b[0m\n\nActivities can be reset in bulk with a visibility query list filter:\n\n\x1b[1mtemporal activity reset \\\n --query 'WorkflowType=\"YourWorkflow\"'\x1b[0m" + s.Command.Long = "Reset an activity.\nThis restarts the activity as if it were first being scheduled: the\nattempt count returns to one, its per-attempt timeouts are re-armed, and\nits heartbeat details are cleared.\n\nIf the activity may be executing (i.e. it has not yet timed out), the\nreset will take effect the next time it fails, heartbeats, or times out.\nIf is waiting for a retry (i.e. has failed or timed out), the reset\nwill apply immediately.\n\nIf the activity is already paused, it will be unpaused by default.\nYou can specify \x1b[1m--keep-paused\x1b[0m to prevent this.\n\nIf the activity is paused and the \x1b[1m--keep-paused\x1b[0m flag is not provided,\nit will be unpaused. If the activity is paused and the \x1b[1m--keep-paused\x1b[0m\nflag is provided, it will stay paused.\n\nEither \x1b[1m--activity-id\x1b[0m (with \x1b[1m--workflow-id\x1b[0m for a workflow Activity, or\nalone for a standalone Activity) or \x1b[1m--query\x1b[0m must be specified.\n\n### Resetting activities that heartbeat {#reset-heartbeats}\n\nActivities that heartbeat will receive a\nCanceled failure the next time\nthey heartbeat after a reset.\n\nIf, in your Activity, you need to do any cleanup when an Activity is\nreset, handle this error and then re-throw it when you've cleaned up.\n\nReset always clears the heartbeat details.\n\nSpecify the Activity and Workflow IDs:\n\n\x1b[1mtemporal activity reset \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId \\\n --keep-paused\x1b[0m\n\nActivities can be reset in bulk with a visibility query list filter:\n\n\x1b[1mtemporal activity reset \\\n --query 'WorkflowType=\"YourWorkflow\"'\x1b[0m\n\nOmit \x1b[1m--workflow-id\x1b[0m to target a Standalone Activity by Activity ID\nand optional Run ID." } else { - s.Command.Long = "Reset an activity. Not supported for Standalone Activities.\nThis restarts the activity as if it were first being\nscheduled. That is, it will reset both the number of attempts and the\nactivity timeout, as well as, optionally, the\nheartbeat details.\n\nIf the activity may be executing (i.e. it has not yet timed out), the\nreset will take effect the next time it fails, heartbeats, or times out.\nIf is waiting for a retry (i.e. has failed or timed out), the reset\nwill apply immediately.\n\nIf the activity is already paused, it will be unpaused by default.\nYou can specify `keep_paused` to prevent this.\n\nIf the activity is paused and the `keep_paused` flag is not provided,\nit will be unpaused. If the activity is paused and `keep_paused` flag\nis provided - it will stay paused.\n\nEither `--activity-id` (with `--workflow-id`) or `--query` must be specified.\n\n### Resetting activities that heartbeat {#reset-heartbeats}\n\nActivities that heartbeat will receive a Canceled failure\nthe next time they heartbeat after a reset.\n\nIf, in your Activity, you need to do any cleanup when an Activity is\nreset, handle this error and then re-throw it when you've cleaned up.\n\nIf the `reset_heartbeats` flag is set, the heartbeat details will also be cleared.\n\nSpecify the Activity and Workflow IDs:\n\n```\ntemporal activity reset \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\n --keep-paused\n --reset-heartbeats\n```\n\nActivities can be reset in bulk with a visibility query list filter:\n\n```\ntemporal activity reset \\\n --query 'WorkflowType=\"YourWorkflow\"'\n```" + s.Command.Long = "Reset an activity.\nThis restarts the activity as if it were first being scheduled: the\nattempt count returns to one, its per-attempt timeouts are re-armed, and\nits heartbeat details are cleared.\n\nIf the activity may be executing (i.e. it has not yet timed out), the\nreset will take effect the next time it fails, heartbeats, or times out.\nIf is waiting for a retry (i.e. has failed or timed out), the reset\nwill apply immediately.\n\nIf the activity is already paused, it will be unpaused by default.\nYou can specify `--keep-paused` to prevent this.\n\nIf the activity is paused and the `--keep-paused` flag is not provided,\nit will be unpaused. If the activity is paused and the `--keep-paused`\nflag is provided, it will stay paused.\n\nEither `--activity-id` (with `--workflow-id` for a workflow Activity, or\nalone for a standalone Activity) or `--query` must be specified.\n\n### Resetting activities that heartbeat {#reset-heartbeats}\n\nActivities that heartbeat will receive a\nCanceled failure the next time\nthey heartbeat after a reset.\n\nIf, in your Activity, you need to do any cleanup when an Activity is\nreset, handle this error and then re-throw it when you've cleaned up.\n\nReset always clears the heartbeat details.\n\nSpecify the Activity and Workflow IDs:\n\n```\ntemporal activity reset \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId \\\n --keep-paused\n```\n\nActivities can be reset in bulk with a visibility query list filter:\n\n```\ntemporal activity reset \\\n --query 'WorkflowType=\"YourWorkflow\"'\n```\n\nOmit `--workflow-id` to target a Standalone Activity by Activity ID\nand optional Run ID." } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVarP(&s.ActivityId, "activity-id", "a", "", "The Activity ID to reset. Mutually exclusive with `--query`. Requires `--workflow-id` to be specified.") s.Command.Flags().BoolVar(&s.KeepPaused, "keep-paused", false, "If the activity was paused, it will stay paused.") - s.Command.Flags().BoolVar(&s.ResetAttempts, "reset-attempts", false, "Reset the activity attempts.") - s.Command.Flags().BoolVar(&s.ResetHeartbeats, "reset-heartbeats", false, "Reset the Activity's heartbeats.") s.Jitter = 0 s.Command.Flags().Var(&s.Jitter, "jitter", "The activity will reset at random a time within the specified duration. Can only be used with --query.") s.Command.Flags().BoolVar(&s.RestoreOriginalOptions, "restore-original-options", false, "Restore the original options of the activity.") diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index f56fc3d0b..250ccd401 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -561,10 +561,9 @@ commands: summary: Reset an Activity description: | Reset an activity. Not supported for Standalone Activities. - This restarts the activity as if it were first being - scheduled. That is, it will reset both the number of attempts and the - activity timeout, as well as, optionally, the - [heartbeat details](#reset-heartbeats). + This restarts the activity as if it were first being scheduled: the + attempt count returns to one, its per-attempt timeouts are re-armed, and + its [heartbeat details](#reset-heartbeats) are cleared. If the activity may be executing (i.e. it has not yet timed out), the reset will take effect the next time it fails, heartbeats, or times out. @@ -614,12 +613,6 @@ commands: - name: keep-paused type: bool description: If the activity was paused, it will stay paused. - - name: reset-attempts - type: bool - description: Reset the activity attempts. - - name: reset-heartbeats - type: bool - description: Reset the Activity's heartbeats. - name: jitter type: duration description: | From 5372d1b19d471a1e02a22722d20147ad1600181a Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Thu, 13 Aug 2026 03:14:31 +0000 Subject: [PATCH 10/15] feat: add temporal options command and declutter help output (#1061) - Adds `temporal options` command (kubectl-style) that displays global and connection flags in a table with env var and config key columns - Hides global flags from root `--help` and connection flags from subcommand `--help`, replacing them with a hint to `temporal options` - Adds `hide-from-help`, `config-key`, and `Description()` support to the option set YAML spec and code generator - Shortens flag descriptions to remove info now shown in dedicated table columns ``` $ temporal --help ... Flags: --client-connect-timeout duration ... --color string ... --command-timeout duration ... ... (15 more global flags) ``` ``` $ temporal --help ... Use "temporal options" for global and connection options. ``` ``` $ temporal options Global options ... FLAG ENV DESCRIPTION --env string TEMPORAL_ENV Active environment name ... ... Connection options ... FLAG ENV CONFIG KEY DESCRIPTION --address string TEMPORAL_ADDRESS address Temporal Service gRPC endpoint ... --api-key string TEMPORAL_API_KEY api_key API key for request ... ``` - [x] `go build ./cmd/temporal` - [x] `go test ./internal/temporalcli/ -run TestHelp` - [ ] Verify `temporal --help` no longer shows global flags - [ ] Verify `temporal workflow --help` no longer shows connection flags - [ ] Verify `temporal workflow list --help` shows only command-specific flags - [ ] Verify `temporal options` displays both tables with correct env/config columns - [ ] Verify all flags still work when passed on the command line --------- Co-authored-by: Ross Nelson (cherry picked from commit 9112b4e0ac7a2b4321251f5922659363813feb7a) --- cliext/flags.gen.go | 84 ++++-- cliext/option-sets.yaml | 99 ++++--- internal/commandsgen/code.go | 30 ++- internal/commandsgen/parse.go | 2 + internal/temporalcli/commands.gen.go | 291 ++++++++++++++++++++- internal/temporalcli/commands.go | 131 +++++++++- internal/temporalcli/commands.help_test.go | 6 +- internal/temporalcli/commands.yaml | 5 + 8 files changed, 556 insertions(+), 92 deletions(-) diff --git a/cliext/flags.gen.go b/cliext/flags.gen.go index dafd27121..83524823a 100644 --- a/cliext/flags.gen.go +++ b/cliext/flags.gen.go @@ -30,16 +30,20 @@ type CommonOptions struct { FlagSet *pflag.FlagSet } +func (v *CommonOptions) Description() string { + return "These options apply to every command. They control output formatting,\nlogging, and which configuration profile and environment to use.\nOptions that accept an environment variable can be set instead of\npassing the flag each time.\n" +} + func (v *CommonOptions) BuildFlags(f *pflag.FlagSet) { v.FlagSet = f - f.StringVar(&v.Env, "env", "default", "Active environment name (`ENV`).") - f.StringVar(&v.EnvFile, "env-file", "", "Path to environment settings file. Defaults to `$HOME/.config/temporalio/temporal.yaml`.") - f.StringVar(&v.ConfigFile, "config-file", "", "File path to read TOML config from, defaults to `$CONFIG_PATH/temporalio/temporal.toml` where `$CONFIG_PATH` is defined as `$HOME/.config` on Unix, `$HOME/Library/Application Support` on macOS, and `%AppData%` on Windows.") - f.StringVar(&v.Profile, "profile", "", "Profile to use for config file.") - f.BoolVar(&v.DisableConfigFile, "disable-config-file", false, "If set, disables loading environment config from config file.") - f.BoolVar(&v.DisableConfigEnv, "disable-config-env", false, "If set, disables loading environment config from environment variables.") + f.StringVar(&v.Env, "env", "default", "Active environment name (`ENV`). Env: TEMPORAL_ENV.") + f.StringVar(&v.EnvFile, "env-file", "", "Path to environment settings file. Env: TEMPORAL_ENV_FILE.") + f.StringVar(&v.ConfigFile, "config-file", "", "TOML config file path. Env: TEMPORAL_CONFIG_FILE.") + f.StringVar(&v.Profile, "profile", "", "Configuration profile to use. Overrides the TEMPORAL_PROFILE environment variable and defaults to \"default\". Env: TEMPORAL_PROFILE.") + f.BoolVar(&v.DisableConfigFile, "disable-config-file", false, "Disable loading config from file.") + f.BoolVar(&v.DisableConfigEnv, "disable-config-env", false, "Disable loading config from environment variables.") v.LogLevel = NewFlagStringEnum([]string{"debug", "info", "warn", "error", "never"}, "never") - f.Var(&v.LogLevel, "log-level", "Log level. Default is \"never\" for most commands and \"warn\" for \"server start-dev\". Accepted values: debug, info, warn, error, never.") + f.Var(&v.LogLevel, "log-level", "Log level. Accepted values: debug, info, warn, error, never.") v.LogFormat = NewFlagStringEnum([]string{"text", "json", "pretty"}, "text") f.Var(&v.LogFormat, "log-format", "Log format. Accepted values: text, json.") v.Output = NewFlagStringEnum([]string{"text", "json", "jsonl", "none"}, "text") @@ -50,9 +54,9 @@ func (v *CommonOptions) BuildFlags(f *pflag.FlagSet) { f.Var(&v.Color, "color", "Output coloring. Accepted values: always, never, auto.") f.BoolVar(&v.NoJsonShorthandPayloads, "no-json-shorthand-payloads", false, "Raw payload output, even if the JSON option was used.") v.CommandTimeout = 0 - f.Var(&v.CommandTimeout, "command-timeout", "The command execution timeout. 0s means no timeout.") + f.Var(&v.CommandTimeout, "command-timeout", "Command execution timeout.") v.ClientConnectTimeout = 0 - f.Var(&v.ClientConnectTimeout, "client-connect-timeout", "The client connection timeout. 0s means no timeout.") + f.Var(&v.ClientConnectTimeout, "client-connect-timeout", "Client connection timeout.") } type ClientOptions struct { @@ -77,24 +81,52 @@ type ClientOptions struct { FlagSet *pflag.FlagSet } +func (v *ClientOptions) Description() string { + return "These options apply to commands that connect to a Temporal Service\n(workflow, activity, schedule, etc). They specify the server address,\nnamespace, authentication, and TLS settings. Values are resolved in\norder: CLI flag > environment variable > config file.\n\nTo persist these settings, use:\n temporal config set --prop KEY --value VALUE\n" +} + func (v *ClientOptions) BuildFlags(f *pflag.FlagSet) { v.FlagSet = f - f.StringVar(&v.Address, "address", "localhost:7233", "Temporal Service gRPC endpoint.") + f.StringVar(&v.Address, "address", "localhost:7233", "Temporal Service gRPC endpoint. Env: TEMPORAL_ADDRESS. Config: address.") f.StringVar(&v.ClientAuthority, "client-authority", "", "Temporal gRPC client :authority pseudoheader.") - f.StringVarP(&v.Namespace, "namespace", "n", "default", "Temporal Service Namespace.") - f.StringVar(&v.ApiKey, "api-key", "", "API key for request.") - f.StringArrayVar(&v.GrpcMeta, "grpc-meta", nil, "HTTP headers for requests. Format as a `KEY=VALUE` pair. May be passed multiple times to set multiple headers. Can also be made available via environment variable as `TEMPORAL_GRPC_META_[name]`.") - f.BoolVar(&v.Tls, "tls", false, "Enable base TLS encryption. Does not have additional options like mTLS or client certs. This is defaulted to true if api-key or any other TLS options are present. Use --tls=false to explicitly disable.") - f.StringVar(&v.TlsCertPath, "tls-cert-path", "", "Path to x509 certificate. Can't be used with --tls-cert-data.") - f.StringVar(&v.TlsCertData, "tls-cert-data", "", "Data for x509 certificate. Can't be used with --tls-cert-path.") - f.StringVar(&v.TlsKeyPath, "tls-key-path", "", "Path to x509 private key. Can't be used with --tls-key-data.") - f.StringVar(&v.TlsKeyData, "tls-key-data", "", "Private certificate key data. Can't be used with --tls-key-path.") - f.StringVar(&v.TlsCaPath, "tls-ca-path", "", "Path to server CA certificate. Can't be used with --tls-ca-data.") - f.StringVar(&v.TlsCaData, "tls-ca-data", "", "Data for server CA certificate. Can't be used with --tls-ca-path.") - f.BoolVar(&v.TlsDisableHostVerification, "tls-disable-host-verification", false, "Disable TLS host-name verification.") - f.StringVar(&v.TlsServerName, "tls-server-name", "", "Override target TLS server name.") - f.StringVar(&v.CodecEndpoint, "codec-endpoint", "", "Remote Codec Server endpoint.") - f.StringVar(&v.CodecAuth, "codec-auth", "", "Authorization header for Codec Server requests.") - f.StringArrayVar(&v.CodecHeader, "codec-header", nil, "HTTP headers for requests to codec server. Format as a `KEY=VALUE` pair. May be passed multiple times to set multiple headers.") - f.StringVar(&v.Identity, "identity", "", "The identity of the user or client submitting this request. Defaults to \"temporal-cli:$USER@$HOST\".") + f.StringVarP(&v.Namespace, "namespace", "n", "default", "Temporal Service Namespace. Env: TEMPORAL_NAMESPACE. Config: namespace.") + f.StringVar(&v.ApiKey, "api-key", "", "API key for request. Env: TEMPORAL_API_KEY. Config: api_key.") + f.StringArrayVar(&v.GrpcMeta, "grpc-meta", nil, "HTTP headers for requests (KEY=VALUE, repeatable). Config: grpc_meta..") + f.BoolVar(&v.Tls, "tls", false, "Enable base TLS encryption. Auto-enabled when api-key or TLS options are set. Env: TEMPORAL_TLS. Config: tls.") + f.StringVar(&v.TlsCertPath, "tls-cert-path", "", "Path to x509 certificate. Env: TEMPORAL_TLS_CLIENT_CERT_PATH. Config: tls.client_cert_path.") + f.StringVar(&v.TlsCertData, "tls-cert-data", "", "Inline x509 certificate data. Env: TEMPORAL_TLS_CLIENT_CERT_DATA. Config: tls.client_cert_data.") + f.StringVar(&v.TlsKeyPath, "tls-key-path", "", "Path to x509 private key. Env: TEMPORAL_TLS_CLIENT_KEY_PATH. Config: tls.client_key_path.") + f.StringVar(&v.TlsKeyData, "tls-key-data", "", "Inline x509 private key data. Env: TEMPORAL_TLS_CLIENT_KEY_DATA. Config: tls.client_key_data.") + f.StringVar(&v.TlsCaPath, "tls-ca-path", "", "Path to server CA certificate. Env: TEMPORAL_TLS_SERVER_CA_CERT_PATH. Config: tls.server_ca_cert_path.") + f.StringVar(&v.TlsCaData, "tls-ca-data", "", "Inline server CA certificate data. Env: TEMPORAL_TLS_SERVER_CA_CERT_DATA. Config: tls.server_ca_cert_data.") + f.BoolVar(&v.TlsDisableHostVerification, "tls-disable-host-verification", false, "Disable TLS host-name verification. Env: TEMPORAL_TLS_DISABLE_HOST_VERIFICATION. Config: tls.disable_host_verification.") + f.StringVar(&v.TlsServerName, "tls-server-name", "", "Override target TLS server name. Env: TEMPORAL_TLS_SERVER_NAME. Config: tls.server_name.") + f.StringVar(&v.CodecEndpoint, "codec-endpoint", "", "Remote Codec Server endpoint. Env: TEMPORAL_CODEC_ENDPOINT. Config: codec.endpoint.") + f.StringVar(&v.CodecAuth, "codec-auth", "", "Authorization header for Codec Server requests. Env: TEMPORAL_CODEC_AUTH. Config: codec.auth.") + f.StringArrayVar(&v.CodecHeader, "codec-header", nil, "HTTP headers for codec server (KEY=VALUE, repeatable).") + f.StringVar(&v.Identity, "identity", "", "Identity of the client submitting requests.") +} + +func (v *ClientOptions) HideFlags() { + if v.FlagSet == nil { + return + } + v.FlagSet.Lookup("address").Hidden = true + v.FlagSet.Lookup("client-authority").Hidden = true + v.FlagSet.Lookup("namespace").Hidden = true + v.FlagSet.Lookup("api-key").Hidden = true + v.FlagSet.Lookup("grpc-meta").Hidden = true + v.FlagSet.Lookup("tls").Hidden = true + v.FlagSet.Lookup("tls-cert-path").Hidden = true + v.FlagSet.Lookup("tls-cert-data").Hidden = true + v.FlagSet.Lookup("tls-key-path").Hidden = true + v.FlagSet.Lookup("tls-key-data").Hidden = true + v.FlagSet.Lookup("tls-ca-path").Hidden = true + v.FlagSet.Lookup("tls-ca-data").Hidden = true + v.FlagSet.Lookup("tls-disable-host-verification").Hidden = true + v.FlagSet.Lookup("tls-server-name").Hidden = true + v.FlagSet.Lookup("codec-endpoint").Hidden = true + v.FlagSet.Lookup("codec-auth").Hidden = true + v.FlagSet.Lookup("codec-header").Hidden = true + v.FlagSet.Lookup("identity").Hidden = true } diff --git a/cliext/option-sets.yaml b/cliext/option-sets.yaml index 1ebad77af..65adfcfc1 100644 --- a/cliext/option-sets.yaml +++ b/cliext/option-sets.yaml @@ -4,6 +4,11 @@ option-sets: - name: common + description: | + These options apply to every command. They control output formatting, + logging, and which configuration profile and environment to use. + Options that accept an environment variable can be set instead of + passing the flag each time. options: - name: env type: string @@ -12,17 +17,11 @@ option-sets: implied-env: TEMPORAL_ENV - name: env-file type: string - description: | - Path to environment settings file. - Defaults to `$HOME/.config/temporalio/temporal.yaml`. + description: Path to environment settings file. implied-env: TEMPORAL_ENV_FILE - name: config-file type: string - description: | - File path to read TOML config from, defaults to - `$CONFIG_PATH/temporalio/temporal.toml` where `$CONFIG_PATH` is defined - as `$HOME/.config` on Unix, `$HOME/Library/Application Support` on - macOS, and `%AppData%` on Windows. + description: TOML config file path. implied-env: TEMPORAL_CONFIG_FILE - name: profile type: string @@ -30,13 +29,10 @@ option-sets: implied-env: TEMPORAL_PROFILE - name: disable-config-file type: bool - description: | - If set, disables loading environment config from config file. + description: Disable loading config from file. - name: disable-config-env type: bool - description: | - If set, disables loading environment config from environment - variables. + description: Disable loading config from environment variables. - name: log-level type: string-enum enum-values: @@ -45,9 +41,7 @@ option-sets: - warn - error - never - description: | - Log level. - Default is "never" for most commands and "warn" for "server start-dev". + description: Log level. default: never - name: log-format type: string-enum @@ -89,20 +83,28 @@ option-sets: description: Raw payload output, even if the JSON option was used. - name: command-timeout type: duration - description: | - The command execution timeout. 0s means no timeout. + description: Command execution timeout. - name: client-connect-timeout type: duration - description: | - The client connection timeout. 0s means no timeout. + description: Client connection timeout. - name: client + hide-from-help: true + description: | + These options apply to commands that connect to a Temporal Service + (workflow, activity, schedule, etc). They specify the server address, + namespace, authentication, and TLS settings. Values are resolved in + order: CLI flag > environment variable > config file. + + To persist these settings, use: + temporal config set --prop KEY --value VALUE options: - name: address type: string description: Temporal Service gRPC endpoint. default: localhost:7233 implied-env: TEMPORAL_ADDRESS + config-key: address - name: client-authority type: string description: Temporal gRPC client :authority pseudoheader. @@ -112,83 +114,74 @@ option-sets: description: Temporal Service Namespace. default: default implied-env: TEMPORAL_NAMESPACE + config-key: namespace - name: api-key type: string description: API key for request. implied-env: TEMPORAL_API_KEY + config-key: api_key - name: grpc-meta type: string[] - description: | - HTTP headers for requests. - Format as a `KEY=VALUE` pair. - May be passed multiple times to set multiple headers. - Can also be made available via environment variable as - `TEMPORAL_GRPC_META_[name]`. + description: HTTP headers for requests (KEY=VALUE, repeatable). + config-key: grpc_meta. - name: tls type: bool - description: | - Enable base TLS encryption. Does not have additional options like mTLS - or client certs. This is defaulted to true if api-key or any other TLS - options are present. Use --tls=false to explicitly disable. + description: Enable base TLS encryption. Auto-enabled when api-key or TLS options are set. implied-env: TEMPORAL_TLS + config-key: tls - name: tls-cert-path type: string - description: | - Path to x509 certificate. - Can't be used with --tls-cert-data. + description: Path to x509 certificate. implied-env: TEMPORAL_TLS_CLIENT_CERT_PATH + config-key: tls.client_cert_path - name: tls-cert-data type: string - description: | - Data for x509 certificate. - Can't be used with --tls-cert-path. + description: Inline x509 certificate data. implied-env: TEMPORAL_TLS_CLIENT_CERT_DATA + config-key: tls.client_cert_data - name: tls-key-path type: string - description: | - Path to x509 private key. - Can't be used with --tls-key-data. + description: Path to x509 private key. implied-env: TEMPORAL_TLS_CLIENT_KEY_PATH + config-key: tls.client_key_path - name: tls-key-data type: string - description: | - Private certificate key data. - Can't be used with --tls-key-path. + description: Inline x509 private key data. implied-env: TEMPORAL_TLS_CLIENT_KEY_DATA + config-key: tls.client_key_data - name: tls-ca-path type: string - description: | - Path to server CA certificate. - Can't be used with --tls-ca-data. + description: Path to server CA certificate. implied-env: TEMPORAL_TLS_SERVER_CA_CERT_PATH + config-key: tls.server_ca_cert_path - name: tls-ca-data type: string - description: | - Data for server CA certificate. - Can't be used with --tls-ca-path. + description: Inline server CA certificate data. implied-env: TEMPORAL_TLS_SERVER_CA_CERT_DATA + config-key: tls.server_ca_cert_data - name: tls-disable-host-verification type: bool description: Disable TLS host-name verification. implied-env: TEMPORAL_TLS_DISABLE_HOST_VERIFICATION + config-key: tls.disable_host_verification - name: tls-server-name type: string description: Override target TLS server name. implied-env: TEMPORAL_TLS_SERVER_NAME + config-key: tls.server_name - name: codec-endpoint type: string description: Remote Codec Server endpoint. implied-env: TEMPORAL_CODEC_ENDPOINT + config-key: codec.endpoint - name: codec-auth type: string description: Authorization header for Codec Server requests. implied-env: TEMPORAL_CODEC_AUTH + config-key: codec.auth - name: codec-header type: string[] - description: | - HTTP headers for requests to codec server. - Format as a `KEY=VALUE` pair. - May be passed multiple times to set multiple headers. + description: HTTP headers for codec server (KEY=VALUE, repeatable). - name: identity type: string - description: The identity of the user or client submitting this request. Defaults to "temporal-cli:$USER@$HOST". + description: Identity of the client submitting requests. diff --git a/internal/commandsgen/code.go b/internal/commandsgen/code.go index 84cff3bbc..4af7a0add 100644 --- a/internal/commandsgen/code.go +++ b/internal/commandsgen/code.go @@ -143,6 +143,12 @@ func (o *OptionSets) writeCode(w *codeWriter) error { w.writeLinef("FlagSet *%v.FlagSet", w.importPflag()) w.writeLinef("}\n") + // write description if present + if o.Description != "" { + w.writeLinef("func (v *%v) Description() string { return %q }", o.setStructName(), o.Description) + w.writeLinef("") + } + // write flags w.writeLinef("func (v *%v) BuildFlags(f *%v.FlagSet) {", o.setStructName(), w.importPflag()) @@ -150,6 +156,16 @@ func (o *OptionSets) writeCode(w *codeWriter) error { o.writeFlagBuilding("v", "f", w) w.writeLinef("}\n") + // write HideFlags if hide-from-help is set + if o.HideFromHelp { + w.writeLinef("func (v *%v) HideFlags() {", o.setStructName()) + w.writeLinef("if v.FlagSet == nil { return }") + for _, opt := range o.Options { + w.writeLinef("v.FlagSet.Lookup(%q).Hidden = true", opt.Name) + } + w.writeLinef("}\n") + } + return nil } @@ -285,9 +301,15 @@ func (c *Command) writeCode(w *codeWriter) error { if optSet != nil && optSet.ExternalPackage != "" { // External option-set: use type name with Options suffix w.writeLinef("s.%vOptions.BuildFlags(%v)", namify(include, true), flagVar) + if optSet.HideFromHelp { + w.writeLinef("s.%vOptions.HideFlags()", namify(include, true)) + } } else { // Internal option-set: use struct name w.writeLinef("s.%v.BuildFlags(%v)", setStructName(include), flagVar) + if optSet != nil && optSet.HideFromHelp { + w.writeLinef("s.%v.HideFlags()", setStructName(include)) + } } } @@ -432,8 +454,14 @@ func (o *Option) writeFlagBuilding(selfVar, flagVar string, w *codeWriter) error return fmt.Errorf("unrecognized data type %v", o.Type) } - // If there are enums, append to desc + // If there is an implied env var or config key, append to desc desc := o.Description + if o.ImpliedEnv != "" { + desc += fmt.Sprintf(" Env: %s.", o.ImpliedEnv) + } + if o.ConfigKey != "" { + desc += fmt.Sprintf(" Config: %s.", o.ConfigKey) + } if len(o.EnumValues) > 0 { desc += fmt.Sprintf(" Accepted values: %s.", strings.Join(o.EnumValues, ", ")) } diff --git a/internal/commandsgen/parse.go b/internal/commandsgen/parse.go index 831b86902..f3f6efd18 100644 --- a/internal/commandsgen/parse.go +++ b/internal/commandsgen/parse.go @@ -23,6 +23,7 @@ type ( Short string `yaml:"short,omitempty"` Default string `yaml:"default,omitempty"` ImpliedEnv string `yaml:"implied-env,omitempty"` + ConfigKey string `yaml:"config-key,omitempty"` Required bool `yaml:"required,omitempty"` Aliases []string `yaml:"aliases,omitempty"` EnumValues []string `yaml:"enum-values,omitempty"` @@ -64,6 +65,7 @@ type ( Description string `yaml:"description"` Options []Option `yaml:"options"` ExternalPackage string `yaml:"external-package"` + HideFromHelp bool `yaml:"hide-from-help,omitempty"` } // Commands represents the top-level structure holding commands and option sets. diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 11629aa39..b24ba0d76 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -452,9 +452,9 @@ func NewTemporalCommand(cctx *CommandContext) *TemporalCommand { s.Command.Use = "temporal" s.Command.Short = "Temporal command-line interface and development server" if hasHighlighting { - s.Command.Long = "The Temporal CLI manages, monitors, and debugs Temporal apps. It lets you run\na local Temporal Service, start Workflow Executions, pass messages to running\nWorkflows, inspect state, and more.\n\n* Start a local development service:\n \x1b[1mtemporal server start-dev\x1b[0m\n* View help: pass \x1b[1m--help\x1b[0m to any command:\n \x1b[1mtemporal activity complete --help\x1b[0m" + s.Command.Long = "The Temporal CLI manages, monitors, and debugs Temporal apps. It lets you run\na local Temporal Service, start Workflow Executions, pass messages to running\nWorkflows, inspect state, and more.\n\n* Start a local development service:\n \x1b[1mtemporal server start-dev\x1b[0m\n* View help: pass \x1b[1m--help\x1b[0m to any command:\n \x1b[1mtemporal activity complete --help\x1b[0m\n* Return structured output with \x1b[1m-o\x1b[0m:\n \x1b[1mtemporal workflow list -o json\x1b[0m\n* View global and connection options:\n \x1b[1mtemporal options\x1b[0m" } else { - s.Command.Long = "The Temporal CLI manages, monitors, and debugs Temporal apps. It lets you run\na local Temporal Service, start Workflow Executions, pass messages to running\nWorkflows, inspect state, and more.\n\n* Start a local development service:\n `temporal server start-dev`\n* View help: pass `--help` to any command:\n `temporal activity complete --help`" + s.Command.Long = "The Temporal CLI manages, monitors, and debugs Temporal apps. It lets you run\na local Temporal Service, start Workflow Executions, pass messages to running\nWorkflows, inspect state, and more.\n\n* Start a local development service:\n `temporal server start-dev`\n* View help: pass `--help` to any command:\n `temporal activity complete --help`\n* Return structured output with `-o`:\n `temporal workflow list -o json`\n* View global and connection options:\n `temporal options`" } s.Command.Args = cobra.NoArgs s.Command.AddCommand(&NewTemporalActivityCommand(cctx, &s).Command) @@ -500,6 +500,7 @@ func NewTemporalActivityCommand(cctx *CommandContext, parent *TemporalCommand) * s.Command.AddCommand(&NewTemporalActivityUnpauseCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalActivityUpdateOptionsCommand(cctx, &s).Command) s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + s.ClientOptions.HideFlags() return &s } @@ -985,6 +986,7 @@ func NewTemporalBatchCommand(cctx *CommandContext, parent *TemporalCommand) *Tem s.Command.AddCommand(&NewTemporalBatchListCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalBatchTerminateCommand(cctx, &s).Command) s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + s.ClientOptions.HideFlags() return &s } @@ -1365,6 +1367,286 @@ func NewTemporalEnvSetCommand(cctx *CommandContext, parent *TemporalEnvCommand) return &s } +type TemporalNexusCommand struct { + Parent *TemporalCommand + Command cobra.Command + cliext.ClientOptions +} + +func NewTemporalNexusCommand(cctx *CommandContext, parent *TemporalCommand) *TemporalNexusCommand { + var s TemporalNexusCommand + s.Parent = parent + s.Command.Use = "nexus" + s.Command.Short = "Start, list, and operate on Nexus Operations" + if hasHighlighting { + s.Command.Long = "Nexus Operation commands perform operations on Nexus\nOperation Executions:\n\n\x1b[1mtemporal nexus [command] [options]\x1b[0m\n\nFor example:\n\n\x1b[1mtemporal nexus operation list\x1b[0m" + } else { + s.Command.Long = "Nexus Operation commands perform operations on Nexus\nOperation Executions:\n\n```\ntemporal nexus [command] [options]\n```\n\nFor example:\n\n```\ntemporal nexus operation list\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewTemporalNexusOperationCommand(cctx, &s).Command) + s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + s.ClientOptions.HideFlags() + return &s +} + +type TemporalNexusOperationCommand struct { + Parent *TemporalNexusCommand + Command cobra.Command +} + +func NewTemporalNexusOperationCommand(cctx *CommandContext, parent *TemporalNexusCommand) *TemporalNexusOperationCommand { + var s TemporalNexusOperationCommand + s.Parent = parent + s.Command.Use = "operation" + s.Command.Short = "Commands for managing Nexus Operations" + if hasHighlighting { + s.Command.Long = "These commands manage Nexus Operation Executions.\n\nNexus Operation commands follow this syntax:\n\n\x1b[1mtemporal nexus operation [command] [options]\x1b[0m" + } else { + s.Command.Long = "These commands manage Nexus Operation Executions.\n\nNexus Operation commands follow this syntax:\n\n```\ntemporal nexus operation [command] [options]\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewTemporalNexusOperationCancelCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationCountCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationDescribeCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationExecuteCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationListCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationResultCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationStartCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationTerminateCommand(cctx, &s).Command) + return &s +} + +type TemporalNexusOperationCancelCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationReferenceOptions + Reason string +} + +func NewTemporalNexusOperationCancelCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationCancelCommand { + var s TemporalNexusOperationCancelCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "cancel [flags]" + s.Command.Short = "Request cancellation of a Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Request cancellation of a Nexus Operation.\n\n\x1b[1mtemporal nexus operation cancel \\\n --operation-id YourOperationId\x1b[0m\n\nThe Operation handler determines how to handle the\ncancellation request." + } else { + s.Command.Long = "Request cancellation of a Nexus Operation.\n\n```\ntemporal nexus operation cancel \\\n --operation-id YourOperationId\n```\n\nThe Operation handler determines how to handle the\ncancellation request." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.Reason, "reason", "", "Reason for cancellation.") + s.NexusOperationReferenceOptions.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 TemporalNexusOperationCountCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + Query string +} + +func NewTemporalNexusOperationCountCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationCountCommand { + var s TemporalNexusOperationCountCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "count [flags]" + s.Command.Short = "Count Nexus Operations matching a query (Experimental)" + if hasHighlighting { + s.Command.Long = "Return a count of Nexus Operations. Use \x1b[1m--query\x1b[0m\nto filter the operations to be counted.\n\n\x1b[1mtemporal nexus operation count \\\n --query 'Endpoint=\"YourEndpoint\"'\x1b[0m\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." + } else { + s.Command.Long = "Return a count of Nexus Operations. Use `--query`\nto filter the operations to be counted.\n\n```\ntemporal nexus operation count \\\n --query 'Endpoint=\"YourEndpoint\"'\n```\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVarP(&s.Query, "query", "q", "", "Query to filter Nexus Operation Executions to count.") + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationDescribeCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationReferenceOptions + Raw bool +} + +func NewTemporalNexusOperationDescribeCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationDescribeCommand { + var s TemporalNexusOperationDescribeCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "describe [flags]" + s.Command.Short = "Show detailed info for a Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Display detailed information about a specific Nexus\nOperation Execution.\n\n\x1b[1mtemporal nexus operation describe \\\n --operation-id YourOperationId\x1b[0m" + } else { + s.Command.Long = "Display detailed information about a specific Nexus\nOperation Execution.\n\n```\ntemporal nexus operation describe \\\n --operation-id YourOperationId\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().BoolVar(&s.Raw, "raw", false, "Print properties without changing their format.") + s.NexusOperationReferenceOptions.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 TemporalNexusOperationExecuteCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationStartOptions + PayloadInputOptions +} + +func NewTemporalNexusOperationExecuteCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationExecuteCommand { + var s TemporalNexusOperationExecuteCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "execute [flags]" + s.Command.Short = "Start a new Nexus Operation and wait for its result (Experimental)" + if hasHighlighting { + s.Command.Long = "Start a new Nexus Operation Execution and block until\nit completes. The result is output to stdout.\n\n\x1b[1mtemporal nexus operation execute \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\x1b[0m" + } else { + s.Command.Long = "Start a new Nexus Operation Execution and block until\nit completes. The result is output to stdout.\n\n```\ntemporal nexus operation execute \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\n```" + } + s.Command.Args = cobra.NoArgs + s.NexusOperationStartOptions.BuildFlags(s.Command.Flags()) + s.PayloadInputOptions.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 TemporalNexusOperationListCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + Query string + Limit int + PageSize int +} + +func NewTemporalNexusOperationListCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationListCommand { + var s TemporalNexusOperationListCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "list [flags]" + s.Command.Short = "List Nexus Operations matching a query (Experimental)" + if hasHighlighting { + s.Command.Long = "List Nexus Operations. Use \x1b[1m--query\x1b[0m to filter results.\n\n\x1b[1mtemporal nexus operation list \\\n --query 'Endpoint=\"YourEndpoint\"'\x1b[0m\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." + } else { + s.Command.Long = "List Nexus Operations. Use `--query` to filter results.\n\n```\ntemporal nexus operation list \\\n --query 'Endpoint=\"YourEndpoint\"'\n```\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVarP(&s.Query, "query", "q", "", "Query to filter the Nexus Operation Executions to list.") + s.Command.Flags().IntVar(&s.Limit, "limit", 0, "Maximum number of Nexus Operation Executions to display.") + s.Command.Flags().IntVar(&s.PageSize, "page-size", 0, "Maximum number of Nexus Operation Executions to fetch at a time from the server.") + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationResultCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationReferenceOptions +} + +func NewTemporalNexusOperationResultCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationResultCommand { + var s TemporalNexusOperationResultCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "result [flags]" + s.Command.Short = "Wait for and output the result of a Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Wait for a Nexus Operation to complete and output\nthe result.\n\n\x1b[1mtemporal nexus operation result \\\n --operation-id YourOperationId\x1b[0m" + } else { + s.Command.Long = "Wait for a Nexus Operation to complete and output\nthe result.\n\n```\ntemporal nexus operation result \\\n --operation-id YourOperationId\n```" + } + s.Command.Args = cobra.NoArgs + s.NexusOperationReferenceOptions.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 TemporalNexusOperationStartCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationStartOptions + PayloadInputOptions +} + +func NewTemporalNexusOperationStartCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationStartCommand { + var s TemporalNexusOperationStartCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "start [flags]" + s.Command.Short = "Start a new Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Start a new Nexus Operation. Outputs the\nOperation ID and Run ID.\n\n\x1b[1mtemporal nexus operation start \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\x1b[0m" + } else { + s.Command.Long = "Start a new Nexus Operation. Outputs the\nOperation ID and Run ID.\n\n```\ntemporal nexus operation start \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\n```" + } + s.Command.Args = cobra.NoArgs + s.NexusOperationStartOptions.BuildFlags(s.Command.Flags()) + s.PayloadInputOptions.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 TemporalNexusOperationTerminateCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationReferenceOptions + Reason string +} + +func NewTemporalNexusOperationTerminateCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationTerminateCommand { + var s TemporalNexusOperationTerminateCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "terminate [flags]" + s.Command.Short = "Forcefully end a Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Terminate a Nexus Operation.\n\n\x1b[1mtemporal nexus operation terminate \\\n --operation-id YourOperationId \\\n --reason YourReason\x1b[0m\n\nOperation handlers cannot see or respond to terminations." + } else { + s.Command.Long = "Terminate a Nexus Operation.\n\n```\ntemporal nexus operation terminate \\\n --operation-id YourOperationId \\\n --reason YourReason\n```\n\nOperation handlers cannot see or respond to terminations." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.Reason, "reason", "", "Reason for termination. Defaults to a message with the current user's name.") + s.NexusOperationReferenceOptions.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 TemporalOperatorCommand struct { Parent *TemporalCommand Command cobra.Command @@ -1387,6 +1669,7 @@ func NewTemporalOperatorCommand(cctx *CommandContext, parent *TemporalCommand) * s.Command.AddCommand(&NewTemporalOperatorNexusCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalOperatorSearchAttributeCommand(cctx, &s).Command) s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + s.ClientOptions.HideFlags() return &s } @@ -2104,6 +2387,7 @@ func NewTemporalScheduleCommand(cctx *CommandContext, parent *TemporalCommand) * s.Command.AddCommand(&NewTemporalScheduleTriggerCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleUpdateCommand(cctx, &s).Command) s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + s.ClientOptions.HideFlags() return &s } @@ -2504,6 +2788,7 @@ func NewTemporalTaskQueueCommand(cctx *CommandContext, parent *TemporalCommand) s.Command.AddCommand(&NewTemporalTaskQueueUpdateBuildIdsCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueVersioningCommand(cctx, &s).Command) s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + s.ClientOptions.HideFlags() return &s } @@ -3235,6 +3520,7 @@ func NewTemporalWorkerCommand(cctx *CommandContext, parent *TemporalCommand) *Te s.Command.AddCommand(&NewTemporalWorkerDescribeCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalWorkerListCommand(cctx, &s).Command) s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + s.ClientOptions.HideFlags() return &s } @@ -3826,6 +4112,7 @@ func NewTemporalWorkflowCommand(cctx *CommandContext, parent *TemporalCommand) * s.Command.AddCommand(&NewTemporalWorkflowUpdateCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalWorkflowUpdateOptionsCommand(cctx, &s).Command) s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + s.ClientOptions.HideFlags() return &s } diff --git a/internal/temporalcli/commands.go b/internal/temporalcli/commands.go index d270b52ba..788c9399f 100644 --- a/internal/temporalcli/commands.go +++ b/internal/temporalcli/commands.go @@ -425,7 +425,10 @@ func Execute(ctx context.Context, options CommandOptions) { } } -// getUsageTemplate returns a custom usage template with proper flag wrapping +// getUsageTemplate returns a custom usage template with proper flag wrapping. +// On the root command, global flags are hidden and a hint to "temporal options" +// is shown instead (similar to kubectl). On subcommands, local flags are shown +// normally and inherited flags are replaced with the same hint. // The default template can be found here: https://github.com/spf13/cobra/blob/v1.9.1/command.go#L1937-L1966 func getUsageTemplate() string { // Get terminal width, default to 80 if unable to determine @@ -455,19 +458,108 @@ Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help") {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if and .HasAvailableLocalFlags .HasParent}} Flags: -{{.LocalFlags.FlagUsagesWrapped %d | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsagesWrapped %d | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} +{{.LocalFlags.FlagUsagesWrapped %d | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} -`, flagWidth, flagWidth) + +Use "{{.Root.Name}} options" for global and connection options. +`, flagWidth) +} + +type flagRow struct { + Flag string `json:"flag"` + Env string `json:"env,omitempty"` + Config string `json:"config,omitempty"` + Description string `json:"description"` +} + +type flagRowNoConfig struct { + Flag string `json:"flag"` + Env string `json:"env,omitempty"` + Description string `json:"description"` +} + +// printFlagTable prints flags in a table using the existing printer package. +func printFlagTable(w io.Writer, flags *pflag.FlagSet) { + p := &printer.Printer{Output: w} + + // Determine which columns are needed + hasConfig := false + flags.VisitAll(func(f *pflag.Flag) { + _, _, config := parseFlagUsage(f.Usage) + if config != "" { + hasConfig = true + } + }) + + // Collect rows + var fullRows []flagRow + var shortRows []flagRowNoConfig + + flags.VisitAll(func(f *pflag.Flag) { + desc, env, config := parseFlagUsage(f.Usage) + + // Build flag name with short and type + flag := "--" + f.Name + if f.Shorthand != "" { + flag += ", -" + f.Shorthand + } + if typ := f.Value.Type(); typ != "bool" { + if typ == "stringArray" { + typ = "string[]" + } + flag += " " + typ + } + + // Add default if non-empty + if f.DefValue != "" && f.DefValue != "false" && f.DefValue != "0" && f.DefValue != "0s" && f.DefValue != "[]" { + desc += " (default " + f.DefValue + ")" + } + + fullRows = append(fullRows, flagRow{Flag: flag, Env: env, Config: config, Description: desc}) + shortRows = append(shortRows, flagRowNoConfig{Flag: flag, Env: env, Description: desc}) + }) + + opts := printer.StructuredOptions{Table: &printer.TableOptions{}} + if hasConfig { + _ = p.PrintStructured(fullRows, opts) + } else { + _ = p.PrintStructured(shortRows, opts) + } +} + +// parseFlagUsage extracts the base description, env var, and config key +// from a flag usage string. It looks for "Env: VALUE." and "Config: VALUE." +// suffixes that were added by the code generator. +// +// Env var values never contain dots, so we split on the first dot. +// Config key values may contain dots (e.g. "tls.server_name"), so we +// split on the last dot. +func parseFlagUsage(usage string) (desc, env, config string) { + desc = usage + // Extract config first (uses last dot) since it may appear after env + if i := strings.Index(desc, " Config: "); i >= 0 { + rest := desc[i+9:] + if j := strings.LastIndex(rest, "."); j >= 0 { + config = rest[:j] + desc = strings.TrimSpace(desc[:i] + rest[j+1:]) + } + } + // Extract env (uses first dot since env vars have no dots) + if i := strings.Index(desc, " Env: "); i >= 0 { + rest := desc[i+6:] + if j := strings.Index(rest, "."); j >= 0 { + env = rest[:j] + desc = strings.TrimSpace(desc[:i] + rest[j+1:]) + } + } + return } func (c *TemporalCommand) initCommand(cctx *CommandContext) { @@ -482,6 +574,31 @@ func (c *TemporalCommand) initCommand(cctx *CommandContext) { // Customize the built-in help command to support --all/-a for listing extensions customizeHelpCommand(&c.Command) + // Add "options" command to list global and connection flags (similar to kubectl options) + c.Command.AddCommand(&cobra.Command{ + Use: "options", + Short: "Print global and connection options inherited by all commands", + Long: "Print the list of global and connection flags available across commands.", + Run: func(cmd *cobra.Command, args []string) { + w := cmd.OutOrStdout() + var commonOpts cliext.CommonOptions + fmt.Fprintln(w, "Global options") + fmt.Fprintln(w) + fmt.Fprint(w, commonOpts.Description()) + fmt.Fprintln(w) + printFlagTable(w, cmd.Root().PersistentFlags()) + fmt.Fprintln(w) + var clientOpts cliext.ClientOptions + fmt.Fprintln(w, "Connection options") + fmt.Fprintln(w) + fmt.Fprint(w, clientOpts.Description()) + fmt.Fprintln(w) + connFlags := pflag.NewFlagSet("connection", pflag.ContinueOnError) + clientOpts.BuildFlags(connFlags) + printFlagTable(w, connFlags) + }, + }) + // Unfortunately color is a global option, so we can set in pre-run but we // must unset in post-run origNoColor := color.NoColor diff --git a/internal/temporalcli/commands.help_test.go b/internal/temporalcli/commands.help_test.go index a73d48a61..fe8c88acd 100644 --- a/internal/temporalcli/commands.help_test.go +++ b/internal/temporalcli/commands.help_test.go @@ -17,6 +17,8 @@ func TestHelp_Root(t *testing.T) { assert.Contains(t, res.Stdout.String(), "Available Commands:") assert.Contains(t, res.Stdout.String(), "workflow") + assert.Contains(t, res.Stdout.String(), "temporal workflow list -o json") + assert.Contains(t, res.Stdout.String(), "temporal options") assert.NoError(t, res.Err) } @@ -70,12 +72,10 @@ func TestHelp_AllFlag_ShowsExtensions(t *testing.T) { assert.Contains(t, out, "foo") // shown now! assert.NotContains(t, out, "bar-baz") // is under workflow - // Verify foo appears in Available Commands section (between "Available Commands:" and "Flags:") + // Verify foo appears in Available Commands section availableIdx := strings.Index(out, "Available Commands:") fooIdx := strings.Index(out, "foo") - flagsIdx := strings.Index(out, "Flags:") assert.Greater(t, fooIdx, availableIdx, "foo should appear after Available Commands:") - assert.Less(t, fooIdx, flagsIdx, "foo should appear before Flags:") assert.NoError(t, res.Err) // Non-executable extensions are skipped diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 250ccd401..c71c7578f 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -141,6 +141,10 @@ commands: `temporal server start-dev` * View help: pass `--help` to any command: `temporal activity complete --help` + * Return structured output with `-o`: + `temporal workflow list -o json` + * View global and connection options: + `temporal options` has-init: true option-sets: - common @@ -4795,6 +4799,7 @@ option-sets: external-package: github.com/temporalio/cli/cliext - name: client external-package: github.com/temporalio/cli/cliext + hide-from-help: true - name: overlap-policy options: From e5bb34307600ae939ecdcd94870f7d7f3a3a247c Mon Sep 17 00:00:00 2001 From: dryrun Date: Mon, 31 Aug 2026 12:51:33 -0700 Subject: [PATCH 11/15] backport: pin compatible dependency set and adjust #1156 for 1.8.x - api v1.62.9 (ceiling for server v1.31.2: v1.62.10 adds CountNexusOperationExecutions to WorkflowServiceClient) - sdk v1.41.1, envconfig v1.0.0, ui-server v2.50.1 - safe non-Temporal bumps: go-isatty, x/tools, grpc, echo, testify - #1156 backport keeps --reset-heartbeats (its removal belongs to #1159) - cliext pinned to tagged sdk (main uses a pseudo-version) --- cliext/flags.gen.go | 2 +- cliext/go.mod | 7 +- cliext/go.sum | 14 +- go.mod | 63 +++-- go.sum | 129 +++++----- internal/temporalcli/commands.activity.go | 2 +- internal/temporalcli/commands.gen.go | 286 +--------------------- internal/temporalcli/commands.yaml | 3 + 8 files changed, 113 insertions(+), 393 deletions(-) diff --git a/cliext/flags.gen.go b/cliext/flags.gen.go index 83524823a..22c6f9268 100644 --- a/cliext/flags.gen.go +++ b/cliext/flags.gen.go @@ -39,7 +39,7 @@ func (v *CommonOptions) BuildFlags(f *pflag.FlagSet) { f.StringVar(&v.Env, "env", "default", "Active environment name (`ENV`). Env: TEMPORAL_ENV.") f.StringVar(&v.EnvFile, "env-file", "", "Path to environment settings file. Env: TEMPORAL_ENV_FILE.") f.StringVar(&v.ConfigFile, "config-file", "", "TOML config file path. Env: TEMPORAL_CONFIG_FILE.") - f.StringVar(&v.Profile, "profile", "", "Configuration profile to use. Overrides the TEMPORAL_PROFILE environment variable and defaults to \"default\". Env: TEMPORAL_PROFILE.") + f.StringVar(&v.Profile, "profile", "", "Profile to use for config file. Env: TEMPORAL_PROFILE.") f.BoolVar(&v.DisableConfigFile, "disable-config-file", false, "Disable loading config from file.") f.BoolVar(&v.DisableConfigEnv, "disable-config-env", false, "Disable loading config from environment variables.") v.LogLevel = NewFlagStringEnum([]string{"debug", "info", "warn", "error", "never"}, "never") diff --git a/cliext/go.mod b/cliext/go.mod index 19d62ff13..10fd66d8f 100644 --- a/cliext/go.mod +++ b/cliext/go.mod @@ -7,8 +7,8 @@ require ( github.com/mattn/go-isatty v0.0.23 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 - go.temporal.io/sdk v1.46.1-0.20260720184640-f34dc3da35ab - go.temporal.io/sdk/contrib/envconfig v1.0.2 + go.temporal.io/sdk v1.41.1 + go.temporal.io/sdk/contrib/envconfig v1.0.0 golang.org/x/oauth2 v0.36.0 google.golang.org/grpc v1.82.1 ) @@ -21,14 +21,13 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect - github.com/nexus-rpc/nexus-proto-annotations v0.1.0 // indirect github.com/nexus-rpc/sdk-go v0.6.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/robfig/cron v1.2.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/stretchr/objx v0.5.3 // indirect go.opentelemetry.io/otel v1.44.0 // indirect - go.temporal.io/api v1.63.5 // indirect + go.temporal.io/api v1.62.9 // indirect golang.org/x/net v0.57.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/cliext/go.sum b/cliext/go.sum index f62dadf7e..1e2b4d6e7 100644 --- a/cliext/go.sum +++ b/cliext/go.sum @@ -32,8 +32,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ= github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= -github.com/nexus-rpc/nexus-proto-annotations v0.1.0 h1:2fELd+9sqUtNu6Fg//pw8YFsxOvp8vZ8hfP0nHhNI80= -github.com/nexus-rpc/nexus-proto-annotations v0.1.0/go.mod h1:n3UjF1bPCW8llR8tHvbxJ+27yPWrhpo8w/Yg1IOuY0Y= github.com/nexus-rpc/sdk-go v0.6.0 h1:QRgnP2zTbxEbiyWG/aXH8uSC5LV/Mg1fqb19jb4DBlo= github.com/nexus-rpc/sdk-go v0.6.0/go.mod h1:FHdPfVQwRuJFZFTF0Y2GOAxCrbIBNrcPna9slkGKPYk= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -63,12 +61,12 @@ go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfC go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= -go.temporal.io/api v1.63.5 h1:c11+kPYHkXXL3UiShPdbMD+xtvqGsbTibUA9ypmiCa4= -go.temporal.io/api v1.63.5/go.mod h1:SrlW2JMwVlDP4nRWSNznUFqnSHd+YeMDS1BkYo63HCQ= -go.temporal.io/sdk v1.46.1-0.20260720184640-f34dc3da35ab h1:iF9QRS220GeFyNWdiKx+jvQl6LAvZdXGStRCWnU5pZw= -go.temporal.io/sdk v1.46.1-0.20260720184640-f34dc3da35ab/go.mod h1:x3v/9ImVh469kiHspoq1xgLdPnetbfuCAm+Y1+sUtIo= -go.temporal.io/sdk/contrib/envconfig v1.0.2 h1:MGHfsuPUtsf7X9M6WYn3zYJj/mWsuYHnA1uuiL0KEuE= -go.temporal.io/sdk/contrib/envconfig v1.0.2/go.mod h1:MuMiH7hksps2uXnmKuAWaP9P6WbkSDy62kl64t1VJVg= +go.temporal.io/api v1.62.9 h1:AUHbS+MPwHpF/TIf1UAv23xHzko4fzORlhfqzSHSPoM= +go.temporal.io/api v1.62.9/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= +go.temporal.io/sdk v1.41.1 h1:yOpvsHyDD1lNuwlGBv/SUodCPhjv9nDeC9lLHW/fJUA= +go.temporal.io/sdk v1.41.1/go.mod h1:/InXQT5guZ6AizYzpmzr5avQ/GMgq1ZObcKlKE2AhTc= +go.temporal.io/sdk/contrib/envconfig v1.0.0 h1:1Q/swVgB4EW/p3k7rI9/4hpU4/DC57FSRbU90+UisXw= +go.temporal.io/sdk/contrib/envconfig v1.0.0/go.mod h1:Pj4N1lwUEvxap6quBm8GrVMSUMJhSZkVtxjt3AYnPPg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/go.mod b/go.mod index 403b1a09f..2ba9223a9 100644 --- a/go.mod +++ b/go.mod @@ -9,24 +9,24 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 github.com/google/uuid v1.6.0 - github.com/mattn/go-isatty v0.0.20 + github.com/mattn/go-isatty v0.0.23 github.com/nexus-rpc/sdk-go v0.6.0 github.com/olekukonko/tablewriter v0.0.5 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.0 github.com/temporalio/cli/cliext v0.0.0 github.com/temporalio/ui-server/v2 v2.50.1 - go.temporal.io/api v1.62.8 + go.temporal.io/api v1.62.9 go.temporal.io/sdk v1.41.1 go.temporal.io/sdk/contrib/envconfig v1.0.0 go.temporal.io/server v1.31.2 golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 - golang.org/x/mod v0.35.0 - golang.org/x/term v0.43.0 - golang.org/x/tools v0.44.0 - google.golang.org/grpc v1.79.3 - google.golang.org/protobuf v1.36.10 + golang.org/x/mod v0.38.0 + golang.org/x/term v0.45.0 + golang.org/x/tools v0.48.0 + google.golang.org/grpc v1.82.1 + google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.44.3 ) @@ -46,7 +46,7 @@ require ( cloud.google.com/go/storage v1.56.0 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.1 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -81,13 +81,13 @@ require ( github.com/cactus/go-statsd-client/v5 v5.1.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect + github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect github.com/coreos/go-oidc/v3 v3.13.0 // indirect github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect @@ -110,8 +110,8 @@ require ( github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect @@ -124,8 +124,8 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/labstack/echo/v4 v4.13.4 // indirect - github.com/labstack/gommon v0.4.2 // indirect + github.com/labstack/echo/v4 v4.15.3 // indirect + github.com/labstack/gommon v0.5.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -141,7 +141,6 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.21.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.63.0 // indirect @@ -156,7 +155,7 @@ require ( github.com/sony/gobreaker v1.0.0 // indirect github.com/spf13/cast v1.7.0 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/objx v0.5.3 // indirect github.com/temporalio/ringpop-go v0.0.0-20250130211428-b97329e994f7 // indirect github.com/temporalio/sqlparser v0.0.0-20231115171017-f4060bcfa6cb // indirect github.com/temporalio/tchannel-go v1.22.1-0.20240528171429-1db37fdea938 // indirect @@ -168,19 +167,19 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.57.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/otel/sdk v1.43.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect - go.opentelemetry.io/proto/otlp v1.7.1 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.19.0 // indirect @@ -190,17 +189,17 @@ require ( go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.52.0 // indirect - golang.org/x/net v0.55.0 // indirect - golang.org/x/oauth2 v0.34.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.45.0 // indirect - golang.org/x/text v0.37.0 // indirect - golang.org/x/time v0.14.0 // indirect + golang.org/x/crypto v0.54.0 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect + golang.org/x/time v0.15.0 // indirect google.golang.org/api v0.256.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/validator.v2 v2.0.1 // indirect diff --git a/go.sum b/go.sum index 1d71eeae3..eeea0ed9b 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= @@ -118,8 +118,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/coreos/go-oidc/v3 v3.13.0 h1:M66zd0pcc5VxvBNM4pB331Wrsanby+QomQYjN8HamW8= github.com/coreos/go-oidc/v3 v3.13.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -140,12 +140,12 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= -github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= -github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= +github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= -github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= -github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= +github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -224,10 +224,10 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -271,10 +271,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA= -github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ= -github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= -github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/labstack/echo/v4 v4.15.3 h1:lIdG4kK5RdMyhwCwSc4AmSQsLBb3AVwok6S8PX/9kwQ= +github.com/labstack/echo/v4 v4.15.3/go.mod h1:Xzp1Ns1RA2c9fY7nSgUJkpkUZGNbEIVHZbtbOMPktBI= +github.com/labstack/gommon v0.5.0 h1:6VSQ2NOzsnEJ5W6+84E0RbcaDDmgB6NIAzWCczTEe6c= +github.com/labstack/gommon v0.5.0/go.mod h1:Rzlg7HHy1maLfzBYGg9NZcVuz1sA68HHhLjhcEllYE0= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= @@ -282,8 +282,8 @@ github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4 github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ= +github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -375,8 +375,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -385,8 +385,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI= +github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw= github.com/temporalio/ringpop-go v0.0.0-20250130211428-b97329e994f7 h1:lEebX/hZss+TSH3EBwhztnBavJVj7pWGJOH8UgKHS0w= github.com/temporalio/ringpop-go v0.0.0-20250130211428-b97329e994f7/go.mod h1:RE+CHmY+kOZQk47AQaVzwrGmxpflnLgTd6EOK0853j4= github.com/temporalio/sqlparser v0.0.0-20231115171017-f4060bcfa6cb h1:YzHH/U/dN7vMP+glybzcXRTczTrgfdRisNTzAj7La04= @@ -424,14 +424,14 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/collector/pdata v1.34.0 h1:2vwYftckXe7pWxI9mfSo+tw3wqdGNrYpMbDx/5q6rw8= go.opentelemetry.io/collector/pdata v1.34.0/go.mod h1:StPHMFkhLBellRWrULq0DNjv4znCDJZP6La4UuC+JHI= -go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= -go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= +go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU= +go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= @@ -442,18 +442,18 @@ go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25 go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= -go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= -go.temporal.io/api v1.62.8 h1:g8RAZmdebYODoNa2GLA4M4TsXNe1096WV3n26C4+fdw= -go.temporal.io/api v1.62.8/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= +go.temporal.io/api v1.62.9 h1:AUHbS+MPwHpF/TIf1UAv23xHzko4fzORlhfqzSHSPoM= +go.temporal.io/api v1.62.9/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2 h1:1hKeH3GyR6YD6LKMHGCZ76t6h1Sgha0hXVQBxWi3dlQ= go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2/go.mod h1:T8dnzVPeO+gaUTj9eDgm/lT2lZH4+JXNvrGaQGyVi50= go.temporal.io/sdk v1.41.1 h1:yOpvsHyDD1lNuwlGBv/SUodCPhjv9nDeC9lLHW/fJUA= @@ -492,8 +492,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -513,8 +513,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= -golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -526,18 +526,18 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -553,18 +553,17 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= -golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -572,10 +571,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -590,30 +589,30 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 h1:zUWMZsvo/IJcD1t6MNCPO/azZTwz0TvwCBqr5aifoVY= +google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529/go.mod h1:a5OGAgyRr4lqco7AG9hQM9Fwh0N2ZV4grR0eXFEsXQg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 h1:XF8+t6QQiS0o9ArVan/HW8Q7cycNPGsJf6GA2nXxYAg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE= +google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/temporalcli/commands.activity.go b/internal/temporalcli/commands.activity.go index 834303319..4a8a3a35a 100644 --- a/internal/temporalcli/commands.activity.go +++ b/internal/temporalcli/commands.activity.go @@ -924,7 +924,7 @@ func (c *TemporalActivityResetCommand) run(cctx *CommandContext, args []string) } else { // batch operation resetActivitiesOperation := &batch.BatchOperationResetActivities{ Identity: c.Parent.Identity, - ResetHeartbeat: true, + ResetHeartbeat: c.ResetHeartbeats, KeepPaused: c.KeepPaused, Jitter: durationpb.New(c.Jitter.Duration()), RestoreOriginalOptions: c.RestoreOriginalOptions, diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index b24ba0d76..2b8ec7275 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -759,6 +759,7 @@ type TemporalActivityResetCommand struct { SingleActivityOrBatchOptions ActivityId string KeepPaused bool + ResetHeartbeats bool Jitter cliext.FlagDuration RestoreOriginalOptions bool } @@ -770,13 +771,14 @@ func NewTemporalActivityResetCommand(cctx *CommandContext, parent *TemporalActiv s.Command.Use = "reset [flags]" s.Command.Short = "Reset an Activity" if hasHighlighting { - s.Command.Long = "Reset an activity.\nThis restarts the activity as if it were first being scheduled: the\nattempt count returns to one, its per-attempt timeouts are re-armed, and\nits heartbeat details are cleared.\n\nIf the activity may be executing (i.e. it has not yet timed out), the\nreset will take effect the next time it fails, heartbeats, or times out.\nIf is waiting for a retry (i.e. has failed or timed out), the reset\nwill apply immediately.\n\nIf the activity is already paused, it will be unpaused by default.\nYou can specify \x1b[1m--keep-paused\x1b[0m to prevent this.\n\nIf the activity is paused and the \x1b[1m--keep-paused\x1b[0m flag is not provided,\nit will be unpaused. If the activity is paused and the \x1b[1m--keep-paused\x1b[0m\nflag is provided, it will stay paused.\n\nEither \x1b[1m--activity-id\x1b[0m (with \x1b[1m--workflow-id\x1b[0m for a workflow Activity, or\nalone for a standalone Activity) or \x1b[1m--query\x1b[0m must be specified.\n\n### Resetting activities that heartbeat {#reset-heartbeats}\n\nActivities that heartbeat will receive a\nCanceled failure the next time\nthey heartbeat after a reset.\n\nIf, in your Activity, you need to do any cleanup when an Activity is\nreset, handle this error and then re-throw it when you've cleaned up.\n\nReset always clears the heartbeat details.\n\nSpecify the Activity and Workflow IDs:\n\n\x1b[1mtemporal activity reset \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId \\\n --keep-paused\x1b[0m\n\nActivities can be reset in bulk with a visibility query list filter:\n\n\x1b[1mtemporal activity reset \\\n --query 'WorkflowType=\"YourWorkflow\"'\x1b[0m\n\nOmit \x1b[1m--workflow-id\x1b[0m to target a Standalone Activity by Activity ID\nand optional Run ID." + s.Command.Long = "Reset an activity. Not supported for Standalone Activities.\nThis restarts the activity as if it were first being scheduled: the\nattempt count returns to one, its per-attempt timeouts are re-armed, and\nits heartbeat details are cleared.\n\nIf the activity may be executing (i.e. it has not yet timed out), the\nreset will take effect the next time it fails, heartbeats, or times out.\nIf is waiting for a retry (i.e. has failed or timed out), the reset\nwill apply immediately.\n\nIf the activity is already paused, it will be unpaused by default.\nYou can specify \x1b[1mkeep_paused\x1b[0m to prevent this.\n\nIf the activity is paused and the \x1b[1mkeep_paused\x1b[0m flag is not provided,\nit will be unpaused. If the activity is paused and \x1b[1mkeep_paused\x1b[0m flag\nis provided - it will stay paused.\n\nEither \x1b[1m--activity-id\x1b[0m (with \x1b[1m--workflow-id\x1b[0m) or \x1b[1m--query\x1b[0m must be specified.\n\n### Resetting activities that heartbeat {#reset-heartbeats}\n\nActivities that heartbeat will receive a Canceled failure\nthe next time they heartbeat after a reset.\n\nIf, in your Activity, you need to do any cleanup when an Activity is\nreset, handle this error and then re-throw it when you've cleaned up.\n\nIf the \x1b[1mreset_heartbeats\x1b[0m flag is set, the heartbeat details will also be cleared.\n\nSpecify the Activity and Workflow IDs:\n\n\x1b[1mtemporal activity reset \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\n --keep-paused\n --reset-heartbeats\x1b[0m\n\nActivities can be reset in bulk with a visibility query list filter:\n\n\x1b[1mtemporal activity reset \\\n --query 'WorkflowType=\"YourWorkflow\"'\x1b[0m" } else { - s.Command.Long = "Reset an activity.\nThis restarts the activity as if it were first being scheduled: the\nattempt count returns to one, its per-attempt timeouts are re-armed, and\nits heartbeat details are cleared.\n\nIf the activity may be executing (i.e. it has not yet timed out), the\nreset will take effect the next time it fails, heartbeats, or times out.\nIf is waiting for a retry (i.e. has failed or timed out), the reset\nwill apply immediately.\n\nIf the activity is already paused, it will be unpaused by default.\nYou can specify `--keep-paused` to prevent this.\n\nIf the activity is paused and the `--keep-paused` flag is not provided,\nit will be unpaused. If the activity is paused and the `--keep-paused`\nflag is provided, it will stay paused.\n\nEither `--activity-id` (with `--workflow-id` for a workflow Activity, or\nalone for a standalone Activity) or `--query` must be specified.\n\n### Resetting activities that heartbeat {#reset-heartbeats}\n\nActivities that heartbeat will receive a\nCanceled failure the next time\nthey heartbeat after a reset.\n\nIf, in your Activity, you need to do any cleanup when an Activity is\nreset, handle this error and then re-throw it when you've cleaned up.\n\nReset always clears the heartbeat details.\n\nSpecify the Activity and Workflow IDs:\n\n```\ntemporal activity reset \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId \\\n --keep-paused\n```\n\nActivities can be reset in bulk with a visibility query list filter:\n\n```\ntemporal activity reset \\\n --query 'WorkflowType=\"YourWorkflow\"'\n```\n\nOmit `--workflow-id` to target a Standalone Activity by Activity ID\nand optional Run ID." + s.Command.Long = "Reset an activity. Not supported for Standalone Activities.\nThis restarts the activity as if it were first being scheduled: the\nattempt count returns to one, its per-attempt timeouts are re-armed, and\nits heartbeat details are cleared.\n\nIf the activity may be executing (i.e. it has not yet timed out), the\nreset will take effect the next time it fails, heartbeats, or times out.\nIf is waiting for a retry (i.e. has failed or timed out), the reset\nwill apply immediately.\n\nIf the activity is already paused, it will be unpaused by default.\nYou can specify `keep_paused` to prevent this.\n\nIf the activity is paused and the `keep_paused` flag is not provided,\nit will be unpaused. If the activity is paused and `keep_paused` flag\nis provided - it will stay paused.\n\nEither `--activity-id` (with `--workflow-id`) or `--query` must be specified.\n\n### Resetting activities that heartbeat {#reset-heartbeats}\n\nActivities that heartbeat will receive a Canceled failure\nthe next time they heartbeat after a reset.\n\nIf, in your Activity, you need to do any cleanup when an Activity is\nreset, handle this error and then re-throw it when you've cleaned up.\n\nIf the `reset_heartbeats` flag is set, the heartbeat details will also be cleared.\n\nSpecify the Activity and Workflow IDs:\n\n```\ntemporal activity reset \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\n --keep-paused\n --reset-heartbeats\n```\n\nActivities can be reset in bulk with a visibility query list filter:\n\n```\ntemporal activity reset \\\n --query 'WorkflowType=\"YourWorkflow\"'\n```" } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVarP(&s.ActivityId, "activity-id", "a", "", "The Activity ID to reset. Mutually exclusive with `--query`. Requires `--workflow-id` to be specified.") s.Command.Flags().BoolVar(&s.KeepPaused, "keep-paused", false, "If the activity was paused, it will stay paused.") + s.Command.Flags().BoolVar(&s.ResetHeartbeats, "reset-heartbeats", false, "Reset the Activity's heartbeats.") s.Jitter = 0 s.Command.Flags().Var(&s.Jitter, "jitter", "The activity will reset at random a time within the specified duration. Can only be used with --query.") s.Command.Flags().BoolVar(&s.RestoreOriginalOptions, "restore-original-options", false, "Restore the original options of the activity.") @@ -1367,286 +1369,6 @@ func NewTemporalEnvSetCommand(cctx *CommandContext, parent *TemporalEnvCommand) return &s } -type TemporalNexusCommand struct { - Parent *TemporalCommand - Command cobra.Command - cliext.ClientOptions -} - -func NewTemporalNexusCommand(cctx *CommandContext, parent *TemporalCommand) *TemporalNexusCommand { - var s TemporalNexusCommand - s.Parent = parent - s.Command.Use = "nexus" - s.Command.Short = "Start, list, and operate on Nexus Operations" - if hasHighlighting { - s.Command.Long = "Nexus Operation commands perform operations on Nexus\nOperation Executions:\n\n\x1b[1mtemporal nexus [command] [options]\x1b[0m\n\nFor example:\n\n\x1b[1mtemporal nexus operation list\x1b[0m" - } else { - s.Command.Long = "Nexus Operation commands perform operations on Nexus\nOperation Executions:\n\n```\ntemporal nexus [command] [options]\n```\n\nFor example:\n\n```\ntemporal nexus operation list\n```" - } - s.Command.Args = cobra.NoArgs - s.Command.AddCommand(&NewTemporalNexusOperationCommand(cctx, &s).Command) - s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) - s.ClientOptions.HideFlags() - return &s -} - -type TemporalNexusOperationCommand struct { - Parent *TemporalNexusCommand - Command cobra.Command -} - -func NewTemporalNexusOperationCommand(cctx *CommandContext, parent *TemporalNexusCommand) *TemporalNexusOperationCommand { - var s TemporalNexusOperationCommand - s.Parent = parent - s.Command.Use = "operation" - s.Command.Short = "Commands for managing Nexus Operations" - if hasHighlighting { - s.Command.Long = "These commands manage Nexus Operation Executions.\n\nNexus Operation commands follow this syntax:\n\n\x1b[1mtemporal nexus operation [command] [options]\x1b[0m" - } else { - s.Command.Long = "These commands manage Nexus Operation Executions.\n\nNexus Operation commands follow this syntax:\n\n```\ntemporal nexus operation [command] [options]\n```" - } - s.Command.Args = cobra.NoArgs - s.Command.AddCommand(&NewTemporalNexusOperationCancelCommand(cctx, &s).Command) - s.Command.AddCommand(&NewTemporalNexusOperationCountCommand(cctx, &s).Command) - s.Command.AddCommand(&NewTemporalNexusOperationDescribeCommand(cctx, &s).Command) - s.Command.AddCommand(&NewTemporalNexusOperationExecuteCommand(cctx, &s).Command) - s.Command.AddCommand(&NewTemporalNexusOperationListCommand(cctx, &s).Command) - s.Command.AddCommand(&NewTemporalNexusOperationResultCommand(cctx, &s).Command) - s.Command.AddCommand(&NewTemporalNexusOperationStartCommand(cctx, &s).Command) - s.Command.AddCommand(&NewTemporalNexusOperationTerminateCommand(cctx, &s).Command) - return &s -} - -type TemporalNexusOperationCancelCommand struct { - Parent *TemporalNexusOperationCommand - Command cobra.Command - NexusOperationReferenceOptions - Reason string -} - -func NewTemporalNexusOperationCancelCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationCancelCommand { - var s TemporalNexusOperationCancelCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "cancel [flags]" - s.Command.Short = "Request cancellation of a Nexus Operation (Experimental)" - if hasHighlighting { - s.Command.Long = "Request cancellation of a Nexus Operation.\n\n\x1b[1mtemporal nexus operation cancel \\\n --operation-id YourOperationId\x1b[0m\n\nThe Operation handler determines how to handle the\ncancellation request." - } else { - s.Command.Long = "Request cancellation of a Nexus Operation.\n\n```\ntemporal nexus operation cancel \\\n --operation-id YourOperationId\n```\n\nThe Operation handler determines how to handle the\ncancellation request." - } - s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVar(&s.Reason, "reason", "", "Reason for cancellation.") - s.NexusOperationReferenceOptions.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 TemporalNexusOperationCountCommand struct { - Parent *TemporalNexusOperationCommand - Command cobra.Command - Query string -} - -func NewTemporalNexusOperationCountCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationCountCommand { - var s TemporalNexusOperationCountCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "count [flags]" - s.Command.Short = "Count Nexus Operations matching a query (Experimental)" - if hasHighlighting { - s.Command.Long = "Return a count of Nexus Operations. Use \x1b[1m--query\x1b[0m\nto filter the operations to be counted.\n\n\x1b[1mtemporal nexus operation count \\\n --query 'Endpoint=\"YourEndpoint\"'\x1b[0m\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." - } else { - s.Command.Long = "Return a count of Nexus Operations. Use `--query`\nto filter the operations to be counted.\n\n```\ntemporal nexus operation count \\\n --query 'Endpoint=\"YourEndpoint\"'\n```\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." - } - s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVarP(&s.Query, "query", "q", "", "Query to filter Nexus Operation Executions to count.") - s.Command.Run = func(c *cobra.Command, args []string) { - if err := s.run(cctx, args); err != nil { - cctx.Options.Fail(err) - } - } - return &s -} - -type TemporalNexusOperationDescribeCommand struct { - Parent *TemporalNexusOperationCommand - Command cobra.Command - NexusOperationReferenceOptions - Raw bool -} - -func NewTemporalNexusOperationDescribeCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationDescribeCommand { - var s TemporalNexusOperationDescribeCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "describe [flags]" - s.Command.Short = "Show detailed info for a Nexus Operation (Experimental)" - if hasHighlighting { - s.Command.Long = "Display detailed information about a specific Nexus\nOperation Execution.\n\n\x1b[1mtemporal nexus operation describe \\\n --operation-id YourOperationId\x1b[0m" - } else { - s.Command.Long = "Display detailed information about a specific Nexus\nOperation Execution.\n\n```\ntemporal nexus operation describe \\\n --operation-id YourOperationId\n```" - } - s.Command.Args = cobra.NoArgs - s.Command.Flags().BoolVar(&s.Raw, "raw", false, "Print properties without changing their format.") - s.NexusOperationReferenceOptions.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 TemporalNexusOperationExecuteCommand struct { - Parent *TemporalNexusOperationCommand - Command cobra.Command - NexusOperationStartOptions - PayloadInputOptions -} - -func NewTemporalNexusOperationExecuteCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationExecuteCommand { - var s TemporalNexusOperationExecuteCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "execute [flags]" - s.Command.Short = "Start a new Nexus Operation and wait for its result (Experimental)" - if hasHighlighting { - s.Command.Long = "Start a new Nexus Operation Execution and block until\nit completes. The result is output to stdout.\n\n\x1b[1mtemporal nexus operation execute \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\x1b[0m" - } else { - s.Command.Long = "Start a new Nexus Operation Execution and block until\nit completes. The result is output to stdout.\n\n```\ntemporal nexus operation execute \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\n```" - } - s.Command.Args = cobra.NoArgs - s.NexusOperationStartOptions.BuildFlags(s.Command.Flags()) - s.PayloadInputOptions.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 TemporalNexusOperationListCommand struct { - Parent *TemporalNexusOperationCommand - Command cobra.Command - Query string - Limit int - PageSize int -} - -func NewTemporalNexusOperationListCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationListCommand { - var s TemporalNexusOperationListCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "list [flags]" - s.Command.Short = "List Nexus Operations matching a query (Experimental)" - if hasHighlighting { - s.Command.Long = "List Nexus Operations. Use \x1b[1m--query\x1b[0m to filter results.\n\n\x1b[1mtemporal nexus operation list \\\n --query 'Endpoint=\"YourEndpoint\"'\x1b[0m\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." - } else { - s.Command.Long = "List Nexus Operations. Use `--query` to filter results.\n\n```\ntemporal nexus operation list \\\n --query 'Endpoint=\"YourEndpoint\"'\n```\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." - } - s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVarP(&s.Query, "query", "q", "", "Query to filter the Nexus Operation Executions to list.") - s.Command.Flags().IntVar(&s.Limit, "limit", 0, "Maximum number of Nexus Operation Executions to display.") - s.Command.Flags().IntVar(&s.PageSize, "page-size", 0, "Maximum number of Nexus Operation Executions to fetch at a time from the server.") - s.Command.Run = func(c *cobra.Command, args []string) { - if err := s.run(cctx, args); err != nil { - cctx.Options.Fail(err) - } - } - return &s -} - -type TemporalNexusOperationResultCommand struct { - Parent *TemporalNexusOperationCommand - Command cobra.Command - NexusOperationReferenceOptions -} - -func NewTemporalNexusOperationResultCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationResultCommand { - var s TemporalNexusOperationResultCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "result [flags]" - s.Command.Short = "Wait for and output the result of a Nexus Operation (Experimental)" - if hasHighlighting { - s.Command.Long = "Wait for a Nexus Operation to complete and output\nthe result.\n\n\x1b[1mtemporal nexus operation result \\\n --operation-id YourOperationId\x1b[0m" - } else { - s.Command.Long = "Wait for a Nexus Operation to complete and output\nthe result.\n\n```\ntemporal nexus operation result \\\n --operation-id YourOperationId\n```" - } - s.Command.Args = cobra.NoArgs - s.NexusOperationReferenceOptions.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 TemporalNexusOperationStartCommand struct { - Parent *TemporalNexusOperationCommand - Command cobra.Command - NexusOperationStartOptions - PayloadInputOptions -} - -func NewTemporalNexusOperationStartCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationStartCommand { - var s TemporalNexusOperationStartCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "start [flags]" - s.Command.Short = "Start a new Nexus Operation (Experimental)" - if hasHighlighting { - s.Command.Long = "Start a new Nexus Operation. Outputs the\nOperation ID and Run ID.\n\n\x1b[1mtemporal nexus operation start \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\x1b[0m" - } else { - s.Command.Long = "Start a new Nexus Operation. Outputs the\nOperation ID and Run ID.\n\n```\ntemporal nexus operation start \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\n```" - } - s.Command.Args = cobra.NoArgs - s.NexusOperationStartOptions.BuildFlags(s.Command.Flags()) - s.PayloadInputOptions.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 TemporalNexusOperationTerminateCommand struct { - Parent *TemporalNexusOperationCommand - Command cobra.Command - NexusOperationReferenceOptions - Reason string -} - -func NewTemporalNexusOperationTerminateCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationTerminateCommand { - var s TemporalNexusOperationTerminateCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "terminate [flags]" - s.Command.Short = "Forcefully end a Nexus Operation (Experimental)" - if hasHighlighting { - s.Command.Long = "Terminate a Nexus Operation.\n\n\x1b[1mtemporal nexus operation terminate \\\n --operation-id YourOperationId \\\n --reason YourReason\x1b[0m\n\nOperation handlers cannot see or respond to terminations." - } else { - s.Command.Long = "Terminate a Nexus Operation.\n\n```\ntemporal nexus operation terminate \\\n --operation-id YourOperationId \\\n --reason YourReason\n```\n\nOperation handlers cannot see or respond to terminations." - } - s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVar(&s.Reason, "reason", "", "Reason for termination. Defaults to a message with the current user's name.") - s.NexusOperationReferenceOptions.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 TemporalOperatorCommand struct { Parent *TemporalCommand Command cobra.Command diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index c71c7578f..a22982a84 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -617,6 +617,9 @@ commands: - name: keep-paused type: bool description: If the activity was paused, it will stay paused. + - name: reset-heartbeats + type: bool + description: Reset the Activity's heartbeats. - name: jitter type: duration description: | From 917dee10992f0b1e8162e66a828c0516b0e3546e Mon Sep 17 00:00:00 2001 From: dryrun Date: Mon, 31 Aug 2026 13:06:26 -0700 Subject: [PATCH 12/15] fix(activity): use correct update-mask path for --task-queue v1.8.2 sends "task_queue_name", which ParseFieldMask normalizes to "taskQueueName" and never matches the server's "taskQueue.name" key, so 'activity update-options --task-queue' silently no-ops. Cherry-picked as a one-liner from #1092 (whose full SAA change requires api v1.63.x). Verified against embedded server v1.31.2. --- internal/temporalcli/commands.activity.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/temporalcli/commands.activity.go b/internal/temporalcli/commands.activity.go index 4a8a3a35a..19d19d169 100644 --- a/internal/temporalcli/commands.activity.go +++ b/internal/temporalcli/commands.activity.go @@ -643,7 +643,7 @@ func (c *TemporalActivityUpdateOptionsCommand) run(cctx *CommandContext, args [] if c.Command.Flags().Changed("task-queue") { activityOptions.TaskQueue = &taskqueuepb.TaskQueue{Name: c.TaskQueue} - updatePath = append(updatePath, "task_queue_name") + updatePath = append(updatePath, "task_queue.name") } if c.Command.Flags().Changed("schedule-to-close-timeout") { From aedacb5fb73e39ad4c909b74e51b61c549c1d766 Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Tue, 1 Sep 2026 18:44:39 +0000 Subject: [PATCH 13/15] test: stabilize activity list pagination (#1171) ## Related issues N/A ## What changed? Make `TestActivity_List_Pagination` exercise a deterministic mix of three completed and two running standalone Activities. The test now waits for completed results, blocks running Activities until pagination finishes, and waits for visibility to report the exact status mix before listing. It also verifies each Activity appears exactly once with the expected status, avoiding the race where an Activity could complete and move in visibility ordering between page requests. ## Checklist **Stability** - [x] No user-facing behavior or output changes **Tests** - [x] Updated functional test (`SharedServerSuite`) - [x] Focused pagination test passed 20 consecutive runs - [x] Neighboring Activity list/count tests passed ## Testing ```sh go test ./internal/temporalcli -run 'TestSharedServerSuite/TestActivity_List_Pagination$' -count=20 go test ./internal/temporalcli -run 'TestSharedServerSuite/TestActivity_(List(_Pagination)?|Count)$' -count=1 git diff --check ``` (cherry picked from commit afcf89cce5b75baaf6b7b0ce80236b14dac7afbd) --- .../temporalcli/commands.activity_test.go | 106 ++++++++++++++++-- 1 file changed, 96 insertions(+), 10 deletions(-) diff --git a/internal/temporalcli/commands.activity_test.go b/internal/temporalcli/commands.activity_test.go index ec6b50a4f..21e693908 100644 --- a/internal/temporalcli/commands.activity_test.go +++ b/internal/temporalcli/commands.activity_test.go @@ -14,6 +14,7 @@ import ( "go.temporal.io/api/history/v1" "go.temporal.io/api/serviceerror" "go.temporal.io/api/workflowservice/v1" + "go.temporal.io/sdk/activity" "go.temporal.io/sdk/client" "go.temporal.io/sdk/converter" "go.temporal.io/sdk/temporal" @@ -1293,36 +1294,116 @@ func (s *SharedServerSuite) TestActivity_SearchAttributes_InvalidKeywordList() { } func (s *SharedServerSuite) TestActivity_List_Pagination() { + runningActivityStarted := make(chan struct{}, 2) + releaseRunningActivities := make(chan struct{}) + var releaseRunningActivitiesOnce sync.Once + releaseRunning := func() { + releaseRunningActivitiesOnce.Do(func() { close(releaseRunningActivities) }) + } + defer releaseRunning() + s.Worker().OnDevActivity(func(ctx context.Context, a any) (any, error) { + if strings.Contains(activity.GetInfo(ctx).ActivityID, "-running-") { + runningActivityStarted <- struct{}{} + select { + case <-releaseRunningActivities: + case <-ctx.Done(): + return nil, ctx.Err() + } + } return "paginated", nil }) uniqueKW := "page-" + uuid.NewString()[:8] - for i := 0; i < 5; i++ { - s.startActivity(fmt.Sprintf("page-test-%d", i), + query := fmt.Sprintf(`CustomKeywordField = "%s"`, uniqueKW) + completedActivityIDs := []string{ + "page-test-completed-0", + "page-test-completed-1", + "page-test-completed-2", + } + for _, activityID := range completedActivityIDs { + started := s.startActivity(activityID, "--search-attribute", fmt.Sprintf(`CustomKeywordField="%s"`, uniqueKW), ) + handle := s.Client.GetActivityHandle(client.GetActivityHandleOptions{ + ActivityID: activityID, + RunID: started["runId"].(string), + }) + s.NoError(handle.Get(s.Context, nil)) } - // Wait for all 5 to be visible + runningActivityIDs := []string{ + "page-test-running-0", + "page-test-running-1", + } + runningActivityHandles := make([]client.ActivityHandle, 0, len(runningActivityIDs)) + for _, activityID := range runningActivityIDs { + started := s.startActivity(activityID, + "--search-attribute", fmt.Sprintf(`CustomKeywordField="%s"`, uniqueKW), + ) + runningActivityHandles = append(runningActivityHandles, s.Client.GetActivityHandle(client.GetActivityHandleOptions{ + ActivityID: activityID, + RunID: started["runId"].(string), + })) + } + runningActivityStartTimeout := time.After(5 * time.Second) + for range runningActivityIDs { + select { + case <-runningActivityStarted: + case <-runningActivityStartTimeout: + s.Fail("running activities did not start within timeout") + return + } + } + + // Wait for visibility to contain the stable mix that pagination will traverse. + visibilityReady := false s.Eventually(func() bool { res := s.Execute( "activity", "list", + "-o", "json", "--address", s.Address(), - "--query", fmt.Sprintf(`CustomKeywordField = "%s"`, uniqueKW), + "--query", query, ) - return res.Err == nil && strings.Count(res.Stdout.String(), "page-test-") >= 5 - }, 5*time.Second, 200*time.Millisecond) + if res.Err != nil { + return false + } + var activities []struct { + Status string `json:"status"` + } + if err := json.Unmarshal(res.Stdout.Bytes(), &activities); err != nil { + return false + } + statusCounts := make(map[string]int) + for _, activity := range activities { + statusCounts[activity.Status]++ + } + visibilityReady = len(activities) == 5 && + statusCounts["ACTIVITY_EXECUTION_STATUS_COMPLETED"] == len(completedActivityIDs) && + statusCounts["ACTIVITY_EXECUTION_STATUS_RUNNING"] == len(runningActivityIDs) + return visibilityReady + }, 10*time.Second, 200*time.Millisecond) + if !visibilityReady { + return + } - // Small page size forces multi-page fetching; verify all 5 appear + // Small page size forces multi-page fetching across both running and completed activities. res := s.Execute( "activity", "list", "--page-size", "2", "--address", s.Address(), - "--query", fmt.Sprintf(`CustomKeywordField = "%s"`, uniqueKW), + "--query", query, ) s.NoError(res.Err) - s.Equal(5, strings.Count(res.Stdout.String(), "page-test-")) + out := res.Stdout.String() + for _, activityID := range completedActivityIDs { + s.ContainsOnSameLine(out, "Completed", activityID) + s.Equal(1, strings.Count(out, activityID)) + } + for _, activityID := range runningActivityIDs { + s.ContainsOnSameLine(out, "Running", activityID) + s.Equal(1, strings.Count(out, activityID)) + } // --limit 3 with page-size 2 should return exactly 3 res = s.Execute( @@ -1330,10 +1411,15 @@ func (s *SharedServerSuite) TestActivity_List_Pagination() { "--page-size", "2", "--limit", "3", "--address", s.Address(), - "--query", fmt.Sprintf(`CustomKeywordField = "%s"`, uniqueKW), + "--query", query, ) s.NoError(res.Err) s.Equal(3, strings.Count(res.Stdout.String(), "page-test-")) + + releaseRunning() + for _, handle := range runningActivityHandles { + s.NoError(handle.Get(s.Context, nil)) + } } func (s *SharedServerSuite) TestActivity_Terminate_DefaultReason_NoUnknownUser() { From 127045fb87d72d2f6e2e341cd91cd24db7511ee6 Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Tue, 1 Sep 2026 20:37:00 +0000 Subject: [PATCH 14/15] fix: document start-dev log-level default (#1186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Related issues N/A ## What changed? Restores the `server start-dev` log-level default exception in `temporal options`, which is now the canonical help surface for global flags. Also corrects the adjacent stale implementation comment and adds regression coverage. This preserves the contract from #956: it changed the common default from `info` to `never` for short-lived CLI commands, while deliberately retaining the long-running `server start-dev` default of `warn`. That exception was present in #956’s option metadata but was removed by the subsequent help decluttering change. ## Checklist - [x] Added applicable unit-test coverage - [x] Help text describes the actual command behavior ## Manual tests ``` temporal options # --log-level describes the `never` default and the `server start-dev` `warn` exception. ``` ## Validation ``` env GOCACHE=/private/tmp/fix-cli-log-desc-gocache make test ``` (cherry picked from commit 1a7dabb7f56ca526802146522af43a2b564b7b48) --- cliext/flags.gen.go | 2 +- cliext/option-sets.yaml | 4 +++- internal/temporalcli/commands.help_test.go | 9 +++++++++ internal/temporalcli/commands.server.go | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cliext/flags.gen.go b/cliext/flags.gen.go index 22c6f9268..922e34eb6 100644 --- a/cliext/flags.gen.go +++ b/cliext/flags.gen.go @@ -43,7 +43,7 @@ func (v *CommonOptions) BuildFlags(f *pflag.FlagSet) { f.BoolVar(&v.DisableConfigFile, "disable-config-file", false, "Disable loading config from file.") f.BoolVar(&v.DisableConfigEnv, "disable-config-env", false, "Disable loading config from environment variables.") v.LogLevel = NewFlagStringEnum([]string{"debug", "info", "warn", "error", "never"}, "never") - f.Var(&v.LogLevel, "log-level", "Log level. Accepted values: debug, info, warn, error, never.") + f.Var(&v.LogLevel, "log-level", "Log level. Default is \"never\" for most commands and \"warn\" for \"server start-dev\". Accepted values: debug, info, warn, error, never.") v.LogFormat = NewFlagStringEnum([]string{"text", "json", "pretty"}, "text") f.Var(&v.LogFormat, "log-format", "Log format. Accepted values: text, json.") v.Output = NewFlagStringEnum([]string{"text", "json", "jsonl", "none"}, "text") diff --git a/cliext/option-sets.yaml b/cliext/option-sets.yaml index 65adfcfc1..eced3e905 100644 --- a/cliext/option-sets.yaml +++ b/cliext/option-sets.yaml @@ -41,7 +41,9 @@ option-sets: - warn - error - never - description: Log level. + description: | + Log level. + Default is "never" for most commands and "warn" for "server start-dev". default: never - name: log-format type: string-enum diff --git a/internal/temporalcli/commands.help_test.go b/internal/temporalcli/commands.help_test.go index fe8c88acd..54f04e9d9 100644 --- a/internal/temporalcli/commands.help_test.go +++ b/internal/temporalcli/commands.help_test.go @@ -31,6 +31,15 @@ func TestHelp_Subcommand(t *testing.T) { assert.NoError(t, res.Err) } +func TestOptions_LogLevelDescribesStartDevDefault(t *testing.T) { + h := NewCommandHarness(t) + + res := h.Execute("options") + + assert.Contains(t, res.Stdout.String(), `Default is "never" for most commands and "warn" for "server start-dev".`) + assert.NoError(t, res.Err) +} + func TestHelp_WithValueFlag(t *testing.T) { h := NewCommandHarness(t) diff --git a/internal/temporalcli/commands.server.go b/internal/temporalcli/commands.server.go index d0aa7d97a..ad335d6cf 100644 --- a/internal/temporalcli/commands.server.go +++ b/internal/temporalcli/commands.server.go @@ -53,7 +53,7 @@ func (t *TemporalServerStartDevCommand) run(cctx *CommandContext, args []string) // Set the log level value of the server to the overall log level given to the // CLI. But if it is "never" we have to do a special value, and if it was // never changed, we have to use the default of "warn" instead of the CLI - // default of "info" since server is noisier. + // default of "never" since server is noisier. logLevel := t.Parent.Parent.LogLevel.Value if !t.Parent.Parent.LogLevel.ChangedFromDefault { logLevel = "warn" From 600b54cc7e4267f5521a0c4795d654c68dbba73d Mon Sep 17 00:00:00 2001 From: justinschoeff Date: Tue, 1 Sep 2026 21:14:40 +0000 Subject: [PATCH 15/15] Support AWS AgentCore Compute Provider (#1177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Related issues n/a ## What changed? This adds support for new AWS compute provider: AWS AgentCore. AgentCore configuration is almost identitical to AWS Lambda, replacing function ARN with Runtime Endpoint ARN. ## Checklist **Stability** - [x] Breaking changes are marked with πŸ’₯ in the PR title and release notes - [x] 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 - > Both lambda and agentcore utilize similar assume-role-external-id, assume-role-arn and the corresponding skip flag. However, we are scoping them to the compute provider in an effort to support multiple compute providers in the future, as WCI currently supports that, but we haven't exposed it yet. - [βœ…] 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 ``` eval `assume-sso team-compute-sandbox/AWSAdministratorAccess` [i] If the browser does not open automatically, please open this link: https://temporal.awsapps.com/start/#/device?user_code=LLXQ-NPJW [i] Awaiting AWS authentication in the browser [i] You will be prompted to authenticate with AWS in the browser, then you will be prompted to 'Allow' [i] Code: LLXQ-NPJW [βœ”] Successfully logged into Start URL: https://temporal.awsapps.com/start go build -o temporal ./cmd/temporal ./temporal server start-dev \ --dynamic-config-value workercontroller.enabled=true Temporal CLI 0.0.0-DEV (Server 1.32.0-162.0, UI 2.53.1) ``` ### Happy Path ``` ./temporal worker deployment create --name temporal-worker-agentcore Successfully created worker deployment ./temporal worker deployment create-version \ --aws-agentcore-endpoint-arn arn:aws:bedrock-agentcore:us-east-1:093235337669:runtime/justinschoeff_temporal_worker_agentcore-DvGerTB5L8/runtime-endpoint/V5 \ --aws-agentcore-assume-role-external-id schoeffExternalId \ --aws-agentcore-role arn:aws:iam::093235337669:role/justinschoeff-temporal-worker-invoke \ --build-id v5 \ --deployment-name temporal-worker-agentcore Successfully created worker deployment version ./temporal worker deployment set-current-version --deployment-name temporal-worker-agentcore --build-id v5 Worker Deployment Before Update: Name temporal-worker-agentcore CreateTime 12 minutes ago CurrentVersionDeploymentName temporal-worker-agentcore CurrentVersionBuildID v5 CurrentVersionChangedTime 11 minutes ago Version Summaries: DeploymentName BuildID DrainageStatus CreateTime temporal-worker-agentcore v5 unspecified 12 minutes ago Continue with set Current? y/N y Successfully set the current worker deployment version ./temporal workflow start \ --type sampleWorkflow \ --task-queue server-scaled-workers \ --input '"What can you do?"' Running execution: WorkflowId 7f7e99b7-d645-42a3-8ca2-d713d1caafb1 RunId 01a035d6-4cb9-73e0-b242-397ba3259475 Type sampleWorkflow Namespace default TaskQueue server-scaled-workers ./temporal workflow describe --workflow-id 7f7e99b7-d645-42a3-8ca2-d713d1caafb1 Execution Info: WorkflowId 7f7e99b7-d645-42a3-8ca2-d713d1caafb1 RunId 01a035d6-4cb9-73e0-b242-397ba3259475 Type sampleWorkflow Namespace default TaskQueue server-scaled-workers ... Versioning Info: Behavior Pinned DeploymentName temporal-worker-agentcore BuildId v5 Results: RunTime 4.27s Status COMPLETED Result "go: Hello What can you do?" ResultEncoding json/plain ``` **Error case** ``` ./temporal worker deployment create-version \ --aws-agentcore-endpoint-arn arn:aws:bedrock-agentcore:us-east-1:093235337669:runtime/justinschoeff_temporal_worker_agentcore-DvGerTB5L8/runtime-endpoint/V5 \ --aws-agentcore-assume-role-external-id schoeffExternalId \ Error: required flag(s) "build-id", "deployment-name" not set Usage: ... Error: required flag(s) "build-id", "deployment-name" not set ``` ``` ./temporal worker deployment create-version \ --aws-agentcore-assume-role-external-id schoeffExternalId \ --aws-agentcore-assume-role-arn arn:aws:iam::093235337669:role/justinschoeff-temporal-worker-invoke \ --build-id v5 \ --deployment-name temporal-worker-agentcore Error: missing configuration for compute provider ``` ``` ./temporal worker deployment create-version \ --aws-agentcore-endpoint-arn arn:aws:iam::093235337669:role/justinschoeff-temporal-worker-invoke \ --aws-agentcore-assume-role-arn arn:aws:iam::093235337669:role/justinschoeff-temporal-worker-invoke \ --build-id v5 \ --deployment-name temporal-worker-agentcore Error: missing required AWS Agentcore provider detail: role_external_id ``` ``` ./temporal worker deployment create-version \ ~/workplace/local-dev/cli --aws-agentcore-endpoint-arn arn:aws:iam::093235337669:role/justinschoeff-temporal-worker-invoke \ --aws-agentcore-assume-role-external-id arn:aws:iam::093235337669:role/justinschoeff-temporal-worker-invoke \ --build-id v5 \ --deployment-name temporal-worker-agentcore Error: missing required AWS Agentcore provider detail: role ``` (cherry picked from commit cbe374d8297d017fa1ab4a5fe3a0d759d14dfcf1) --- internal/temporalcli/commands.gen.go | 24 ++- .../temporalcli/commands.worker.deployment.go | 202 +++++++++++++----- ...ommands.worker.deployment.internal_test.go | 3 +- .../commands.worker.deployment_test.go | 192 ++++++++++++++++- internal/temporalcli/commands.yaml | 80 +++++++ 5 files changed, 443 insertions(+), 58 deletions(-) diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 2b8ec7275..669914ddb 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -3312,6 +3312,10 @@ type TemporalWorkerDeploymentCreateVersionCommand struct { AwsLambdaAssumeRoleArn string AwsLambdaAssumeRoleExternalId string AwsLambdaSkipRoleAndExternalId bool + AwsAgentcoreEndpointArn string + AwsAgentcoreAssumeRoleArn string + AwsAgentcoreAssumeRoleExternalId string + AwsAgentcoreSkipRoleAndExternalId bool GcpCloudRunProject string GcpCloudRunRegion string GcpCloudRunWorkerPool string @@ -3330,15 +3334,19 @@ func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, paren s.Command.Use = "create-version [flags]" s.Command.Short = "Create a new Worker Deployment Version" if hasHighlighting { - s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n\x1b[1mtemporal worker deployment create-version [options]\x1b[0m\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\x1b[0m\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n\x1b[1mtemporal worker deployment create-version [options]\x1b[0m\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\x1b[0m\n\nOr pass compute provider information for an AWS Bedrock Agentcore Runtime\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-agentcore-endpoint-arn AgentcoreRuntimeEndpointARN \\\n --aws-agentcore-assume-role-arn AgentcoreAssumeRoleARN \\\n --aws-agentcore-assume-role-external-id AgentcoreAssumeRoleExternalID\x1b[0m\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." } else { - s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n```\ntemporal worker deployment create-version [options]\n```\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\n```\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n```\ntemporal worker deployment create-version [options]\n```\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\n```\n\nOr pass compute provider information for an AWS Bedrock Agentcore Runtime\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-agentcore-endpoint-arn AgentcoreRuntimeEndpointARN \\\n --aws-agentcore-assume-role-arn AgentcoreAssumeRoleARN \\\n --aws-agentcore-assume-role-external-id AgentcoreAssumeRoleExternalID\n```\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleArn, "aws-lambda-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is specified, and must be omitted when --aws-lambda-skip-role-and-external-id is passed.") s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleExternalId, "aws-lambda-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-lambda-function-arn is specified, and must be omitted when --aws-lambda-skip-role-and-external-id is passed.") s.Command.Flags().BoolVar(&s.AwsLambdaSkipRoleAndExternalId, "aws-lambda-skip-role-and-external-id", false, "When --aws-lambda-function-arn is specified, --aws-lambda-assume-role-arn and --aws-lambda-assume-role-external-id are required unless this flag is passed, in which case both must be omitted.") + s.Command.Flags().StringVar(&s.AwsAgentcoreEndpointArn, "aws-agentcore-endpoint-arn", "", "AWS Bedrock Agentcore Runtime endpoint ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment. The endpoint ARN encodes the runtime, endpoint name, and region.") + s.Command.Flags().StringVar(&s.AwsAgentcoreAssumeRoleArn, "aws-agentcore-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Agentcore Runtime that spawns a new Worker in this Worker Deployment Version. Required when --aws-agentcore-endpoint-arn is specified, and must be omitted when --aws-agentcore-skip-role-and-external-id is passed.") + s.Command.Flags().StringVar(&s.AwsAgentcoreAssumeRoleExternalId, "aws-agentcore-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-agentcore-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-agentcore-endpoint-arn is specified, and must be omitted when --aws-agentcore-skip-role-and-external-id is passed.") + s.Command.Flags().BoolVar(&s.AwsAgentcoreSkipRoleAndExternalId, "aws-agentcore-skip-role-and-external-id", false, "When --aws-agentcore-endpoint-arn is specified, --aws-agentcore-assume-role-arn and --aws-agentcore-assume-role-external-id are required unless this flag is passed, in which case both must be omitted.") s.Command.Flags().StringVar(&s.GcpCloudRunProject, "gcp-cloud-run-project", "", "GCP project ID hosting the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunRegion, "gcp-cloud-run-region", "", "Region of the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunWorkerPool, "gcp-cloud-run-worker-pool", "", "GCP Cloud Run worker pool name to scale when there are no active pollers for task queue targets in the Worker Deployment.") @@ -3656,6 +3664,10 @@ type TemporalWorkerDeploymentUpdateVersionComputeConfigCommand struct { AwsLambdaAssumeRoleArn string AwsLambdaAssumeRoleExternalId string AwsLambdaSkipRoleAndExternalId bool + AwsAgentcoreEndpointArn string + AwsAgentcoreAssumeRoleArn string + AwsAgentcoreAssumeRoleExternalId string + AwsAgentcoreSkipRoleAndExternalId bool GcpCloudRunProject string GcpCloudRunRegion string GcpCloudRunWorkerPool string @@ -3675,15 +3687,19 @@ func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandC s.Command.Use = "update-version-compute-config [flags]" s.Command.Short = "Update compute configuration for a Version" if hasHighlighting { - s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\x1b[0m\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\x1b[0m\n\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nTo update only the scaling settings on an existing GCP Cloud Run Worker\nDeployment Version, supply the five scaler flags without the provider\nfields (all five must be set together):\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nProvider fields are only required when changing the compute provider.\nSwitching the provider resets the scaling settings for the new provider.\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\x1b[0m\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\x1b[0m\n\nTo update the AWS Bedrock Agentcore Runtime endpoint associated with an\nexisting Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-agentcore-endpoint-arn UpdatedAgentcoreRuntimeEndpointARN \\\n --aws-agentcore-assume-role-arn UpdatedRoleARN \\\n --aws-agentcore-assume-role-external-id UpdatedExternalID\x1b[0m\n\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nTo update only the scaling settings on an existing GCP Cloud Run Worker\nDeployment Version, supply the five scaler flags without the provider\nfields (all five must be set together):\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\x1b[0m\n\nProvider fields are only required when changing the compute provider.\nSwitching the provider resets the scaling settings for the new provider.\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." } else { - s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\n```\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\n```\n\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nTo update only the scaling settings on an existing GCP Cloud Run Worker\nDeployment Version, supply the five scaler flags without the provider\nfields (all five must be set together):\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nProvider fields are only required when changing the compute provider.\nSwitching the provider resets the scaling settings for the new provider.\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\n```\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\n```\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\n```\n\nTo update the AWS Bedrock Agentcore Runtime endpoint associated with an\nexisting Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-agentcore-endpoint-arn UpdatedAgentcoreRuntimeEndpointARN \\\n --aws-agentcore-assume-role-arn UpdatedRoleARN \\\n --aws-agentcore-assume-role-external-id UpdatedExternalID\n```\n\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nTo update only the scaling settings on an existing GCP Cloud Run Worker\nDeployment Version, supply the five scaler flags without the provider\nfields (all five must be set together):\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75 \\\n --gcp-cloud-run-scale-down-stabilization-duration 5m\n```\n\nProvider fields are only required when changing the compute provider.\nSwitching the provider resets the scaling settings for the new provider.\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\n```\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleArn, "aws-lambda-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is specified, and must be omitted when --aws-lambda-skip-role-and-external-id is passed.") s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleExternalId, "aws-lambda-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-lambda-function-arn is specified, and must be omitted when --aws-lambda-skip-role-and-external-id is passed.") s.Command.Flags().BoolVar(&s.AwsLambdaSkipRoleAndExternalId, "aws-lambda-skip-role-and-external-id", false, "When --aws-lambda-function-arn is specified, --aws-lambda-assume-role-arn and --aws-lambda-assume-role-external-id are required unless this flag is passed, in which case both must be omitted.") + s.Command.Flags().StringVar(&s.AwsAgentcoreEndpointArn, "aws-agentcore-endpoint-arn", "", "AWS Bedrock Agentcore Runtime endpoint ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment. The endpoint ARN encodes the runtime, endpoint name, and region.") + s.Command.Flags().StringVar(&s.AwsAgentcoreAssumeRoleArn, "aws-agentcore-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Agentcore Runtime that spawns a new Worker in this Worker Deployment Version. Required when --aws-agentcore-endpoint-arn is specified, and must be omitted when --aws-agentcore-skip-role-and-external-id is passed.") + s.Command.Flags().StringVar(&s.AwsAgentcoreAssumeRoleExternalId, "aws-agentcore-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-agentcore-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-agentcore-endpoint-arn is specified, and must be omitted when --aws-agentcore-skip-role-and-external-id is passed.") + s.Command.Flags().BoolVar(&s.AwsAgentcoreSkipRoleAndExternalId, "aws-agentcore-skip-role-and-external-id", false, "When --aws-agentcore-endpoint-arn is specified, --aws-agentcore-assume-role-arn and --aws-agentcore-assume-role-external-id are required unless this flag is passed, in which case both must be omitted.") s.Command.Flags().StringVar(&s.GcpCloudRunProject, "gcp-cloud-run-project", "", "GCP project ID hosting the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunRegion, "gcp-cloud-run-region", "", "Region of the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunWorkerPool, "gcp-cloud-run-worker-pool", "", "GCP Cloud Run worker pool name to scale when there are no active pollers for task queue targets in the Worker Deployment.") diff --git a/internal/temporalcli/commands.worker.deployment.go b/internal/temporalcli/commands.worker.deployment.go index 6bc5f07f7..6dece0b35 100644 --- a/internal/temporalcli/commands.worker.deployment.go +++ b/internal/temporalcli/commands.worker.deployment.go @@ -1035,8 +1035,9 @@ func awsLambdaProviderDetailsPayload( ) (*commonpb.Payload, error) { // Map keys from temporal-auto-scaled-workers: // https://github.com/temporalio/temporal-auto-scaled-workers/blob/c4a7e69b6504365d7e5326b0b8e6cd95e3293f96/wci/workflow/compute_provider/aws_lambda.go#L16-L20 - providerDetails := map[string]any{ - "arn": functionARN, + providerDetails := map[string]any{} + if functionARN != "" { + providerDetails["arn"] = functionARN } if assumeRoleARN != "" { providerDetails["role"] = assumeRoleARN @@ -1052,6 +1053,51 @@ func awsLambdaProviderDetailsPayload( return dc.ToPayload(&providerDetails) } +func validateAWSAgentcoreProviderDetails(details map[string]any, skipRoleAndExternalID bool) error { + if v, ok := details["endpoint_arn"].(string); !ok || v == "" { + return fmt.Errorf("missing required AWS Agentcore provider detail: endpoint_arn") + } + if skipRoleAndExternalID { + for _, key := range []string{"role", "role_external_id"} { + if _, ok := details[key]; ok { + return fmt.Errorf("AWS Agentcore provider detail %q must not be set when --aws-agentcore-skip-role-and-external-id is passed", key) + } + } + return nil + } + for _, key := range []string{"role", "role_external_id"} { + if v, ok := details[key].(string); !ok || v == "" { + return fmt.Errorf("missing required AWS Agentcore provider detail: %s", key) + } + } + return nil +} + +// awsAgentcoreProviderDetailsPayload validates the AWS AgentCore inputs and returns encoded payload +func awsAgentcoreProviderDetailsPayload( + endpointARN string, + assumeRoleARN string, + assumeRoleExternalID string, + skipRoleAndExternalID bool, +) (*commonpb.Payload, error) { + providerDetails := map[string]any{} + if endpointARN != "" { + providerDetails["endpoint_arn"] = endpointARN + } + if assumeRoleARN != "" { + providerDetails["role"] = assumeRoleARN + } + if assumeRoleExternalID != "" { + providerDetails["role_external_id"] = assumeRoleExternalID + } + err := validateAWSAgentcoreProviderDetails(providerDetails, skipRoleAndExternalID) + if err != nil { + return nil, err + } + dc := converter.GetDefaultDataConverter() + return dc.ToPayload(&providerDetails) +} + func validateGCPCloudRunProviderDetails(details map[string]any) error { for _, key := range []string{"project", "region", "worker_pool", "service_account"} { if v, ok := details[key].(string); !ok || v == "" { @@ -1087,43 +1133,87 @@ func gcpCloudRunProviderDetailsPayload( return dc.ToPayload(&providerDetails) } +// ComputeConfigArgs holds ComputeConfig specific args along with helpers for distinguishing between desired compute +// provider +type ComputeConfigArgs struct { + awsLambdaFunctionArn string + awsLambdaAssumeRoleArn string + awsLambdaAssumeRoleExternalId string + awsLambdaSkipRoleAndExternalId bool + awsAgentcoreEndpointArn string + awsAgentcoreAssumeRoleArn string + awsAgentcoreAssumeRoleExternalId string + awsAgentcoreSkipRoleAndExternalId bool + gcpCloudRunProject string + gcpCloudRunRegion string + gcpCloudRunWorkerPool string + gcpCloudRunServiceAccount string +} + +func (c *ComputeConfigArgs) hasAwsLambdaArgs() bool { + return c.awsLambdaFunctionArn != "" || + c.awsLambdaAssumeRoleArn != "" || + c.awsLambdaAssumeRoleExternalId != "" || + c.awsLambdaSkipRoleAndExternalId +} + +func (c *ComputeConfigArgs) hasAwsAgentcoreArgs() bool { + return c.awsAgentcoreEndpointArn != "" || + c.awsAgentcoreAssumeRoleArn != "" || + c.awsAgentcoreAssumeRoleExternalId != "" || + c.awsAgentcoreSkipRoleAndExternalId +} + +func (c *ComputeConfigArgs) hasGcpCloudRunArgs() bool { + return c.gcpCloudRunProject != "" || + c.gcpCloudRunRegion != "" || + c.gcpCloudRunWorkerPool != "" || + c.gcpCloudRunServiceAccount != "" +} + // computeProviderConfig selects the single compute provider for a Worker // Deployment Version's "default" scaling group from the command's flags. It -// enforces that AWS Lambda and GCP Cloud Run flags are not mixed, then -// dispatches on the trigger flag (--aws-lambda-function-arn / +// enforces that flags for different providers are not mixed, then dispatches on +// the trigger flag (--aws-lambda-function-arn / --aws-agentcore-endpoint-arn / // --gcp-cloud-run-worker-pool). Returns an empty providerType when no provider // flags are set, leaving the "no configuration" decision to the caller. -func computeProviderConfig( - awsLambdaFunctionARN string, - awsLambdaAssumeRoleARN string, - awsLambdaAssumeRoleExternalID string, - awsLambdaSkipRoleAndExternalID bool, - gcpCloudRunProject string, - gcpCloudRunRegion string, - gcpCloudRunWorkerPool string, - gcpCloudRunServiceAccount string, -) (providerType string, detailsPayload *commonpb.Payload, err error) { - awsSet := awsLambdaFunctionARN != "" || awsLambdaAssumeRoleARN != "" || awsLambdaAssumeRoleExternalID != "" - gcpSet := gcpCloudRunProject != "" || gcpCloudRunRegion != "" || gcpCloudRunWorkerPool != "" || gcpCloudRunServiceAccount != "" - if awsSet && gcpSet { - return "", nil, fmt.Errorf("cannot combine --aws-lambda-* and --gcp-cloud-run-* flags; a Worker Deployment Version supports a single compute provider") +func computeProviderConfig(c *ComputeConfigArgs) (providerType string, detailsPayload *commonpb.Payload, err error) { + awsLambdaSet := c.hasAwsLambdaArgs() + awsAgentcoreSet := c.hasAwsAgentcoreArgs() + gcpSet := c.hasGcpCloudRunArgs() + setCount := 0 + for _, set := range []bool{awsLambdaSet, awsAgentcoreSet, gcpSet} { + if set { + setCount++ + } + } + if setCount > 1 { + return "", nil, fmt.Errorf("cannot combine --aws-lambda-*, --aws-agentcore-*, and --gcp-cloud-run-* flags; a Worker Deployment Version supports a single compute provider") } switch { - case awsLambdaFunctionARN != "": + case c.hasAwsLambdaArgs(): p, err := awsLambdaProviderDetailsPayload( - awsLambdaFunctionARN, - awsLambdaAssumeRoleARN, - awsLambdaAssumeRoleExternalID, - awsLambdaSkipRoleAndExternalID, + c.awsLambdaFunctionArn, + c.awsLambdaAssumeRoleArn, + c.awsLambdaAssumeRoleExternalId, + c.awsLambdaSkipRoleAndExternalId, ) return "aws-lambda", p, err - case gcpCloudRunWorkerPool != "": + case c.hasAwsAgentcoreArgs(): + p, err := awsAgentcoreProviderDetailsPayload( + c.awsAgentcoreEndpointArn, + c.awsAgentcoreAssumeRoleArn, + c.awsAgentcoreAssumeRoleExternalId, + c.awsAgentcoreSkipRoleAndExternalId, + ) + return "aws-agentcore", p, err + case c.hasGcpCloudRunArgs(): p, err := gcpCloudRunProviderDetailsPayload( - gcpCloudRunProject, - gcpCloudRunRegion, - gcpCloudRunWorkerPool, - gcpCloudRunServiceAccount, + c.gcpCloudRunProject, + c.gcpCloudRunRegion, + c.gcpCloudRunWorkerPool, + c.gcpCloudRunServiceAccount, ) return "gcp-cloud-run", p, err default: @@ -1137,6 +1227,7 @@ func computeProviderConfig( // WCI rejects an incompatible pairing at CreateWorkerDeploymentVersion. var scalerTypeByProvider = map[string]string{ "aws-lambda": "no-sync", + "aws-agentcore": "no-sync", "gcp-cloud-run": "rate-based", } @@ -1258,16 +1349,20 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, deploymentName := c.DeploymentName requestID := uuid.NewString() - providerType, detailsPayload, err := computeProviderConfig( - c.AwsLambdaFunctionArn, - c.AwsLambdaAssumeRoleArn, - c.AwsLambdaAssumeRoleExternalId, - c.AwsLambdaSkipRoleAndExternalId, - c.GcpCloudRunProject, - c.GcpCloudRunRegion, - c.GcpCloudRunWorkerPool, - c.GcpCloudRunServiceAccount, - ) + providerType, detailsPayload, err := computeProviderConfig(&ComputeConfigArgs{ + awsLambdaFunctionArn: c.AwsLambdaFunctionArn, + awsLambdaAssumeRoleArn: c.AwsLambdaAssumeRoleArn, + awsLambdaAssumeRoleExternalId: c.AwsLambdaAssumeRoleExternalId, + awsLambdaSkipRoleAndExternalId: c.AwsLambdaSkipRoleAndExternalId, + awsAgentcoreEndpointArn: c.AwsAgentcoreEndpointArn, + awsAgentcoreAssumeRoleArn: c.AwsAgentcoreAssumeRoleArn, + awsAgentcoreAssumeRoleExternalId: c.AwsAgentcoreAssumeRoleExternalId, + awsAgentcoreSkipRoleAndExternalId: c.AwsAgentcoreSkipRoleAndExternalId, + gcpCloudRunProject: c.GcpCloudRunProject, + gcpCloudRunRegion: c.GcpCloudRunRegion, + gcpCloudRunWorkerPool: c.GcpCloudRunWorkerPool, + gcpCloudRunServiceAccount: c.GcpCloudRunServiceAccount, + }) if err != nil { return err } @@ -1351,24 +1446,29 @@ func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) run(cctx *Co RequestId: requestID, } + computeConfigArgs := &ComputeConfigArgs{ + awsLambdaFunctionArn: c.AwsLambdaFunctionArn, + awsLambdaAssumeRoleArn: c.AwsLambdaAssumeRoleArn, + awsLambdaAssumeRoleExternalId: c.AwsLambdaAssumeRoleExternalId, + awsLambdaSkipRoleAndExternalId: c.AwsLambdaSkipRoleAndExternalId, + awsAgentcoreEndpointArn: c.AwsAgentcoreEndpointArn, + awsAgentcoreAssumeRoleArn: c.AwsAgentcoreAssumeRoleArn, + awsAgentcoreAssumeRoleExternalId: c.AwsAgentcoreAssumeRoleExternalId, + awsAgentcoreSkipRoleAndExternalId: c.AwsAgentcoreSkipRoleAndExternalId, + gcpCloudRunProject: c.GcpCloudRunProject, + gcpCloudRunRegion: c.GcpCloudRunRegion, + gcpCloudRunWorkerPool: c.GcpCloudRunWorkerPool, + gcpCloudRunServiceAccount: c.GcpCloudRunServiceAccount, + } + if c.Remove { - if c.AwsLambdaFunctionArn != "" || c.AwsLambdaAssumeRoleArn != "" || c.AwsLambdaAssumeRoleExternalId != "" || - c.GcpCloudRunProject != "" || c.GcpCloudRunRegion != "" || c.GcpCloudRunWorkerPool != "" || c.GcpCloudRunServiceAccount != "" || + if computeConfigArgs.hasAwsLambdaArgs() || computeConfigArgs.hasAwsAgentcoreArgs() || computeConfigArgs.hasGcpCloudRunArgs() || c.gcpScalerFlags().anySet() { - return fmt.Errorf("--remove cannot be combined with --aws-lambda-* or --gcp-cloud-run-* flags") + return fmt.Errorf("--remove cannot be combined with --aws-lambda-*, --aws-agentcore-*, or --gcp-cloud-run-* flags") } request.RemoveComputeConfigScalingGroups = []string{"default"} } else { - providerType, detailsPayload, err := computeProviderConfig( - c.AwsLambdaFunctionArn, - c.AwsLambdaAssumeRoleArn, - c.AwsLambdaAssumeRoleExternalId, - c.AwsLambdaSkipRoleAndExternalId, - c.GcpCloudRunProject, - c.GcpCloudRunRegion, - c.GcpCloudRunWorkerPool, - c.GcpCloudRunServiceAccount, - ) + providerType, detailsPayload, err := computeProviderConfig(computeConfigArgs) if err != nil { return err } diff --git a/internal/temporalcli/commands.worker.deployment.internal_test.go b/internal/temporalcli/commands.worker.deployment.internal_test.go index 18e0a6a08..f25603819 100644 --- a/internal/temporalcli/commands.worker.deployment.internal_test.go +++ b/internal/temporalcli/commands.worker.deployment.internal_test.go @@ -17,6 +17,7 @@ func TestScalerTypeForProvider(t *testing.T) { expectErr bool }{ {"aws-lambda is invoke-based -> no-sync", "aws-lambda", "no-sync", false}, + {"aws-agentcore is invoke-based -> no-sync", "aws-agentcore", "no-sync", false}, {"gcp-cloud-run is worker-set-based -> rate-based", "gcp-cloud-run", "rate-based", false}, {"unknown provider errors", "azure-container-apps", "", true}, {"empty provider errors", "", "", true}, @@ -38,7 +39,7 @@ func TestScalerTypeForProvider(t *testing.T) { // scaler mapping; a missing entry makes scalerTypeForProvider error before the // request is sent, so this guards against forgetting to map a newly-added provider. func TestScalerTypeByProviderCoversAllProviders(t *testing.T) { - for _, providerType := range []string{"aws-lambda", "gcp-cloud-run"} { + for _, providerType := range []string{"aws-lambda", "aws-agentcore", "gcp-cloud-run"} { _, ok := scalerTypeByProvider[providerType] require.Truef(t, ok, "provider %q has no scaler mapping", providerType) } diff --git a/internal/temporalcli/commands.worker.deployment_test.go b/internal/temporalcli/commands.worker.deployment_test.go index 7bc7f740c..db9c62661 100644 --- a/internal/temporalcli/commands.worker.deployment_test.go +++ b/internal/temporalcli/commands.worker.deployment_test.go @@ -1347,6 +1347,76 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { s.Error(res.Err) s.ErrorContains(res.Err, "--aws-lambda-skip-role-and-external-id") + // AWS Agentcore: a single endpoint ARN drives the provider; role and external + // id are required (unless skipped) + agentcoreEndpointARN := "arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/my-runtime-abc123/runtime-endpoint/DEFAULT" + + agentcoreMissingExternalIDBuildID := uuid.NewString() + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", agentcoreMissingExternalIDBuildID, + "--aws-agentcore-endpoint-arn", agentcoreEndpointARN, + "--aws-agentcore-assume-role-arn", assumeRoleARN, + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "missing required AWS Agentcore provider detail: role_external_id") + + agentcoreMissingRoleBuildID := uuid.NewString() + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", agentcoreMissingRoleBuildID, + "--aws-agentcore-endpoint-arn", agentcoreEndpointARN, + "--aws-agentcore-assume-role-external-id", assumeRoleExternalID, + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "missing required AWS Agentcore provider detail: role") + + // --aws-agentcore-skip-role-and-external-id and the role/external-id flags are + // mutually exclusive: passing both is rejected client-side. + agentcoreSkipWithRoleBuildID := uuid.NewString() + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", agentcoreSkipWithRoleBuildID, + "--aws-agentcore-endpoint-arn", agentcoreEndpointARN, + "--aws-agentcore-assume-role-arn", assumeRoleARN, + "--aws-agentcore-assume-role-external-id", assumeRoleExternalID, + "--aws-agentcore-skip-role-and-external-id", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "--aws-agentcore-skip-role-and-external-id") + + // AWS Agentcore and GCP Cloud Run providers are mutually exclusive on create. + agentcoreMixedProvidersBuildID := uuid.NewString() + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", agentcoreMixedProvidersBuildID, + "--aws-agentcore-endpoint-arn", agentcoreEndpointARN, + "--gcp-cloud-run-worker-pool", "my-worker-pool", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "cannot combine --aws-lambda-*, --aws-agentcore-*, and --gcp-cloud-run-* flags") + + // AWS Agentcore and Lambda providers are mutually exclusive on create. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", agentcoreMixedProvidersBuildID, + "--aws-agentcore-endpoint-arn", agentcoreEndpointARN, + "--aws-lambda-assume-role-arn", assumeRoleARN, + "--aws-lambda-assume-role-external-id", assumeRoleExternalID, + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "cannot combine --aws-lambda-*, --aws-agentcore-*, and --gcp-cloud-run-* flags") + // --gcp-cloud-run-worker-pool requires project, region, and // service-account; the first missing detail key is reported. missingGCPProjectBuildID := uuid.NewString() @@ -1388,7 +1458,7 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { "--gcp-cloud-run-worker-pool", "my-worker-pool", ) s.Error(res.Err) - s.ErrorContains(res.Err, "cannot combine --aws-lambda-* and --gcp-cloud-run-* flags") + s.ErrorContains(res.Err, "cannot combine --aws-lambda-*, --aws-agentcore-*, and --gcp-cloud-run-* flags") // Attempting to update the compute config for a non-existent WDV // should fail. @@ -1428,7 +1498,7 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { "--gcp-cloud-run-worker-pool", "my-worker-pool", ) s.Error(res.Err) - s.ErrorContains(res.Err, "cannot combine --aws-lambda-* and --gcp-cloud-run-* flags") + s.ErrorContains(res.Err, "cannot combine --aws-lambda-*, --aws-agentcore-*, and --gcp-cloud-run-* flags") // --remove cannot be combined with GCP Cloud Run flags. res = s.Execute( @@ -1923,6 +1993,124 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_LambdaComputeConfi s.Contains(res.Stdout.String(), "Successfully removed worker deployment version compute config") } +// TODO(jaypipes): Enable this test when we have a way of ensuring AWS resource +// fixtures since the CLI test harness uses a real Temporal Server and a real +// Temporal Server validates any supplied AWS Lambda Function and Assume Role +// ARNs are good... +func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_AgentCoreComputeConfig() { + s.T().Skip("AWS AgentCore Runtime, Endpoint and Assume Role fixtures needed.") + deploymentName := uuid.NewString() + taskQueue := uuid.NewString() + + lazyCreatedBuildID := uuid.NewString() + lazyCreatedVer := worker.WorkerDeploymentVersion{ + DeploymentName: deploymentName, + BuildID: lazyCreatedBuildID, + } + + // Create worker with explicit versioning. This will end up creating a + // WorkerDeployment with the specified name. We will then manually create a + // worker deployment version using the `temporal worker deployment + // create-version` command. + w1 := worker.New(s.Client, taskQueue, worker.Options{ + DeploymentOptions: worker.DeploymentOptions{ + UseVersioning: true, + Version: lazyCreatedVer, + }, + }) + + // Register a workflow with explicit Pinned versioning behavior to trigger + // creation of the worker deployment. + w1.RegisterWorkflowWithOptions( + func(ctx workflow.Context, input any) (any, error) { + workflow.GetSignalChannel(ctx, "complete-signal").Receive(ctx, nil) + return nil, nil + }, + workflow.RegisterOptions{ + Name: "TestCreateWorkerDeploymentVersion_AgentCoreComputeConfig", + VersioningBehavior: workflow.VersioningBehaviorPinned, + }, + ) + + s.NoError(w1.Start()) + + // Now that we know the worker deployment exists (because the above + // lazily-created worker deployment version ended up creating it), we will + // manually create a new worker deployment version using the `temporal + // worker deployment create-version` CLI command. + // + // Create a WDV with a valid Compute Config specified and verify that the + // compute config provider is displayed in the output of `temporal worker + // deployment describe-version` + computeConfigBuildID := uuid.NewString() + + endpointARN := "arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/my-runtime-abc123/runtime-endpoint/myEndpoint" + assumeRoleARN := "arn:aws:iam::123456789012:role/MyServiceRole" + assumeRoleExternalID := "external-id" + + res := s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + "--aws-agentcore-endpoint-arn", endpointARN, + "--aws-agentcore-assume-role-arn", assumeRoleARN, + "--aws-agentcore-assume-role-external-id", assumeRoleExternalID, + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Successfully created worker deployment version") + + // Wait for the deployment version to appear + s.EventuallyWithT(func(t *assert.CollectT) { + res := s.Execute( + "worker", "deployment", "describe-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + ) + assert.NoError(t, res.Err) + }, 30*time.Second, 100*time.Millisecond) + + // Check that there is a compute config returned for this WDV + res = s.Execute( + "worker", "deployment", "describe-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + "--output", "json", + ) + s.NoError(res.Err) + jsonOut := jsonDeploymentVersionInfoType{} + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &jsonOut)) + s.NotNil(jsonOut.ComputeConfig, "ComputeConfig should not be nil.") + + // We should be able to update the compute config. + endpointARN2 := "arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/my-runtime-abc123/runtime-endpoint/myEndpoint2" + assumeRoleARN2 := "arn:aws:iam::123456789012:role/MyServiceRole2" + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + "--aws-agentcore-endpoint-arn", endpointARN2, + "--aws-agentcore-assume-role-arn", assumeRoleARN2, + "--aws-agentcore-assume-role-external-id", assumeRoleExternalID, + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Successfully updated worker deployment version compute config") + + // As well as remove the compute config. + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + "--remove", + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Successfully removed worker deployment version compute config") +} + // TODO(jaypipes): Enable this test when we have a way of ensuring GCP resource // fixtures since the CLI test harness uses a real Temporal Server and a real // Temporal Server validates that any supplied GCP Cloud Run worker pool and diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index a22982a84..4365e5f50 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -1093,6 +1093,19 @@ commands: --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID ``` + Or pass compute provider information for an AWS Bedrock Agentcore Runtime + that spawns a Worker in the Worker Deployment: + + ``` + temporal worker deployment create-version \ + --namespace YourNamespaceName \ + --deployment-name YourDeploymentName \ + --build-id YourBuildID \ + --aws-agentcore-endpoint-arn AgentcoreRuntimeEndpointARN \ + --aws-agentcore-assume-role-arn AgentcoreAssumeRoleARN \ + --aws-agentcore-assume-role-external-id AgentcoreAssumeRoleExternalID + ``` + Or pass compute provider information for a GCP Cloud Run worker pool that spawns a Worker in the Worker Deployment: @@ -1150,6 +1163,34 @@ commands: --aws-lambda-assume-role-arn and --aws-lambda-assume-role-external-id are required unless this flag is passed, in which case both must be omitted. + - name: aws-agentcore-endpoint-arn + type: string + description: | + AWS Bedrock Agentcore Runtime endpoint ARN to invoke when there are + no active pollers for task queue targets in the Worker Deployment. + The endpoint ARN encodes the runtime, endpoint name, and region. + - name: aws-agentcore-assume-role-arn + type: string + description: | + AWS IAM role ARN that the Temporal server will assume when invoking + the Agentcore Runtime that spawns a new Worker in this Worker + Deployment Version. Required when --aws-agentcore-endpoint-arn is + specified, and must be omitted when + --aws-agentcore-skip-role-and-external-id is passed. + - name: aws-agentcore-assume-role-external-id + type: string + description: | + Temporal server will enforce that the AWS IAM trust policy associated + with the AWS IAM role specified in --aws-agentcore-assume-role-arn has an + aws:ExternalId condition that matches the supplied value. Required + when --aws-agentcore-endpoint-arn is specified, and must be omitted + when --aws-agentcore-skip-role-and-external-id is passed. + - name: aws-agentcore-skip-role-and-external-id + type: bool + description: | + When --aws-agentcore-endpoint-arn is specified, --aws-agentcore-assume-role-arn + and --aws-agentcore-assume-role-external-id are required unless this + flag is passed, in which case both must be omitted. - name: gcp-cloud-run-project type: string description: | @@ -1449,6 +1490,17 @@ commands: --aws-lambda-assume-role-arn UpdatedRoleARN ``` + To update the AWS Bedrock Agentcore Runtime endpoint associated with an + existing Worker Deployment Version: + + ``` + temporal worker deployment update-version-compute-config \ + --deployment-name YourDeploymentName --build-id YourBuildID \ + --aws-agentcore-endpoint-arn UpdatedAgentcoreRuntimeEndpointARN \ + --aws-agentcore-assume-role-arn UpdatedRoleARN \ + --aws-agentcore-assume-role-external-id UpdatedExternalID + ``` + To update the GCP Cloud Run worker pool associated with an existing Worker Deployment Version: @@ -1528,6 +1580,34 @@ commands: --aws-lambda-assume-role-arn and --aws-lambda-assume-role-external-id are required unless this flag is passed, in which case both must be omitted. + - name: aws-agentcore-endpoint-arn + type: string + description: | + AWS Bedrock Agentcore Runtime endpoint ARN to invoke when there are + no active pollers for task queue targets in the Worker Deployment. + The endpoint ARN encodes the runtime, endpoint name, and region. + - name: aws-agentcore-assume-role-arn + type: string + description: | + AWS IAM role ARN that the Temporal server will assume when invoking + the Agentcore Runtime that spawns a new Worker in this Worker + Deployment Version. Required when --aws-agentcore-endpoint-arn is + specified, and must be omitted when + --aws-agentcore-skip-role-and-external-id is passed. + - name: aws-agentcore-assume-role-external-id + type: string + description: | + Temporal server will enforce that the AWS IAM trust policy associated + with the AWS IAM role specified in --aws-agentcore-assume-role-arn has an + aws:ExternalId condition that matches the supplied value. Required + when --aws-agentcore-endpoint-arn is specified, and must be omitted + when --aws-agentcore-skip-role-and-external-id is passed. + - name: aws-agentcore-skip-role-and-external-id + type: bool + description: | + When --aws-agentcore-endpoint-arn is specified, --aws-agentcore-assume-role-arn + and --aws-agentcore-assume-role-external-id are required unless this + flag is passed, in which case both must be omitted. - name: gcp-cloud-run-project type: string description: |