diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..7226d8733 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,24 @@ +version: 2 +updates: + - package-ecosystem: gomod + directory: "/" + schedule: + interval: weekly + cooldown: + default-days: 14 + allow: + - dependency-type: direct + ignore: + # Temporal dependencies are managed manually to ensure coordinated upgrades across all temporal packages + - dependency-name: "go.temporal.io/*" + + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + cooldown: + default-days: 14 + groups: + github-actions: + patterns: + - "*" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..0b129b983 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,74 @@ +## Related issues + + + +## What changed? + + + +## Checklist + + + +**Stability** +- [ ] Breaking changes are marked with 💥 in the PR title and release notes +- [ ] Changes to JSON output (`-o json` / `-o jsonl`) are treated as breaking changes + +**Design** +- [ ] This feature does not depend on Cloud-only APIs or behavior (it works against an OSS server) +- [ ] New commands follow `temporal ` structure (e.g. `temporal workflow start`) +- [ ] New flags are named after the API concept, not the implementation mechanism (good: `--search-attribute`, bad: `--index-field`) +- [ ] New flags don't duplicate an existing flag that serves the same purpose +- [ ] New flags do not have short aliases without strong justification +- [ ] Experimental features are marked with `(Experimental)` in `commands.yaml` + +**Help text** (see style guide at the top of `commands.yaml`) +- [ ] All flags shown in help text and examples are implemented and functional +- [ ] Summaries use sentence case and have no trailing period +- [ ] Long descriptions end with a period and include at least one example invocation +- [ ] Examples use long flags (`--namespace`, not `-n`), one flag per line +- [ ] Placeholder values use `YourXxx` form (`YourWorkflowId`, `YourNamespace`) + +**Behavior** +- [ ] Results go to stdout; errors and warnings go to stderr +- [ ] Error messages are lowercase with no trailing punctuation + +**Tests** +- [ ] Added functional test(s) (`SharedServerSuite`) +- [ ] Added unit test(s) (`func TestXxx`) where applicable + +## Manual tests + + + +**Setup** +``` +temporal server start-dev --headless +temporal workflow start \ + --type YourWorkflowType \ + --task-queue YourTaskQueue \ + --workflow-id YourWorkflowId +``` + +**Happy path** +``` +$ temporal \ + --flag value + +``` + +**Error case** +``` +$ temporal \ + --invalid-combination +Error: +$ echo $? +1 +``` + +**Composition** +``` +$ temporal ... +$ temporal --flag + +``` diff --git a/.github/workflows/build-and-publish-docker.yml b/.github/workflows/build-and-publish-docker.yml index f6bc513ab..502c103d1 100644 --- a/.github/workflows/build-and-publish-docker.yml +++ b/.github/workflows/build-and-publish-docker.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -36,7 +36,7 @@ jobs: env: INPUT_VERSION: ${{ inputs.version }} INPUT_TAG_LATEST: ${{ inputs.tag_latest }} - uses: actions/github-script@v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const inputVersion = process.env.INPUT_VERSION; @@ -82,14 +82,14 @@ jobs: ls -lh dist/arm64/temporal - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub if: inputs.publish - uses: docker/login-action@v3 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 22764a5af..6a5df116d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod @@ -53,12 +53,12 @@ jobs: HAS_SECRETS: ${{ secrets.TEMPORAL_CLIENT_CERT != '' && secrets.TEMPORAL_CLIENT_KEY != '' }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod @@ -72,7 +72,7 @@ jobs: run: gotestsum --junitfile junit-xml/${{matrix.os}}.xml -- ./... - name: Upload junit-xml artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() with: name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--${{matrix.os}} @@ -108,7 +108,7 @@ jobs: - name: Test cloud API key env var if: ${{ matrix.cloudTestTarget && env.HAS_SECRETS == 'true' }} env: - TEMPORAL_ADDRESS: us-east-1.aws.api.temporal.io:7233 + TEMPORAL_ADDRESS: ca-central-1.aws.api.temporal.io:7233 TEMPORAL_NAMESPACE: ${{ vars.TEMPORAL_CLIENT_NAMESPACE }} TEMPORAL_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} shell: bash @@ -117,7 +117,7 @@ jobs: - name: Test cloud API key arg if: ${{ matrix.cloudTestTarget && env.HAS_SECRETS == 'true' }} env: - TEMPORAL_ADDRESS: us-east-1.aws.api.temporal.io:7233 + TEMPORAL_ADDRESS: ca-central-1.aws.api.temporal.io:7233 TEMPORAL_NAMESPACE: ${{ vars.TEMPORAL_CLIENT_NAMESPACE }} shell: bash run: go run ./cmd/temporal workflow list --limit 2 --api-key ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index b8b6cbe97..7c49fd9df 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -11,8 +11,8 @@ jobs: name: Govulncheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - uses: actions/setup-go@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod - uses: temporalio/public-actions/golang/govulncheck@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0bcd5d666..ec10831b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,12 +15,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: "go.mod" check-latest: true @@ -47,7 +47,7 @@ jobs: run: echo "go=$(go version | cut -d ' ' -f 3)" >> "$GITHUB_OUTPUT" - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 + uses: goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2 with: version: v2.12.7 args: release diff --git a/.github/workflows/trigger-docs.yml b/.github/workflows/trigger-docs.yml index cfa6349a8..a6124050c 100644 --- a/.github/workflows/trigger-docs.yml +++ b/.github/workflows/trigger-docs.yml @@ -39,7 +39,7 @@ jobs: - name: Generate token id: generate_token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: app-id: ${{ secrets.TEMPORAL_CICD_APP_ID }} private-key: ${{ secrets.TEMPORAL_CICD_PRIVATE_KEY }} diff --git a/.github/workflows/validate-dependabot.yml b/.github/workflows/validate-dependabot.yml new file mode 100644 index 000000000..d9010aa50 --- /dev/null +++ b/.github/workflows/validate-dependabot.yml @@ -0,0 +1,29 @@ +name: Validate Dependabot Config +on: + pull_request: + paths: + - '.github/dependabot.yml' + push: + branches: + - main + paths: + - '.github/dependabot.yml' + +permissions: + contents: read + +jobs: + validate: + name: Validate Dependabot Config + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + with: + python-version: '3.12' + + - name: Validate dependabot.yml + run: | + pip install check-jsonschema==0.37.2 + check-jsonschema --builtin-schema vendor.dependabot .github/dependabot.yml diff --git a/Dockerfile b/Dockerfile index 8b19020af..5b679d63a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.23 +FROM alpine:3.23.4 ARG TARGETARCH diff --git a/internal/commandsgen/code.go b/internal/commandsgen/code.go index fb500751b..84cff3bbc 100644 --- a/internal/commandsgen/code.go +++ b/internal/commandsgen/code.go @@ -233,12 +233,22 @@ func (c *Command) writeCode(w *codeWriter) error { } else { w.writeLinef("s.Command.Args = %v.NoArgs", w.importCobra()) } - if c.IgnoreMissingEnv { + if c.IgnoreMissingEnv || c.Deprecated { w.writeLinef("s.Command.Annotations = make(map[string]string)") - w.writeLinef("s.Command.Annotations[\"ignoresMissingEnv\"] = \"true\"") + if c.IgnoreMissingEnv { + w.writeLinef("s.Command.Annotations[\"ignoresMissingEnv\"] = \"true\"") + } } - if c.Deprecated != "" { - w.writeLinef("s.Command.Deprecated = %q", c.Deprecated) + // Note: We intentionally don't set s.Command.Deprecated here because Cobra + // prints deprecation warnings to stdout, which breaks JSON output. Instead, + // the deprecation warning is prepended to the description/help text and + // printed to stderr via the annotation below. + if c.Deprecated { + msg := c.DeprecationMessage + if msg == "" { + msg = defaultDeprecationMessage + } + w.writeLinef("s.Command.Annotations[\"deprecationWarning\"] = %q", msg) } // Add subcommands for _, subCommand := range subCommands { diff --git a/internal/commandsgen/parse.go b/internal/commandsgen/parse.go index 2083a033d..831b86902 100644 --- a/internal/commandsgen/parse.go +++ b/internal/commandsgen/parse.go @@ -39,8 +39,9 @@ type ( Description string `yaml:"description"` DescriptionPlain string DescriptionHighlighted string - Deprecated string `yaml:"deprecated"` - HasInit bool `yaml:"has-init"` + Deprecated bool `yaml:"deprecated"` + DeprecationMessage string `yaml:"deprecation-message"` + HasInit bool `yaml:"has-init"` ExactArgs int `yaml:"exact-args"` MaximumArgs int `yaml:"maximum-args"` IgnoreMissingEnv bool `yaml:"ignores-missing-env"` @@ -137,6 +138,22 @@ var markdownInlineCodeRegex = regexp.MustCompile("`([^`]+)`") const ansiReset = "\033[0m" const ansiBold = "\033[1m" +const defaultDeprecationMessage = "This command is deprecated and will be removed in a later release." + +// generateDeprecationBox creates a formatted CAUTION box for deprecated commands. +// If message is empty, uses the default deprecation message. +func generateDeprecationBox(message string) string { + if message == "" { + message = defaultDeprecationMessage + } + content := "CAUTION: " + message + // Calculate box width (content + 2 spaces padding + 2 border chars) + boxWidth := len(content) + 4 + border := "+" + strings.Repeat("-", boxWidth-2) + "+" + middle := "| " + content + " |" + return "```\n" + border + "\n" + middle + "\n" + border + "\n```\n\n" +} + func (o OptionSets) processSection() error { if o.Name == "" { return fmt.Errorf("missing option set name") @@ -172,6 +189,13 @@ func (c *Command) processSection() error { return fmt.Errorf("missing description for command: %s", c.FullName) } + // Auto-handle deprecation: prepend warning box to description and append + // "(Deprecated)" to summary. + if c.Deprecated { + c.Description = generateDeprecationBox(c.DeprecationMessage) + c.Description + c.Summary += " (Deprecated)" + } + if len(c.NamePath) == 2 { if c.Docs.Keywords == nil { return fmt.Errorf("missing keywords for root command: %s", c.FullName) diff --git a/internal/commandsgen/parse_test.go b/internal/commandsgen/parse_test.go new file mode 100644 index 000000000..eb77a83e1 --- /dev/null +++ b/internal/commandsgen/parse_test.go @@ -0,0 +1,48 @@ +package commandsgen + +import "testing" + +func TestGenerateDeprecationBox(t *testing.T) { + tests := []struct { + name string + message string + expected string + }{ + { + name: "default message when empty", + message: "", + expected: "```\n" + + "+-----------------------------------------------------------------------------+\n" + + "| CAUTION: This command is deprecated and will be removed in a later release. |\n" + + "+-----------------------------------------------------------------------------+\n" + + "```\n\n", + }, + { + name: "custom message", + message: "Use the new API instead.", + expected: "```\n" + + "+-----------------------------------+\n" + + "| CAUTION: Use the new API instead. |\n" + + "+-----------------------------------+\n" + + "```\n\n", + }, + { + name: "short custom message", + message: "Removed.", + expected: "```\n" + + "+-------------------+\n" + + "| CAUTION: Removed. |\n" + + "+-------------------+\n" + + "```\n\n", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := generateDeprecationBox(tt.message) + if got != tt.expected { + t.Errorf("generateDeprecationBox(%q) =\n%q\nwant:\n%q", tt.message, got, tt.expected) + } + }) + } +} diff --git a/internal/temporalcli/commands.config.go b/internal/temporalcli/commands.config.go index e8a423dd0..0b88bfaac 100644 --- a/internal/temporalcli/commands.config.go +++ b/internal/temporalcli/commands.config.go @@ -111,13 +111,19 @@ func (c *TemporalConfigGetCommand) run(cctx *CommandContext, _ []string) error { } return cctx.Printer.PrintStructured(tomlConf.Profiles[profileName], printer.StructuredOptions{}) } else { + // Capture whether TLS is configured before the loop below. Looking up + // any "tls.*" property via reflectEnvConfigProp lazily initializes + // confProfile.TLS to a non-nil empty struct, which would otherwise make + // TLS appear configured when it is not (#1077). + tlsConfigured := confProfile.TLS != nil + // Get every property individually as a property-value pair except zero // vals var props []prop for k := range envConfigPropsToFieldNames { // TLS is a special case if k == "tls" { - if confProfile.TLS != nil { + if tlsConfigured { props = append(props, prop{Property: "tls", Value: true}) } continue diff --git a/internal/temporalcli/commands.config_test.go b/internal/temporalcli/commands.config_test.go index 5cc89ec86..9375402bf 100644 --- a/internal/temporalcli/commands.config_test.go +++ b/internal/temporalcli/commands.config_test.go @@ -151,6 +151,30 @@ disable_host_verification = true`)) } } +func TestConfig_Get_NoTLSWhenUnconfigured(t *testing.T) { + // Regression test for #1077: `config get` (table output) must not display + // "tls true" for a profile that has no TLS configuration. + h := NewCommandHarness(t) + defer h.Close() + + f, err := os.CreateTemp("", "") + h.NoError(err) + defer os.Remove(f.Name()) + _, err = f.Write([]byte(` +[profile.devtest] +address = "localhost:17233" +namespace = "default"`)) + f.Close() + h.NoError(err) + h.Options.EnvLookup = EnvLookupMap{"TEMPORAL_CONFIG_FILE": f.Name(), "TEMPORAL_PROFILE": "devtest"} + + res := h.Execute("config", "get") + h.NoError(res.Err) + h.Contains(res.Stdout.String(), "localhost:17233") + // No TLS section was configured, so "tls" should not appear at all. + h.NotContains(res.Stdout.String(), "tls") +} + func TestConfig_TLS_Boolean(t *testing.T) { h := NewCommandHarness(t) defer h.Close() diff --git a/internal/temporalcli/commands.extension.go b/internal/temporalcli/commands.extension.go index d46de64dc..794242263 100644 --- a/internal/temporalcli/commands.extension.go +++ b/internal/temporalcli/commands.extension.go @@ -31,12 +31,10 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo // Find the deepest matching built-in command and remaining args. foundCmd, remainingArgs, findErr := tcmd.Command.Find(cctx.Options.Args) - // Check if remaining args include positional args (not just flags). - // If not, a built-in command fully handles this - no extension needed. - hasPosArgs := slices.ContainsFunc(remainingArgs, isPosArg) - if findErr == nil && !hasPosArgs { - return nil, false - } + // Cobra normally adds --help/-h before parsing, but extension dispatch + // pre-parses flags before Cobra's execution path runs. We Initialize it so that + // help is treated as a normal CLI flag instead of surfacing as pflag.ErrHelp from flag parsing. + foundCmd.InitDefaultHelpFlag() // Group args into these lists: // - cliParseArgs: args to validate (subset of cliPassArgs) @@ -44,6 +42,12 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo // - extArgs: args to pass to extension and use for extension lookup cliParseArgs, cliPassArgs, extArgs := groupArgs(foundCmd, remainingArgs) + // Check if remaining args include positional args (not just flags). + // If not, a built-in command fully handles this - no extension needed. + if findErr == nil && !slices.ContainsFunc(extArgs, isPosArg) { + return nil, false + } + // Search for an extension executable. cmdPrefix := strings.Fields(foundCmd.CommandPath()) extPath, extArgs := lookupExtension(cmdPrefix, extArgs) @@ -95,6 +99,13 @@ func groupArgs(foundCmd *cobra.Command, args []string) (cliParseArgs, cliPassArg name, hasInline := parseFlagArg(arg) if f, takesValue := lookupFlag(foundCmd, name); f != nil { + // Help flag after positional args should go to extArgs so it + // gets forwarded to extensions (e.g. "temporal foo bar --help"). + // Before positional args it stays in cliPassArgs for Cobra to handle. + if f.Name == "help" && seenPos { + extArgs = append(extArgs, arg) + continue + } // Known CLI flag: goes to cliPassArgs. // Flags in cliArgsToParseForExtension also go to cliParseArgs. shouldParse := cliArgsToParseForExtension[f.Name] diff --git a/internal/temporalcli/commands.extension_test.go b/internal/temporalcli/commands.extension_test.go index 46ab15c49..266a8fcd1 100644 --- a/internal/temporalcli/commands.extension_test.go +++ b/internal/temporalcli/commands.extension_test.go @@ -88,6 +88,19 @@ func TestExtension_DoesNotOverrideBuiltinCommand(t *testing.T) { res := h.Execute("workflow", "list", "--help") assert.Contains(t, res.Stdout.String(), "List Workflow Executions") }) + + t.Run("subcommand with value flags", func(t *testing.T) { + for _, args := range [][]string{ + {"workflow", "list", "--address", "123", "--help"}, + {"workflow", "list", "--help", "--address", "123"}, + {"workflow", "list", "--namespace", "foo", "--help"}, + } { + res := h.Execute(args...) + assert.Contains(t, res.Stdout.String(), "List Workflow Executions") + assert.NotContains(t, res.Stderr.String(), "pflag: help requested") + assert.NoError(t, res.Err) + } + }) } func TestExtension_Flags(t *testing.T) { @@ -149,6 +162,14 @@ func TestExtension_Flags(t *testing.T) { {args: "workflow diagram foo --flag value", want: "temporal-workflow-diagram-foo --flag value"}, // nested commands {args: "workflow diagram --output invalid", want: "temporal-workflow-diagram --output invalid"}, // invalid value passed through + // Help flags are forwarded to extensions, not handled locally. + + {args: "foo --help", want: "temporal-foo --help"}, + {args: "foo -h", want: "temporal-foo -h"}, + {args: "foo bar --help", want: "temporal-foo-bar --help"}, // most specific extension wins + {args: "workflow diagram --help", want: "temporal-workflow-diagram --help"}, + {args: "workflow diagram -h", want: "temporal-workflow-diagram -h"}, + // Note: Flag aliases are already implicitly tested via other command-specific tests. } diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 08a68010e..4acec6e5d 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -735,9 +735,9 @@ func NewTemporalActivityPauseCommand(cctx *CommandContext, parent *TemporalActiv s.Command.Use = "pause [flags]" s.Command.Short = "Pause an Activity" if hasHighlighting { - s.Command.Long = "Pause an Activity. Not supported for Standalone Activities.\n\nIf the Activity is not currently running (e.g. because it previously\nfailed), it will not be run again until it is unpaused.\n\nHowever, if the Activity is currently running, it will run until the next\ntime it fails, completes, or times out, at which point the pause will kick in.\n\nIf the Activity is on its last retry attempt and fails, the failure will\nbe returned to the caller, just as if the Activity had not been paused.\n\nSpecify the Activity and Workflow IDs:\n\n\x1b[1mtemporal activity pause \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\x1b[0m\n\nTo later unpause the activity, see unpause. You may also want to\nreset the activity to unpause it while also starting it from the beginning." + s.Command.Long = "Pause an Activity. Not supported for Standalone Activities.\n\nIf the Activity is not currently running (e.g. because it previously\nfailed), it will not be run again until it is unpaused.\n\nHowever, if the Activity is currently running, it will run until the next\ntime it fails, completes, or times out, at which point the pause will kick in.\n\nPause does not stop or extend the Activity's Schedule-To-Close Timeout.\nA paused Activity can still time out. Use \x1b[1mtemporal activity update-options\x1b[0m\nto extend timeout settings before a long pause.\n\nIf the Activity is on its last retry attempt and fails, the failure will\nbe returned to the caller, just as if the Activity had not been paused.\n\nSpecify the Activity and Workflow IDs:\n\n\x1b[1mtemporal activity pause \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\x1b[0m\n\nTo later unpause the activity, see unpause. You may also want to\nreset the activity to unpause it while also starting it from the beginning." } else { - s.Command.Long = "Pause an Activity. Not supported for Standalone Activities.\n\nIf the Activity is not currently running (e.g. because it previously\nfailed), it will not be run again until it is unpaused.\n\nHowever, if the Activity is currently running, it will run until the next\ntime it fails, completes, or times out, at which point the pause will kick in.\n\nIf the Activity is on its last retry attempt and fails, the failure will\nbe returned to the caller, just as if the Activity had not been paused.\n\nSpecify the Activity and Workflow IDs:\n\n```\ntemporal activity pause \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\n```\n\nTo later unpause the activity, see unpause. You may also want to\nreset the activity to unpause it while also starting it from the beginning." + s.Command.Long = "Pause an Activity. Not supported for Standalone Activities.\n\nIf the Activity is not currently running (e.g. because it previously\nfailed), it will not be run again until it is unpaused.\n\nHowever, if the Activity is currently running, it will run until the next\ntime it fails, completes, or times out, at which point the pause will kick in.\n\nPause does not stop or extend the Activity's Schedule-To-Close Timeout.\nA paused Activity can still time out. Use `temporal activity update-options`\nto extend timeout settings before a long pause.\n\nIf the Activity is on its last retry attempt and fails, the failure will\nbe returned to the caller, just as if the Activity had not been paused.\n\nSpecify the Activity and Workflow IDs:\n\n```\ntemporal activity pause \\\n --activity-id YourActivityId \\\n --workflow-id YourWorkflowId\n```\n\nTo later unpause the activity, see unpause. You may also want to\nreset the activity to unpause it while also starting it from the beginning." } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVarP(&s.ActivityId, "activity-id", "a", "", "The Activity ID to pause. Required.") @@ -2103,6 +2103,7 @@ func NewTemporalScheduleCommand(cctx *CommandContext, parent *TemporalCommand) * s.Command.AddCommand(&NewTemporalScheduleDeleteCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleDescribeCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleListCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalScheduleListMatchingTimesCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleToggleCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleTriggerCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleUpdateCommand(cctx, &s).Command) @@ -2270,6 +2271,39 @@ func NewTemporalScheduleListCommand(cctx *CommandContext, parent *TemporalSchedu return &s } +type TemporalScheduleListMatchingTimesCommand struct { + Parent *TemporalScheduleCommand + Command cobra.Command + ScheduleIdOptions + StartTime cliext.FlagTimestamp + EndTime cliext.FlagTimestamp +} + +func NewTemporalScheduleListMatchingTimesCommand(cctx *CommandContext, parent *TemporalScheduleCommand) *TemporalScheduleListMatchingTimesCommand { + var s TemporalScheduleListMatchingTimesCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "list-matching-times [flags]" + s.Command.Short = "List matching times for a Schedule (Experimental feature)" + if hasHighlighting { + s.Command.Long = "\nNote: This is an experimental feature and may change in the future.\n\nList the times a Schedule's spec would match within a given time\nrange. The time range may be in the past or future. Use this\ncommand to preview when a Schedule will take actions without\nactually running them.\n\nFor example:\n\n\x1b[1m temporal schedule list-matching-times \\\n --schedule-id \"YourScheduleId\" \\\n --start-time \"2024-01-01T00:00:00Z\" \\\n --end-time \"2024-01-31T23:59:59Z\"\x1b[0m" + } else { + s.Command.Long = "\nNote: This is an experimental feature and may change in the future.\n\nList the times a Schedule's spec would match within a given time\nrange. The time range may be in the past or future. Use this\ncommand to preview when a Schedule will take actions without\nactually running them.\n\nFor example:\n\n```\n temporal schedule list-matching-times \\\n --schedule-id \"YourScheduleId\" \\\n --start-time \"2024-01-01T00:00:00Z\" \\\n --end-time \"2024-01-31T23:59:59Z\"\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().Var(&s.StartTime, "start-time", "Start of time range to list matching times. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "start-time") + s.Command.Flags().Var(&s.EndTime, "end-time", "End of time range to list matching times. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "end-time") + s.ScheduleIdOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type TemporalScheduleToggleCommand struct { Parent *TemporalScheduleCommand Command cobra.Command @@ -2548,9 +2582,9 @@ func NewTemporalTaskQueueConfigSetCommand(cctx *CommandContext, parent *Temporal s.Command.Use = "set [flags]" s.Command.Short = "Set Task Queue configuration" if hasHighlighting { - s.Command.Long = "Update configuration settings for a Task Queue.\n\n\x1b[1mtemporal task-queue config set \\\n --task-queue YourTaskQueue \\\n --task-queue-type activity \\\n --namespace YourNamespace \\\n --queue-rps-limit \\\n --queue-rps-limit-reason \\\n --fairness-key-rps-limit-default \\\n --fairness-key-rps-limit-reason \\\n --fairness-key-weight-set HighPriority=2.0 \\\n --fairness-key-weight-set LowPriority=0.5\x1b[0m\n\nThis command supports updating:\n- Queue rate limits: Controls the overall rate limit of the task queue.\n This setting overrides the worker rate limit if set.\n Unless modified, this is the system-defined rate limit.\n- Fairness key rate limit defaults: Sets default rate limits for fairness keys.\n If set, each individual fairness key will be limited to this rate,\n scaled by the weight of the fairness key.\n- Fairness key weight overrides: Set custom weights for specific fairness keys.\n Weights control the relative share of capacity each key receives.\n\nTo unset a rate limit, pass in 'default', for example: --queue-rps-limit default\nTo unset specific fairness weights, use --fairness-key-weight-unset \nTo unset all fairness weights, use --fairness-key-weight-unset-all" + s.Command.Long = "Update configuration settings for a Task Queue.\n\n\x1b[1mtemporal task-queue config set \\\n --task-queue YourTaskQueue \\\n --task-queue-type activity \\\n --namespace YourNamespace \\\n --queue-rps-limit \\\n --queue-rps-limit-reason \\\n --fairness-key-rps-limit-default \\\n --fairness-key-rps-limit-reason \\\n --fairness-key-weight HighPriority=2.0 \\\n --fairness-key-weight LowPriority=0.5\x1b[0m\n\nThis command supports updating:\n- Queue rate limits: Controls the overall rate limit of the task queue.\n This setting overrides the worker rate limit if set.\n Unless modified, this is the system-defined rate limit.\n- Fairness key rate limit defaults: Sets default rate limits for fairness keys.\n If set, each individual fairness key will be limited to this rate,\n scaled by the weight of the fairness key.\n- Fairness key weight overrides: Set custom weights for specific fairness keys.\n Weights control the relative share of capacity each key receives.\n\nTo unset a rate limit, pass in 'default', for example: --queue-rps-limit default\nTo unset a specific fairness weight, use --fairness-key-weight =default\nTo unset all fairness weights, use --fairness-key-weight-clear-all" } else { - s.Command.Long = "Update configuration settings for a Task Queue.\n\n```\ntemporal task-queue config set \\\n --task-queue YourTaskQueue \\\n --task-queue-type activity \\\n --namespace YourNamespace \\\n --queue-rps-limit \\\n --queue-rps-limit-reason \\\n --fairness-key-rps-limit-default \\\n --fairness-key-rps-limit-reason \\\n --fairness-key-weight-set HighPriority=2.0 \\\n --fairness-key-weight-set LowPriority=0.5\n```\n\nThis command supports updating:\n- Queue rate limits: Controls the overall rate limit of the task queue.\n This setting overrides the worker rate limit if set.\n Unless modified, this is the system-defined rate limit.\n- Fairness key rate limit defaults: Sets default rate limits for fairness keys.\n If set, each individual fairness key will be limited to this rate,\n scaled by the weight of the fairness key.\n- Fairness key weight overrides: Set custom weights for specific fairness keys.\n Weights control the relative share of capacity each key receives.\n\nTo unset a rate limit, pass in 'default', for example: --queue-rps-limit default\nTo unset specific fairness weights, use --fairness-key-weight-unset \nTo unset all fairness weights, use --fairness-key-weight-unset-all" + s.Command.Long = "Update configuration settings for a Task Queue.\n\n```\ntemporal task-queue config set \\\n --task-queue YourTaskQueue \\\n --task-queue-type activity \\\n --namespace YourNamespace \\\n --queue-rps-limit \\\n --queue-rps-limit-reason \\\n --fairness-key-rps-limit-default \\\n --fairness-key-rps-limit-reason \\\n --fairness-key-weight HighPriority=2.0 \\\n --fairness-key-weight LowPriority=0.5\n```\n\nThis command supports updating:\n- Queue rate limits: Controls the overall rate limit of the task queue.\n This setting overrides the worker rate limit if set.\n Unless modified, this is the system-defined rate limit.\n- Fairness key rate limit defaults: Sets default rate limits for fairness keys.\n If set, each individual fairness key will be limited to this rate,\n scaled by the weight of the fairness key.\n- Fairness key weight overrides: Set custom weights for specific fairness keys.\n Weights control the relative share of capacity each key receives.\n\nTo unset a rate limit, pass in 'default', for example: --queue-rps-limit default\nTo unset a specific fairness weight, use --fairness-key-weight =default\nTo unset all fairness weights, use --fairness-key-weight-clear-all" } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") @@ -2644,6 +2678,8 @@ func NewTemporalTaskQueueGetBuildIdReachabilityCommand(cctx *CommandContext, par s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nShow if a given Build ID can be used for new, existing, or closed Workflows\nin Namespaces that support Worker versioning:\n\n```\ntemporal task-queue get-build-id-reachability \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\nYou can specify the `--build-id` and `--task-queue` flags multiple times. If\n`--task-queue` is omitted, the command checks Build ID reachability against\nall Task Queues." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringArrayVar(&s.BuildId, "build-id", nil, "One or more Build ID strings. Can be passed multiple times.") s.ReachabilityType = cliext.NewFlagStringEnum([]string{"open", "closed", "existing"}, "existing") s.Command.Flags().Var(&s.ReachabilityType, "reachability-type", "Reachability filter. `open`: reachable by one or more open workflows. `closed`: reachable by one or more closed workflows. `existing`: reachable by either. New Workflow Executions reachable by a Build ID are always reported. Accepted values: open, closed, existing.") @@ -2675,6 +2711,8 @@ func NewTemporalTaskQueueGetBuildIdsCommand(cctx *CommandContext, parent *Tempor s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nFetch sets of compatible Build IDs for specified Task Queues and display their\ninformation:\n\n```\ntemporal task-queue get-build-ids \\\n --task-queue YourTaskQueue\n```\n\nThis command is limited to Namespaces that support Worker versioning." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "task-queue") s.Command.Flags().IntVar(&s.MaxSets, "max-sets", 0, "Max return count. Use 1 for default major version. Use 0 for all sets.") @@ -2730,6 +2768,8 @@ func NewTemporalTaskQueueUpdateBuildIdsCommand(cctx *CommandContext, parent *Tem s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nAdd or change a Task Queue's compatible Build IDs for Namespaces using Worker\nversioning:\n\n```\ntemporal task-queue update-build-ids [subcommands] [options] \\\n --task-queue YourTaskQueue\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.AddCommand(&NewTemporalTaskQueueUpdateBuildIdsAddNewCompatibleCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueUpdateBuildIdsAddNewDefaultCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueUpdateBuildIdsPromoteIdInSetCommand(cctx, &s).Command) @@ -2792,6 +2832,8 @@ func NewTemporalTaskQueueUpdateBuildIdsAddNewDefaultCommand(cctx *CommandContext s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nCreate a new Task Queue Build ID set, add a Build ID to it, and make it the\noverall Task Queue default. The new set will be incompatible with previous\nsets and versions.\n\n```\ntemporal task-queue update-build-ids add-new-default \\\n --task-queue YourTaskQueue \\\n --build-id \"YourNewBuildId\"\n```\n\n```\n+------------------------------------------------------------------------+\n| NOTICE: This command is limited to Namespaces that support Worker |\n| versioning. Worker versioning is experimental. Versioning commands are |\n| subject to change. |\n+------------------------------------------------------------------------+\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Build ID to be added. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") @@ -2823,6 +2865,8 @@ func NewTemporalTaskQueueUpdateBuildIdsPromoteIdInSetCommand(cctx *CommandContex s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nEstablish an existing Build ID as the default in its Task Queue set. New tasks\ncompatible with this set will now be dispatched to this ID:\n\n```\ntemporal task-queue update-build-ids promote-id-in-set \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\n```\n+------------------------------------------------------------------------+\n| NOTICE: This command is limited to Namespaces that support Worker |\n| versioning. Worker versioning is experimental. Versioning commands are |\n| subject to change. |\n+------------------------------------------------------------------------+\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Build ID to set as default. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") @@ -2854,6 +2898,8 @@ func NewTemporalTaskQueueUpdateBuildIdsPromoteSetCommand(cctx *CommandContext, p s.Command.Long = "```\n+-----------------------------------------------------------------------------+\n| CAUTION: This command is deprecated and will be removed in a later release. |\n+-----------------------------------------------------------------------------+\n```\n\nPromote a Build ID set to be the default on a Task Queue. Identify the set by\nproviding a Build ID within it. If the set is already the default, this\ncommand has no effect:\n\n```\ntemporal task-queue update-build-ids promote-set \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\n```\n+------------------------------------------------------------------------+\n| NOTICE: This command is limited to Namespaces that support Worker |\n| versioning. Worker versioning is experimental. Versioning commands are |\n| subject to change. |\n+------------------------------------------------------------------------+\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This command is deprecated and will be removed in a later release." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Build ID within the promoted set. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.") @@ -2878,11 +2924,13 @@ func NewTemporalTaskQueueVersioningCommand(cctx *CommandContext, parent *Tempora s.Command.Use = "versioning" s.Command.Short = "Manage Task Queue Build ID handling (Deprecated)" if hasHighlighting { - s.Command.Long = "\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m\n\nProvides commands to add, list, remove, or replace Worker Build ID assignment\nand redirect rules associated with Task Queues:\n\n\x1b[1mtemporal task-queue versioning [subcommands] [options] \\\n --task-queue YourTaskQueue\x1b[0m\n\nTask Queues support the following versioning rules and policies:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nProvides commands to add, list, remove, or replace Worker Build ID assignment\nand redirect rules associated with Task Queues:\n\n\x1b[1mtemporal task-queue versioning [subcommands] [options] \\\n --task-queue YourTaskQueue\x1b[0m\n\nTask Queues support the following versioning rules and policies:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." } else { - s.Command.Long = "```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```\n\nProvides commands to add, list, remove, or replace Worker Build ID assignment\nand redirect rules associated with Task Queues:\n\n```\ntemporal task-queue versioning [subcommands] [options] \\\n --task-queue YourTaskQueue\n```\n\nTask Queues support the following versioning rules and policies:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nProvides commands to add, list, remove, or replace Worker Build ID assignment\nand redirect rules associated with Task Queues:\n\n```\ntemporal task-queue versioning [subcommands] [options] \\\n --task-queue YourTaskQueue\n```\n\nTask Queues support the following versioning rules and policies:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.AddCommand(&NewTemporalTaskQueueVersioningAddRedirectRuleCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueVersioningCommitBuildIdCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalTaskQueueVersioningDeleteAssignmentRuleCommand(cctx, &s).Command) @@ -2911,11 +2959,13 @@ func NewTemporalTaskQueueVersioningAddRedirectRuleCommand(cctx *CommandContext, s.Command.Use = "add-redirect-rule [flags]" s.Command.Short = "Add Task Queue redirect rules (Deprecated)" if hasHighlighting { - s.Command.Long = "Add a new redirect rule for a given Task Queue. You may add at most one\nredirect rule for each distinct source build ID:\n\n\x1b[1mtemporal task-queue versioning add-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourSourceBuildID\" \\\n --target-build-id \"YourTargetBuildID\"\x1b[0m\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nAdd a new redirect rule for a given Task Queue. You may add at most one\nredirect rule for each distinct source build ID:\n\n\x1b[1mtemporal task-queue versioning add-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourSourceBuildID\" \\\n --target-build-id \"YourTargetBuildID\"\x1b[0m" } else { - s.Command.Long = "Add a new redirect rule for a given Task Queue. You may add at most one\nredirect rule for each distinct source build ID:\n\n```\ntemporal task-queue versioning add-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourSourceBuildID\" \\\n --target-build-id \"YourTargetBuildID\"\n```\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nAdd a new redirect rule for a given Task Queue. You may add at most one\nredirect rule for each distinct source build ID:\n\n```\ntemporal task-queue versioning add-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourSourceBuildID\" \\\n --target-build-id \"YourTargetBuildID\"\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.SourceBuildId, "source-build-id", "", "Source build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "source-build-id") s.Command.Flags().StringVar(&s.TargetBuildId, "target-build-id", "", "Target build ID. Required.") @@ -2944,11 +2994,13 @@ func NewTemporalTaskQueueVersioningCommitBuildIdCommand(cctx *CommandContext, pa s.Command.Use = "commit-build-id [flags]" s.Command.Short = "Complete Build ID rollout (Deprecated)" if hasHighlighting { - s.Command.Long = "Complete a Build ID's rollout and clean up unnecessary rules that might have\nbeen created during a gradual rollout:\n\n\x1b[1mtemporal task-queue versioning commit-build-id \\\n --task-queue YourTaskQueue\n --build-id \"YourBuildId\"\x1b[0m\n\nThis command automatically applies the following atomic changes:\n\n- Adds an unconditional assignment rule for the target Build ID at the\n end of the list.\n- Removes all previously added assignment rules to the given target\n Build ID.\n- Removes any unconditional assignment rules for other Build IDs.\n\nRejects requests when there have been no recent pollers for this Build ID.\nThis prevents committing invalid Build IDs. Use the \x1b[1m--force\x1b[0m option to\noverride this validation.\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nComplete a Build ID's rollout and clean up unnecessary rules that might have\nbeen created during a gradual rollout:\n\n\x1b[1mtemporal task-queue versioning commit-build-id \\\n --task-queue YourTaskQueue\n --build-id \"YourBuildId\"\x1b[0m\n\nThis command automatically applies the following atomic changes:\n\n- Adds an unconditional assignment rule for the target Build ID at the\n end of the list.\n- Removes all previously added assignment rules to the given target\n Build ID.\n- Removes any unconditional assignment rules for other Build IDs.\n\nRejects requests when there have been no recent pollers for this Build ID.\nThis prevents committing invalid Build IDs. Use the \x1b[1m--force\x1b[0m option to\noverride this validation." } else { - s.Command.Long = "Complete a Build ID's rollout and clean up unnecessary rules that might have\nbeen created during a gradual rollout:\n\n```\ntemporal task-queue versioning commit-build-id \\\n --task-queue YourTaskQueue\n --build-id \"YourBuildId\"\n```\n\nThis command automatically applies the following atomic changes:\n\n- Adds an unconditional assignment rule for the target Build ID at the\n end of the list.\n- Removes all previously added assignment rules to the given target\n Build ID.\n- Removes any unconditional assignment rules for other Build IDs.\n\nRejects requests when there have been no recent pollers for this Build ID.\nThis prevents committing invalid Build IDs. Use the `--force` option to\noverride this validation.\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nComplete a Build ID's rollout and clean up unnecessary rules that might have\nbeen created during a gradual rollout:\n\n```\ntemporal task-queue versioning commit-build-id \\\n --task-queue YourTaskQueue\n --build-id \"YourBuildId\"\n```\n\nThis command automatically applies the following atomic changes:\n\n- Adds an unconditional assignment rule for the target Build ID at the\n end of the list.\n- Removes all previously added assignment rules to the given target\n Build ID.\n- Removes any unconditional assignment rules for other Build IDs.\n\nRejects requests when there have been no recent pollers for this Build ID.\nThis prevents committing invalid Build IDs. Use the `--force` option to\noverride this validation." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Target build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().BoolVar(&s.Force, "force", false, "Bypass recent-poller validation.") @@ -2976,11 +3028,13 @@ func NewTemporalTaskQueueVersioningDeleteAssignmentRuleCommand(cctx *CommandCont s.Command.Use = "delete-assignment-rule [flags]" s.Command.Short = "Removes a Task Queue assignment rule (Deprecated)" if hasHighlighting { - s.Command.Long = "Deletes a rule identified by its index in the Task Queue's list of assignment\nrules.\n\n\x1b[1mtemporal task-queue versioning delete-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index YourIntegerRuleIndex\x1b[0m\n\nBy default, the Task Queue must retain one unconditional rule, such as \"no\nhint filter\" or \"percentage\". Otherwise, the delete operation is rejected.\nUse the \x1b[1m--force\x1b[0m option to override this validation.\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nDeletes a rule identified by its index in the Task Queue's list of assignment\nrules.\n\n\x1b[1mtemporal task-queue versioning delete-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index YourIntegerRuleIndex\x1b[0m\n\nBy default, the Task Queue must retain one unconditional rule, such as \"no\nhint filter\" or \"percentage\". Otherwise, the delete operation is rejected.\nUse the \x1b[1m--force\x1b[0m option to override this validation." } else { - s.Command.Long = "Deletes a rule identified by its index in the Task Queue's list of assignment\nrules.\n\n```\ntemporal task-queue versioning delete-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index YourIntegerRuleIndex\n```\n\nBy default, the Task Queue must retain one unconditional rule, such as \"no\nhint filter\" or \"percentage\". Otherwise, the delete operation is rejected.\nUse the `--force` option to override this validation.\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nDeletes a rule identified by its index in the Task Queue's list of assignment\nrules.\n\n```\ntemporal task-queue versioning delete-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index YourIntegerRuleIndex\n```\n\nBy default, the Task Queue must retain one unconditional rule, such as \"no\nhint filter\" or \"percentage\". Otherwise, the delete operation is rejected.\nUse the `--force` option to override this validation." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().IntVarP(&s.RuleIndex, "rule-index", "i", 0, "Position of the assignment rule to be replaced. Requests for invalid indices will fail. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "rule-index") s.Command.Flags().BoolVar(&s.Force, "force", false, "Bypass one-unconditional-rule validation.") @@ -3007,11 +3061,13 @@ func NewTemporalTaskQueueVersioningDeleteRedirectRuleCommand(cctx *CommandContex s.Command.Use = "delete-redirect-rule [flags]" s.Command.Short = "Removes Build-ID routing rule (Deprecated)" if hasHighlighting { - s.Command.Long = "Deletes the routing rule for the given source Build ID.\n\n\x1b[1mtemporal task-queue versioning delete-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourBuildId\"\x1b[0m\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nDeletes the routing rule for the given source Build ID.\n\n\x1b[1mtemporal task-queue versioning delete-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourBuildId\"\x1b[0m" } else { - s.Command.Long = "Deletes the routing rule for the given source Build ID.\n\n```\ntemporal task-queue versioning delete-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourBuildId\"\n```\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nDeletes the routing rule for the given source Build ID.\n\n```\ntemporal task-queue versioning delete-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id \"YourBuildId\"\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.SourceBuildId, "source-build-id", "", "Source Build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "source-build-id") s.Command.Flags().BoolVarP(&s.Yes, "yes", "y", false, "Don't prompt to confirm.") @@ -3035,11 +3091,13 @@ func NewTemporalTaskQueueVersioningGetRulesCommand(cctx *CommandContext, parent s.Command.Use = "get-rules [flags]" s.Command.Short = "Fetch Worker Build ID assignments and redirect rules (Deprecated)" if hasHighlighting { - s.Command.Long = "Retrieve all the Worker Build ID assignments and redirect rules associated\nwith a Task Queue:\n\n\x1b[1mtemporal task-queue versioning get-rules \\\n --task-queue YourTaskQueue\x1b[0m\n\nTask Queues support the following versioning rules:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID.\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nRetrieve all the Worker Build ID assignments and redirect rules associated\nwith a Task Queue:\n\n\x1b[1mtemporal task-queue versioning get-rules \\\n --task-queue YourTaskQueue\x1b[0m\n\nTask Queues support the following versioning rules:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." } else { - s.Command.Long = "Retrieve all the Worker Build ID assignments and redirect rules associated\nwith a Task Queue:\n\n```\ntemporal task-queue versioning get-rules \\\n --task-queue YourTaskQueue\n```\n\nTask Queues support the following versioning rules:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID.\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nRetrieve all the Worker Build ID assignments and redirect rules associated\nwith a Task Queue:\n\n```\ntemporal task-queue versioning get-rules \\\n --task-queue YourTaskQueue\n```\n\nTask Queues support the following versioning rules:\n\n- Assignment Rules: manage how new executions are assigned to run on specific\n Worker Build IDs. Each Task Queue stores a list of ordered Assignment Rules,\n which are evaluated from first to last. Assignment Rules also allow for\n gradual rollout of new Build IDs by setting ramp percentage.\n- Redirect Rules: automatically assign work for a source Build ID to a target\n Build ID. You may add at most one redirect rule for each source Build ID.\n Redirect rules require that a target Build ID is fully compatible with\n the source Build ID." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { cctx.Options.Fail(err) @@ -3064,11 +3122,13 @@ func NewTemporalTaskQueueVersioningInsertAssignmentRuleCommand(cctx *CommandCont s.Command.Use = "insert-assignment-rule [flags]" s.Command.Short = "Add an assignment rule at a index (Deprecated)" if hasHighlighting { - s.Command.Long = "Inserts a new assignment rule for this Task Queue. Rules are evaluated in\norder, starting from index 0. The first applicable rule is applied, and the\nrest ignored:\n\n\x1b[1mtemporal task-queue versioning insert-assignment-rule \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\x1b[0m\n\nIf you do not specify a \x1b[1m--rule-index\x1b[0m, this command inserts at index 0.\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nInserts a new assignment rule for this Task Queue. Rules are evaluated in\norder, starting from index 0. The first applicable rule is applied, and the\nrest ignored:\n\n\x1b[1mtemporal task-queue versioning insert-assignment-rule \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\x1b[0m\n\nIf you do not specify a \x1b[1m--rule-index\x1b[0m, this command inserts at index 0." } else { - s.Command.Long = "Inserts a new assignment rule for this Task Queue. Rules are evaluated in\norder, starting from index 0. The first applicable rule is applied, and the\nrest ignored:\n\n```\ntemporal task-queue versioning insert-assignment-rule \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\nIf you do not specify a `--rule-index`, this command inserts at index 0.\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nInserts a new assignment rule for this Task Queue. Rules are evaluated in\norder, starting from index 0. The first applicable rule is applied, and the\nrest ignored:\n\n```\ntemporal task-queue versioning insert-assignment-rule \\\n --task-queue YourTaskQueue \\\n --build-id \"YourBuildId\"\n```\n\nIf you do not specify a `--rule-index`, this command inserts at index 0." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Target Build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().IntVarP(&s.RuleIndex, "rule-index", "i", 0, "Insertion position. Ranges from 0 (insert at start) to count (append). Any number greater than the count is treated as \"append\".") @@ -3099,11 +3159,13 @@ func NewTemporalTaskQueueVersioningReplaceAssignmentRuleCommand(cctx *CommandCon s.Command.Use = "replace-assignment-rule [flags]" s.Command.Short = "Update assignment rule at index (Deprecated)" if hasHighlighting { - s.Command.Long = "Change an assignment rule for this Task Queue. By default, this enforces one\nunconditional rule (no hint filter or percentage). Otherwise, the operation\nwill be rejected. Set \x1b[1mforce\x1b[0m to true to bypass this validation.\n\n\x1b[1mtemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\"\x1b[0m\n\nTo assign multiple assignment rules to a single Build ID, use\n'insert-assignment-rule'.\n\nTo update the percent:\n\n\x1b[1mtemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\" \\\n --percentage AnIntegerPercent\x1b[0m\n\nPercent may vary between 0 and 100 (default).\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nChange an assignment rule for this Task Queue. By default, this enforces one\nunconditional rule (no hint filter or percentage). Otherwise, the operation\nwill be rejected. Set \x1b[1mforce\x1b[0m to true to bypass this validation.\n\n\x1b[1mtemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\"\x1b[0m\n\nTo assign multiple assignment rules to a single Build ID, use\n'insert-assignment-rule'.\n\nTo update the percent:\n\n\x1b[1mtemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\" \\\n --percentage AnIntegerPercent\x1b[0m\n\nPercent may vary between 0 and 100 (default)." } else { - s.Command.Long = "Change an assignment rule for this Task Queue. By default, this enforces one\nunconditional rule (no hint filter or percentage). Otherwise, the operation\nwill be rejected. Set `force` to true to bypass this validation.\n\n```\ntemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\"\n```\n\nTo assign multiple assignment rules to a single Build ID, use\n'insert-assignment-rule'.\n\nTo update the percent:\n\n```\ntemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\" \\\n --percentage AnIntegerPercent\n```\n\nPercent may vary between 0 and 100 (default).\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nChange an assignment rule for this Task Queue. By default, this enforces one\nunconditional rule (no hint filter or percentage). Otherwise, the operation\nwill be rejected. Set `force` to true to bypass this validation.\n\n```\ntemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\"\n```\n\nTo assign multiple assignment rules to a single Build ID, use\n'insert-assignment-rule'.\n\nTo update the percent:\n\n```\ntemporal task-queue versioning replace-assignment-rule \\\n --task-queue YourTaskQueue \\\n --rule-index AnIntegerIndex \\\n --build-id \"YourBuildId\" \\\n --percentage AnIntegerPercent\n```\n\nPercent may vary between 0 and 100 (default)." } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "Target Build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "build-id") s.Command.Flags().IntVarP(&s.RuleIndex, "rule-index", "i", 0, "Position of the assignment rule to be replaced. Requests for invalid indices will fail. Required.") @@ -3134,11 +3196,13 @@ func NewTemporalTaskQueueVersioningReplaceRedirectRuleCommand(cctx *CommandConte s.Command.Use = "replace-redirect-rule [flags]" s.Command.Short = "Change the target for a Build ID's redirect (Deprecated)" if hasHighlighting { - s.Command.Long = "Updates a Build ID's redirect rule on a Task Queue by replacing its target\nBuild ID:\n\n\x1b[1mtemporal task-queue versioning replace-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id YourSourceBuildId \\\n --target-build-id YourNewTargetBuildId\x1b[0m\n\n\x1b[1m+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\x1b[0m" + s.Command.Long = "\x1b[1m+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\x1b[0m\n\nUpdates a Build ID's redirect rule on a Task Queue by replacing its target\nBuild ID:\n\n\x1b[1mtemporal task-queue versioning replace-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id YourSourceBuildId \\\n --target-build-id YourNewTargetBuildId\x1b[0m" } else { - s.Command.Long = "Updates a Build ID's redirect rule on a Task Queue by replacing its target\nBuild ID:\n\n```\ntemporal task-queue versioning replace-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id YourSourceBuildId \\\n --target-build-id YourNewTargetBuildId\n```\n\n```\n+---------------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+---------------------------------------------------------------------+\n```" + s.Command.Long = "```\n+-------------------------------------------------------------+\n| CAUTION: This API has been deprecated by Worker Deployment. |\n+-------------------------------------------------------------+\n```\n\nUpdates a Build ID's redirect rule on a Task Queue by replacing its target\nBuild ID:\n\n```\ntemporal task-queue versioning replace-redirect-rule \\\n --task-queue YourTaskQueue \\\n --source-build-id YourSourceBuildId \\\n --target-build-id YourNewTargetBuildId\n```" } s.Command.Args = cobra.NoArgs + s.Command.Annotations = make(map[string]string) + s.Command.Annotations["deprecationWarning"] = "This API has been deprecated by Worker Deployment." s.Command.Flags().StringVar(&s.SourceBuildId, "source-build-id", "", "Source Build ID. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "source-build-id") s.Command.Flags().StringVar(&s.TargetBuildId, "target-build-id", "", "Target Build ID. Required.") @@ -3756,9 +3820,9 @@ func NewTemporalWorkflowDeleteCommand(cctx *CommandContext, parent *TemporalWork s.Command.Use = "delete [flags]" s.Command.Short = "Remove Workflow Execution" if hasHighlighting { - s.Command.Long = "Delete a Workflow Executions and its Event History:\n\n\x1b[1mtemporal workflow delete \\\n --workflow-id YourWorkflowId\x1b[0m\n\nThe removal executes asynchronously. If the Execution is Running, the Service\nterminates it before deletion.\n\nVisit https://docs.temporal.io/visibility to read more about Search Attributes\nand Query creation. See \x1b[1mtemporal batch --help\x1b[0m for a quick reference." + s.Command.Long = "Delete a Workflow Execution and its Event History:\n\n\x1b[1mtemporal workflow delete \\\n --workflow-id YourWorkflowId\x1b[0m\n\nThe removal executes asynchronously. If the Execution is Running, the Service\nterminates it before deletion.\n\nWARNING: Deleting Workflow Executions in a global Namespace removes them from\nall replicas. Requests sent to a passive cluster are forwarded to the active\ncluster by default; to target the passive cluster directly, specify\n\x1b[1m--grpc-meta xdc-redirection=false\x1b[0m.\n\nVisit https://docs.temporal.io/visibility to read more about Search Attributes\nand Query creation. See \x1b[1mtemporal batch --help\x1b[0m for a quick reference." } else { - s.Command.Long = "Delete a Workflow Executions and its Event History:\n\n```\ntemporal workflow delete \\\n --workflow-id YourWorkflowId\n```\n\nThe removal executes asynchronously. If the Execution is Running, the Service\nterminates it before deletion.\n\nVisit https://docs.temporal.io/visibility to read more about Search Attributes\nand Query creation. See `temporal batch --help` for a quick reference." + s.Command.Long = "Delete a Workflow Execution and its Event History:\n\n```\ntemporal workflow delete \\\n --workflow-id YourWorkflowId\n```\n\nThe removal executes asynchronously. If the Execution is Running, the Service\nterminates it before deletion.\n\nWARNING: Deleting Workflow Executions in a global Namespace removes them from\nall replicas. Requests sent to a passive cluster are forwarded to the active\ncluster by default; to target the passive cluster directly, specify\n`--grpc-meta xdc-redirection=false`.\n\nVisit https://docs.temporal.io/visibility to read more about Search Attributes\nand Query creation. See `temporal batch --help` for a quick reference." } s.Command.Args = cobra.NoArgs s.SingleWorkflowOrBatchOptions.BuildFlags(s.Command.Flags()) diff --git a/internal/temporalcli/commands.go b/internal/temporalcli/commands.go index 116de4800..ecb52515d 100644 --- a/internal/temporalcli/commands.go +++ b/internal/temporalcli/commands.go @@ -497,6 +497,11 @@ func (c *TemporalCommand) initCommand(cctx *CommandContext) { } cctx.ActuallyRanCommand = true + // Print deprecation warning to stderr so it doesn't break JSON output + if msg, ok := cmd.Annotations["deprecationWarning"]; ok { + fmt.Fprintf(cctx.Options.Stderr, "warning: %s\n", strings.ToLower(strings.TrimRight(msg, "."))) + } + if cctx.Options.DeprecatedEnvConfig.EnvConfigName != "default" { if _, ok := cctx.DeprecatedEnvConfigValues[cctx.Options.DeprecatedEnvConfig.EnvConfigName]; !ok { if _, ok := cmd.Annotations["ignoresMissingEnv"]; !ok { diff --git a/internal/temporalcli/commands.help_test.go b/internal/temporalcli/commands.help_test.go index ebf6757cd..b3cd10c00 100644 --- a/internal/temporalcli/commands.help_test.go +++ b/internal/temporalcli/commands.help_test.go @@ -29,6 +29,21 @@ func TestHelp_Subcommand(t *testing.T) { assert.NoError(t, res.Err) } +func TestHelp_WithValueFlag(t *testing.T) { + h := NewCommandHarness(t) + + for _, args := range [][]string{ + {"workflow", "list", "--address", "123", "--help"}, + {"workflow", "list", "--help", "--address", "123"}, + {"workflow", "list", "--namespace", "foo", "--help"}, + } { + res := h.Execute(args...) + assert.Contains(t, res.Stdout.String(), "List Workflow Executions.") + assert.NotContains(t, res.Stderr.String(), "pflag: help requested") + assert.NoError(t, res.Err) + } +} + func TestHelp_HelpShowsAllFlag(t *testing.T) { h := NewCommandHarness(t) res := h.Execute("help", "--help") diff --git a/internal/temporalcli/commands.operator_search_attribute.go b/internal/temporalcli/commands.operator_search_attribute.go index aa0df7e38..b1919d9eb 100644 --- a/internal/temporalcli/commands.operator_search_attribute.go +++ b/internal/temporalcli/commands.operator_search_attribute.go @@ -2,6 +2,7 @@ package temporalcli import ( "fmt" + "slices" "strings" "github.com/fatih/color" @@ -130,12 +131,17 @@ func (c *TemporalOperatorSearchAttributeListCommand) run(cctx *CommandContext, a Type: saType.String(), }) } + slices.SortFunc(sas, func(a, b saNameType) int { return strings.Compare(a.Name, b.Name) }) + + var csa []saNameType for saName, saType := range resp.CustomAttributes { - sas = append(sas, saNameType{ + csa = append(csa, saNameType{ Name: saName, Type: saType.String(), }) } + slices.SortFunc(csa, func(a, b saNameType) int { return strings.Compare(a.Name, b.Name) }) + sas = append(sas, csa...) return cctx.Printer.PrintStructured(sas, printer.StructuredOptions{Table: &printer.TableOptions{}}) } diff --git a/internal/temporalcli/commands.schedule.go b/internal/temporalcli/commands.schedule.go index 5deadb8ec..3a18271bd 100644 --- a/internal/temporalcli/commands.schedule.go +++ b/internal/temporalcli/commands.schedule.go @@ -11,6 +11,7 @@ import ( "github.com/temporalio/cli/cliext" "github.com/temporalio/cli/internal/printer" "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/timestamppb" commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" @@ -606,3 +607,36 @@ func formatDuration(d time.Duration) string { s = strings.TrimSpace(s) return s } + +func (c *TemporalScheduleListMatchingTimesCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + res, err := cl.WorkflowService().ListScheduleMatchingTimes(cctx, &workflowservice.ListScheduleMatchingTimesRequest{ + Namespace: c.Parent.Namespace, + ScheduleId: c.ScheduleId, + StartTime: timestamppb.New(c.StartTime.Time()), + EndTime: timestamppb.New(c.EndTime.Time()), + }) + if err != nil { + return err + } + + if cctx.JSONOutput { + return cctx.Printer.PrintStructured(res, printer.StructuredOptions{}) + } + + type matchingTime struct { + Time string `json:"time"` + } + var rows []matchingTime + for _, t := range res.StartTime { + rows = append(rows, matchingTime{Time: t.AsTime().Format(time.RFC3339)}) + } + return cctx.Printer.PrintStructured(rows, printer.StructuredOptions{ + Table: &printer.TableOptions{}, + }) +} diff --git a/internal/temporalcli/commands.schedule_test.go b/internal/temporalcli/commands.schedule_test.go index d828e55ea..d4d089bf8 100644 --- a/internal/temporalcli/commands.schedule_test.go +++ b/internal/temporalcli/commands.schedule_test.go @@ -9,6 +9,7 @@ import ( "io" "math/rand" "regexp" + "strings" "time" "github.com/stretchr/testify/assert" @@ -553,3 +554,55 @@ func (s *SharedServerSuite) TestSchedule_Memo_Update() { return j.Schedule.Action.StartWorkflow.Memo.Fields.Bar.Data == "Mg==" }, 10*time.Second, 100*time.Millisecond) } + +func (s *SharedServerSuite) TestSchedule_ListMatchingTimes() { + // use a calendar spec with known hours so results are deterministic + schedId, _, res := s.createSchedule("--calendar", `{"hour":"3,6,9"}`) + s.NoError(res.Err) + + // query a full day - should match exactly 3 times + res = s.Execute( + "schedule", "list-matching-times", + "--address", s.Address(), + "-s", schedId, + "--start-time", "2025-01-01T00:00:00Z", + "--end-time", "2025-01-01T23:59:59Z", + ) + s.NoError(res.Err) + // text output should contain parseable RFC3339 timestamps + for _, line := range strings.Split(res.Stdout.String(), "\n") { + line = strings.TrimSpace(line) + if line == "" || line == "Time" { + continue + } + _, err := time.Parse(time.RFC3339, line) + s.NoError(err, "should parse text line as time: %q", line) + } + + // json output + res = s.Execute( + "schedule", "list-matching-times", + "--address", s.Address(), + "-s", schedId, + "--start-time", "2025-01-01T00:00:00Z", + "--end-time", "2025-01-01T23:59:59Z", + "-o", "json", + ) + s.NoError(res.Err) + var resp struct { + StartTime []string `json:"startTime"` + } + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &resp)) + s.Equal(3, len(resp.StartTime)) +} + +func (s *SharedServerSuite) TestSchedule_ListMatchingTimes_NotFound() { + res := s.Execute( + "schedule", "list-matching-times", + "--address", s.Address(), + "-s", "nonexistent-schedule-id", + "--start-time", "2025-01-01T00:00:00Z", + "--end-time", "2025-01-01T23:59:59Z", + ) + s.Error(res.Err) +} diff --git a/internal/temporalcli/commands.server.go b/internal/temporalcli/commands.server.go index ac216423f..bd45f911a 100644 --- a/internal/temporalcli/commands.server.go +++ b/internal/temporalcli/commands.server.go @@ -14,10 +14,16 @@ import ( var defaultDynamicConfigValues = map[string]any{ // Make search attributes immediately visible on creation, so users don't // have to wait for eventual consistency to happen when testing against the - // dev-server. Since it's a very rare thing to create search attributes, + // dev-server. Since it's a very rare thing to create search attributes, // we're comfortable that this is very unlikely to mask bugs in user code. "system.forceSearchAttributesCacheRefreshOnRead": true, + // Make Nexus endpoints immediately visible on creation, so users don't + // have to wait for eventual consistency to happen when testing against the + // dev-server. Since it's a very rare thing to create Nexus endpoints, + // we're comfortable that this is very unlikely to mask bugs in user code. + "system.forceNexusEndpointRefreshOnRead": true, + // Since we disable the SA cache, we need to bump max QPS accordingly. // These numbers were chosen to maintain the ratio between the two that's // established in the defaults. @@ -154,15 +160,20 @@ func (t *TemporalServerStartDevCommand) run(cctx *CommandContext, args []string) defer s.Stop() cctx.Printer.Printlnf("Temporal CLI %v\n", VersionString()) - cctx.Printer.Printlnf("%-17s %v:%v", "Temporal Server:", toFriendlyIp(opts.FrontendIP), opts.FrontendPort) + cctx.Printer.Printlnf("%-21s %v:%v", "Temporal Server:", toFriendlyIp(opts.FrontendIP), opts.FrontendPort) + if t.DbFilename == "" { + cctx.Printer.Printlnf("%-21s %v", "Temporal Persistence:", "in-memory") + } else { + cctx.Printer.Printlnf("%-21s %v", "Temporal Persistence:", t.DbFilename) + } // Only print HTTP port if explicitly provided to avoid promoting the unstable HTTP API. if opts.FrontendHTTPPort > 0 { - cctx.Printer.Printlnf("%-17s %v:%v", "Temporal HTTP:", toFriendlyIp(opts.FrontendIP), opts.FrontendHTTPPort) + cctx.Printer.Printlnf("%-21s %v:%v", "Temporal HTTP:", toFriendlyIp(opts.FrontendIP), opts.FrontendHTTPPort) } if !t.Headless { - cctx.Printer.Printlnf("%-17s http://%v:%v%v", "Temporal UI:", toFriendlyIp(opts.UIIP), opts.UIPort, opts.PublicPath) + cctx.Printer.Printlnf("%-21s http://%v:%v%v", "Temporal UI:", toFriendlyIp(opts.UIIP), opts.UIPort, opts.PublicPath) } - cctx.Printer.Printlnf("%-17s http://%v:%v/metrics", "Temporal Metrics:", toFriendlyIp(opts.FrontendIP), opts.MetricsPort) + cctx.Printer.Printlnf("%-21s http://%v:%v/metrics", "Temporal Metrics:", toFriendlyIp(opts.FrontendIP), opts.MetricsPort) <-cctx.Done() if !t.Parent.Parent.LogLevel.ChangedFromDefault { // The server routinely emits various warnings on shutdown. @@ -185,7 +196,7 @@ func persistentClusterID() string { file := defaultDeprecatedEnvConfigFile("temporalio", "version-info") if file == "" { // No file, can do nothing here - return uuid.NewString() + return "dev-server-" + uuid.NewString() } // Try to get existing first env, _ := readDeprecatedEnvConfigFile(file) @@ -193,7 +204,7 @@ func persistentClusterID() string { return id } // Create and try to write - id := uuid.NewString() + id := "dev-server-" + uuid.NewString() _ = writeDeprecatedEnvConfigFile(file, map[string]map[string]string{"default": {"cluster-id": id}}) return id } diff --git a/internal/temporalcli/commands.server_test.go b/internal/temporalcli/commands.server_test.go index 8c25558d9..26b92cf60 100644 --- a/internal/temporalcli/commands.server_test.go +++ b/internal/temporalcli/commands.server_test.go @@ -271,6 +271,93 @@ func TestServer_StartDev_WithSearchAttributes(t *testing.T) { } } +func TestServer_StartDev_BannerPersistenceInMemory(t *testing.T) { + h := NewCommandHarness(t) + defer h.Close() + + port := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) + httpPort := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) + resCh := make(chan *CommandResult, 1) + go func() { + resCh <- h.Execute("server", "start-dev", "-p", port, "--http-port", httpPort, "--headless") + }() + + // Wait until the server is dial-able, then cancel + var cl client.Client + h.EventuallyWithT(func(t *assert.CollectT) { + select { + case res := <-resCh: + require.NoError(t, res.Err) + require.Fail(t, "got early server result") + default: + } + var err error + cl, err = client.Dial(client.Options{HostPort: "127.0.0.1:" + port}) + assert.NoError(t, err) + }, 3*time.Second, 200*time.Millisecond) + defer cl.Close() + + h.CancelContext() + var res *CommandResult + select { + case <-time.After(20 * time.Second): + h.Fail("didn't cleanup after 20 seconds") + case res = <-resCh: + h.NoError(res.Err) + } + out := res.Stdout.String() + h.Contains(out, "Temporal Persistence:") + h.Contains(out, "in-memory") +} + +func TestServer_StartDev_BannerPersistenceFile(t *testing.T) { + h := NewCommandHarness(t) + defer h.Close() + + port := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) + httpPort := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) + // Use os.TempDir with an explicit defer-remove so Windows file-lock cleanup + // (os.RemoveAll in t.TempDir) does not race with a still-open SQLite handle. + dbFilename := filepath.Join(os.TempDir(), "devserver-banner-"+t.Name()+".sqlite") + t.Cleanup(func() { + _ = os.Remove(dbFilename) + _ = os.Remove(dbFilename + "-shm") + _ = os.Remove(dbFilename + "-wal") + }) + resCh := make(chan *CommandResult, 1) + go func() { + resCh <- h.Execute("server", "start-dev", "-p", port, "--http-port", httpPort, + "--headless", "--db-filename", dbFilename) + }() + + var cl client.Client + // File-backed server takes longer to start due to SQLite initialization. + h.EventuallyWithT(func(t *assert.CollectT) { + select { + case res := <-resCh: + require.NoError(t, res.Err) + require.Fail(t, "got early server result") + default: + } + var err error + cl, err = client.Dial(client.Options{HostPort: "127.0.0.1:" + port}) + assert.NoError(t, err) + }, 15*time.Second, 200*time.Millisecond) + defer cl.Close() + + h.CancelContext() + var res *CommandResult + select { + case <-time.After(20 * time.Second): + h.Fail("didn't cleanup after 20 seconds") + case res = <-resCh: + h.NoError(res.Err) + } + out := res.Stdout.String() + h.Contains(out, "Temporal Persistence:") + h.Contains(out, dbFilename) +} + type testLogger struct { t *testing.T } diff --git a/internal/temporalcli/commands.taskqueue_build_id_test.go b/internal/temporalcli/commands.taskqueue_build_id_test.go index 365bf9e1b..fb137ac28 100644 --- a/internal/temporalcli/commands.taskqueue_build_id_test.go +++ b/internal/temporalcli/commands.taskqueue_build_id_test.go @@ -180,3 +180,45 @@ func (s *SharedServerSuite) TestTaskQueue_BuildId() { }, }, jsonReachOut) } + +func (s *SharedServerSuite) TestDeprecatedCommand_WarnsOnStderr() { + taskQueue := uuid.NewString() + + // Run a deprecated command + res := s.Execute( + "task-queue", "update-build-ids", "add-new-default", + "--address", s.Address(), + "--task-queue", taskQueue, + "--build-id", "1.0", + ) + s.NoError(res.Err) + + // Deprecation warning must appear on stderr + stderr := res.Stderr.String() + s.Contains(stderr, "warning:") + s.Contains(stderr, "deprecated") + + // Warning must not appear on stdout (would break JSON consumers) + s.NotContains(res.Stdout.String(), "warning:") +} + +func (s *SharedServerSuite) TestDeprecatedCommand_WarnsOnStderr_JSON() { + taskQueue := uuid.NewString() + + // Run a deprecated command with JSON output + res := s.Execute( + "task-queue", "get-build-ids", + "-o", "json", + "--address", s.Address(), + "--task-queue", taskQueue, + ) + s.NoError(res.Err) + + // Deprecation warning must appear on stderr even with JSON output + stderr := res.Stderr.String() + s.Contains(stderr, "warning:") + s.Contains(stderr, "deprecated") + + // Stdout must be valid JSON, not polluted by warnings + s.NotContains(res.Stdout.String(), "warning:") +} diff --git a/internal/temporalcli/commands.taskqueue_test.go b/internal/temporalcli/commands.taskqueue_test.go index 6c5e3ba1a..7ca102c00 100644 --- a/internal/temporalcli/commands.taskqueue_test.go +++ b/internal/temporalcli/commands.taskqueue_test.go @@ -262,10 +262,6 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple() { ) s.NoError(res.Err) - // TODO(antlai-temporal): Delete when a server caching bug in 1.26.2 is fixed, - // see https://github.com/temporalio/temporal/pull/6978 - time.Sleep(1 * time.Second) - // Text res = s.Execute( "task-queue", "describe", @@ -279,10 +275,6 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple() { // No pollers on id1 s.NotContains(res.Stdout.String(), "now") - // TODO(antlai-temporal): Delete when a server caching bug in 1.26.2 is fixed, - // see https://github.com/temporalio/temporal/pull/6978 - time.Sleep(1 * time.Second) - res = s.Execute( "task-queue", "describe", "--select-unversioned", @@ -294,12 +286,8 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple() { s.NoError(res.Err) s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "unreachable") - s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "workflow", s.DevServer.Options.ClientOptions.Identity, "2 seconds ago", "100000") - s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "activity", s.DevServer.Options.ClientOptions.Identity, "2 seconds ago", "100000") - - // TODO(antlai-temporal): Delete when a server caching bug in 1.26.2 is fixed, - // see https://github.com/temporalio/temporal/pull/6978 - time.Sleep(1 * time.Second) + s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "workflow", s.DevServer.Options.ClientOptions.Identity, "now", "100000") + s.ContainsOnSameLine(res.Stdout.String(), "UNVERSIONED", "activity", s.DevServer.Options.ClientOptions.Identity, "now", "100000") res = s.Execute( "task-queue", "describe", @@ -315,10 +303,6 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple() { // No pollers on id2 s.NotContains(res.Stdout.String(), "now") - // TODO(antlai-temporal): Delete when a server caching bug in 1.26.2 is fixed, - // see https://github.com/temporalio/temporal/pull/6978 - time.Sleep(1 * time.Second) - res = s.Execute( "task-queue", "describe", "--select-all-active", diff --git a/internal/temporalcli/commands.worker.deployment_test.go b/internal/temporalcli/commands.worker.deployment_test.go index a3963dd23..8b97854e3 100644 --- a/internal/temporalcli/commands.worker.deployment_test.go +++ b/internal/temporalcli/commands.worker.deployment_test.go @@ -1199,6 +1199,10 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_EmptyComputeConfig } func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { + // Keep the fake AWS validation case local; otherwise the SDK tries EC2 IMDS + // during credential resolution and waits on network retries. + s.T().Setenv("AWS_EC2_METADATA_DISABLED", "true") + deploymentName := uuid.NewString() taskQueue := uuid.NewString() diff --git a/internal/temporalcli/commands.workflow.go b/internal/temporalcli/commands.workflow.go index 5a98d6ffe..e03047714 100644 --- a/internal/temporalcli/commands.workflow.go +++ b/internal/temporalcli/commands.workflow.go @@ -30,6 +30,8 @@ import ( const metadataQueryName = "__temporal_workflow_metadata" +const workflowDeleteWarning = "WARNING: Deleting Workflow Executions in a global Namespace removes them from all replicas. Requests sent to a passive cluster are forwarded to the active cluster by default; to target the passive cluster directly, specify `--grpc-meta xdc-redirection=false`." + func (c *TemporalWorkflowCancelCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.ClientOptions) if err != nil { @@ -69,13 +71,29 @@ func (c *TemporalWorkflowDeleteCommand) run(cctx *CommandContext, args []string) } defer cl.Close() - exec, batchReq, err := c.workflowExecOrBatch(cctx, c.Parent.Namespace, cl, singleOrBatchOverrides{}) + // Only warn when the namespace is global. + nsResp, nsErr := cl.WorkflowService().DescribeNamespace(cctx, &workflowservice.DescribeNamespaceRequest{ + Namespace: c.Parent.Namespace, + }) + if nsErr != nil || nsResp.GetIsGlobalNamespace() { + fmt.Fprintln(cctx.Options.Stderr, workflowDeleteWarning) + } + + exec, batchReq, err := c.workflowExecOrBatch(cctx, c.Parent.Namespace, cl, singleOrBatchOverrides{ + AllowYesWithWorkflowID: true, + }) // Run single or batch if err != nil { return err } else if exec != nil { - _, err := cl.WorkflowService().DeleteWorkflowExecution(cctx, &workflowservice.DeleteWorkflowExecutionRequest{ + yes, err := cctx.promptYes(workflowDeleteSingleConfirmationMessage(exec), c.Yes) + if err != nil { + return err + } else if !yes { + return fmt.Errorf("user denied confirmation") + } + _, err = cl.WorkflowService().DeleteWorkflowExecution(cctx, &workflowservice.DeleteWorkflowExecutionRequest{ Namespace: c.Parent.Namespace, WorkflowExecution: &common.WorkflowExecution{WorkflowId: c.WorkflowId, RunId: c.RunId}, }) @@ -96,6 +114,14 @@ func (c *TemporalWorkflowDeleteCommand) run(cctx *CommandContext, args []string) return nil } +func workflowDeleteSingleConfirmationMessage(exec *common.WorkflowExecution) string { + action := fmt.Sprintf("Delete Workflow %q", exec.GetWorkflowId()) + if exec.GetRunId() != "" { + action += fmt.Sprintf(" with Run ID %q", exec.GetRunId()) + } + return fmt.Sprintf("%s? y/N", action) +} + func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.ClientOptions) if err != nil { @@ -511,6 +537,7 @@ func defaultReason() string { type singleOrBatchOverrides struct { AllowReasonWithWorkflowID bool + AllowYesWithWorkflowID bool } func (s *SingleWorkflowOrBatchOptions) workflowExecOrBatch( @@ -525,7 +552,7 @@ func (s *SingleWorkflowOrBatchOptions) workflowExecOrBatch( return nil, nil, fmt.Errorf("cannot set query when workflow ID is set") } else if s.Reason != "" && !overrides.AllowReasonWithWorkflowID { return nil, nil, fmt.Errorf("cannot set reason when workflow ID is set") - } else if s.Yes { + } else if s.Yes && !overrides.AllowYesWithWorkflowID { return nil, nil, fmt.Errorf("cannot set 'yes' when workflow ID is set") } else if s.Rps != 0 { return nil, nil, fmt.Errorf("cannot set rps when workflow ID is set") @@ -542,13 +569,19 @@ func (s *SingleWorkflowOrBatchOptions) workflowExecOrBatch( return nil, nil, fmt.Errorf("cannot set run ID when query is set") } - // Count the workflows that will be affected - count, err := cl.CountWorkflow(cctx, &workflowservice.CountWorkflowExecutionsRequest{Query: s.Query}) - if err != nil { - return nil, nil, fmt.Errorf("failed counting workflows from query: %w", err) + // The count is only used in the confirmation prompt; skip the request when --yes + // bypasses it, so batch jobs can still proceed if the visibility API is timing out. + var promptMessage string + if s.Yes { + promptMessage = fmt.Sprintf("Start batch against workflows matching query %q? y/N", s.Query) + } else { + count, err := cl.CountWorkflow(cctx, &workflowservice.CountWorkflowExecutionsRequest{Query: s.Query}) + if err != nil { + return nil, nil, fmt.Errorf("failed counting workflows from query: %w", err) + } + promptMessage = fmt.Sprintf("Start batch against approximately %v workflow(s)? y/N", count.Count) } - yes, err := cctx.promptYes( - fmt.Sprintf("Start batch against approximately %v workflow(s)? y/N", count.Count), s.Yes) + yes, err := cctx.promptYes(promptMessage, s.Yes) if err != nil { return nil, nil, err } else if !yes { diff --git a/internal/temporalcli/commands.workflow_reset.go b/internal/temporalcli/commands.workflow_reset.go index 4c33065ce..aee402891 100644 --- a/internal/temporalcli/commands.workflow_reset.go +++ b/internal/temporalcli/commands.workflow_reset.go @@ -134,12 +134,19 @@ func (c *TemporalWorkflowResetCommand) runBatchResetWithPostOps(cctx *CommandCon PostResetOperations: postOps, }, } - count, err := cl.CountWorkflow(cctx, &workflowservice.CountWorkflowExecutionsRequest{Query: c.Query}) - if err != nil { - return fmt.Errorf("failed counting workflows from query: %w", err) + // The count is only used in the confirmation prompt; skip the request when --yes + // bypasses it, so batch jobs can still proceed if the visibility API is timing out. + var promptMessage string + if c.Yes { + promptMessage = fmt.Sprintf("Start batch against workflows matching query %q? y/N", c.Query) + } else { + count, err := cl.CountWorkflow(cctx, &workflowservice.CountWorkflowExecutionsRequest{Query: c.Query}) + if err != nil { + return fmt.Errorf("failed counting workflows from query: %w", err) + } + promptMessage = fmt.Sprintf("Start batch against approximately %v workflow(s)? y/N", count.Count) } - yes, err := cctx.promptYes( - fmt.Sprintf("Start batch against approximately %v workflow(s)? y/N", count.Count), c.Yes) + yes, err := cctx.promptYes(promptMessage, c.Yes) if err != nil { return err } diff --git a/internal/temporalcli/commands.workflow_reset_test.go b/internal/temporalcli/commands.workflow_reset_test.go index 33b3cc525..5dc87604c 100644 --- a/internal/temporalcli/commands.workflow_reset_test.go +++ b/internal/temporalcli/commands.workflow_reset_test.go @@ -927,3 +927,42 @@ func (sut *batchResetTestData) getWorkflowHistory() ([]*history.HistoryEvent, er return events, nil } + +func (s *SharedServerSuite) TestWorkflow_ResetBatch_SkipsCountWhenYes() { + s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { + ctx.Done().Receive(ctx, nil) + return nil, ctx.Err() + }) + + searchAttr := "keyword-" + uuid.NewString() + _, err := s.Client.ExecuteWorkflow( + s.Context, + client.StartWorkflowOptions{ + TaskQueue: s.Worker().Options.TaskQueue, + SearchAttributes: map[string]any{"CustomKeywordField": searchAttr}, + }, + DevWorkflow, + "ignored", + ) + s.NoError(err) + s.Eventually(func() bool { + resp, err := s.Client.ListWorkflow(s.Context, &workflowservice.ListWorkflowExecutionsRequest{ + Query: "CustomKeywordField = '" + searchAttr + "'", + }) + s.NoError(err) + return len(resp.Executions) == 1 + }, 3*time.Second, 100*time.Millisecond) + + res := s.Execute( + "workflow", "reset", + "--address", s.Address(), + "--query", "CustomKeywordField = '"+searchAttr+"'", + "--type", "FirstWorkflowTask", + "--reason", "skip-count-test", + "--yes", + ) + s.NoError(res.Err) + + s.NotContains(res.Stdout.String(), "approximately") + s.Contains(res.Stdout.String(), "matching query") +} diff --git a/internal/temporalcli/commands.workflow_test.go b/internal/temporalcli/commands.workflow_test.go index 5a11f3627..aedf331f7 100644 --- a/internal/temporalcli/commands.workflow_test.go +++ b/internal/temporalcli/commands.workflow_test.go @@ -7,6 +7,7 @@ import ( "math/rand" "regexp" "strconv" + "strings" "sync" "time" @@ -169,6 +170,44 @@ func (s *SharedServerSuite) testSignalBatchWorkflow(json bool) *CommandResult { return res } +func (s *SharedServerSuite) TestWorkflow_Delete_SingleWorkflowRequiresConfirmation() { + res := s.Execute( + "workflow", "delete", + "--address", s.Address(), + "--workflow-id", "delete-confirmation-test", + ) + s.EqualError(res.Err, "user denied confirmation") + // Dev-server's default namespace is non-global, so the warning is suppressed. + s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") + s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") +} + +func (s *SharedServerSuite) TestWorkflow_Delete_SingleWorkflowSuccess() { + s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { + ctx.Done().Receive(ctx, nil) + return nil, ctx.Err() + }) + + run, err := s.Client.ExecuteWorkflow( + s.Context, + client.StartWorkflowOptions{TaskQueue: s.Worker().Options.TaskQueue}, + DevWorkflow, + "ignored", + ) + s.NoError(err) + + res := s.Execute( + "workflow", "delete", + "--address", s.Address(), + "--workflow-id", run.GetID(), + "--yes", + ) + s.NoError(res.Err) + s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") + s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") + s.Contains(res.Stdout.String(), "Delete workflow succeeded") +} + func (s *SharedServerSuite) TestWorkflow_Delete_BatchWorkflowSuccess() { s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { ctx.Done().Receive(ctx, nil) @@ -212,6 +251,9 @@ func (s *SharedServerSuite) TestWorkflow_Delete_BatchWorkflowSuccess() { "-y", ) s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Start batch against workflows matching query") + s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") + s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") // Confirm workflows were deleted s.Eventually(func() bool { @@ -346,6 +388,51 @@ func (s *SharedServerSuite) TestWorkflow_Terminate_BatchWorkflowSuccess_JSON() { s.NotEmpty(jsonRes["batchJobId"]) } +func (s *SharedServerSuite) TestWorkflow_Terminate_BatchWorkflow_SkipsCountWhenYes() { + s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { + ctx.Done().Receive(ctx, nil) + return nil, ctx.Err() + }) + + searchAttr := "keyword-" + uuid.NewString() + run, err := s.Client.ExecuteWorkflow( + s.Context, + client.StartWorkflowOptions{ + TaskQueue: s.Worker().Options.TaskQueue, + SearchAttributes: map[string]any{"CustomKeywordField": searchAttr}, + }, + DevWorkflow, + "ignored", + ) + s.NoError(err) + s.Eventually(func() bool { + resp, err := s.Client.ListWorkflow(s.Context, &workflowservice.ListWorkflowExecutionsRequest{ + Query: "CustomKeywordField = '" + searchAttr + "'", + }) + s.NoError(err) + return len(resp.Executions) == 1 + }, 3*time.Second, 100*time.Millisecond) + + res := s.Execute( + "workflow", "terminate", + "--address", s.Address(), + "--query", "CustomKeywordField = '"+searchAttr+"'", + "--reason", "skip-count-test", + "--yes", + ) + s.NoError(res.Err) + + // Prompt text should show the query, not the count + s.NotContains(res.Stdout.String(), "approximately") + s.Contains(res.Stdout.String(), "matching query") + + // Confirm workflow was terminated + s.Eventually(func() bool { + err := run.Get(s.Context, nil) + return err != nil && strings.Contains(err.Error(), "terminated") + }, 5*time.Second, 100*time.Millisecond) +} + func (s *SharedServerSuite) testTerminateBatchWorkflow( total int, rps float32, diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 32af0d955..3014f6848 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -471,6 +471,10 @@ commands: However, if the Activity is currently running, it will run until the next time it fails, completes, or times out, at which point the pause will kick in. + Pause does not stop or extend the Activity's Schedule-To-Close Timeout. + A paused Activity can still time out. Use `temporal activity update-options` + to extend timeout settings before a long pause. + If the Activity is on its last retry attempt and fails, the failure will be returned to the caller, just as if the Activity had not been paused. @@ -2320,6 +2324,37 @@ commands: - overlap-policy - schedule-id + - name: temporal schedule list-matching-times + summary: List matching times for a Schedule (Experimental feature) + description: | + + Note: This is an experimental feature and may change in the future. + + List the times a Schedule's spec would match within a given time + range. The time range may be in the past or future. Use this + command to preview when a Schedule will take actions without + actually running them. + + For example: + + ``` + temporal schedule list-matching-times \ + --schedule-id "YourScheduleId" \ + --start-time "2024-01-01T00:00:00Z" \ + --end-time "2024-01-31T23:59:59Z" + ``` + options: + - name: start-time + type: timestamp + description: Start of time range to list matching times. + required: true + - name: end-time + type: timestamp + description: End of time range to list matching times. + required: true + option-sets: + - schedule-id + - name: temporal schedule create summary: Create a new Schedule description: | @@ -2839,14 +2874,9 @@ commands: configuration for the task queue. - name: temporal task-queue get-build-id-reachability - summary: Show Build ID availability (Deprecated) + summary: Show Build ID availability + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Show if a given Build ID can be used for new, existing, or closed Workflows in Namespaces that support Worker versioning: @@ -2886,14 +2916,9 @@ commands: Can be passed multiple times. - name: temporal task-queue get-build-ids - summary: Fetch Build ID versions (Deprecated) + summary: Fetch Build ID versions + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Fetch sets of compatible Build IDs for specified Task Queues and display their information: @@ -2935,14 +2960,9 @@ commands: short: t - name: temporal task-queue update-build-ids - summary: Manage Build IDs (Deprecated) + summary: Manage Build IDs + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Add or change a Task Queue's compatible Build IDs for Namespaces using Worker versioning: @@ -2987,14 +3007,9 @@ commands: description: Set the expanded Build ID set as the Task Queue default. - name: temporal task-queue update-build-ids add-new-default - summary: Set new default Build ID set (Deprecated) + summary: Set new default Build ID set + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Create a new Task Queue Build ID set, add a Build ID to it, and make it the overall Task Queue default. The new set will be incompatible with previous sets and versions. @@ -3024,14 +3039,9 @@ commands: short: t - name: temporal task-queue update-build-ids promote-id-in-set - summary: Set Build ID as set default (Deprecated) + summary: Set Build ID as set default + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Establish an existing Build ID as the default in its Task Queue set. New tasks compatible with this set will now be dispatched to this ID: @@ -3060,14 +3070,9 @@ commands: short: t - name: temporal task-queue update-build-ids promote-set - summary: Promote Build ID set (Deprecated) + summary: Promote Build ID set + deprecated: true description: | - ``` - +-----------------------------------------------------------------------------+ - | CAUTION: This command is deprecated and will be removed in a later release. | - +-----------------------------------------------------------------------------+ - ``` - Promote a Build ID set to be the default on a Task Queue. Identify the set by providing a Build ID within it. If the set is already the default, this command has no effect: @@ -3097,14 +3102,10 @@ commands: short: t - name: temporal task-queue versioning - summary: Manage Task Queue Build ID handling (Deprecated) + summary: Manage Task Queue Build ID handling + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` - Provides commands to add, list, remove, or replace Worker Build ID assignment and redirect rules associated with Task Queues: @@ -3131,7 +3132,9 @@ commands: required: true - name: temporal task-queue versioning add-redirect-rule - summary: Add Task Queue redirect rules (Deprecated) + summary: Add Task Queue redirect rules + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Add a new redirect rule for a given Task Queue. You may add at most one redirect rule for each distinct source build ID: @@ -3142,12 +3145,6 @@ commands: --source-build-id "YourSourceBuildID" \ --target-build-id "YourTargetBuildID" ``` - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: source-build-id type: string @@ -3163,7 +3160,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning commit-build-id - summary: Complete Build ID rollout (Deprecated) + summary: Complete Build ID rollout + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Complete a Build ID's rollout and clean up unnecessary rules that might have been created during a gradual rollout: @@ -3185,12 +3184,6 @@ commands: Rejects requests when there have been no recent pollers for this Build ID. This prevents committing invalid Build IDs. Use the `--force` option to override this validation. - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: build-id type: string @@ -3205,7 +3198,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning delete-assignment-rule - summary: Removes a Task Queue assignment rule (Deprecated) + summary: Removes a Task Queue assignment rule + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Deletes a rule identified by its index in the Task Queue's list of assignment rules. @@ -3219,12 +3214,6 @@ commands: By default, the Task Queue must retain one unconditional rule, such as "no hint filter" or "percentage". Otherwise, the delete operation is rejected. Use the `--force` option to override this validation. - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: rule-index type: int @@ -3242,7 +3231,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning delete-redirect-rule - summary: Removes Build-ID routing rule (Deprecated) + summary: Removes Build-ID routing rule + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Deletes the routing rule for the given source Build ID. @@ -3251,12 +3242,6 @@ commands: --task-queue YourTaskQueue \ --source-build-id "YourBuildId" ``` - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: source-build-id type: string @@ -3268,7 +3253,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning get-rules - summary: Fetch Worker Build ID assignments and redirect rules (Deprecated) + summary: Fetch Worker Build ID assignments and redirect rules + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Retrieve all the Worker Build ID assignments and redirect rules associated with a Task Queue: @@ -3288,14 +3275,11 @@ commands: Build ID. You may add at most one redirect rule for each source Build ID. Redirect rules require that a target Build ID is fully compatible with the source Build ID. - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` - name: temporal task-queue versioning insert-assignment-rule - summary: Add an assignment rule at a index (Deprecated) + summary: Add an assignment rule at a index + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Inserts a new assignment rule for this Task Queue. Rules are evaluated in order, starting from index 0. The first applicable rule is applied, and the @@ -3308,12 +3292,6 @@ commands: ``` If you do not specify a `--rule-index`, this command inserts at index 0. - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: build-id type: string @@ -3338,7 +3316,9 @@ commands: description: Don't prompt to confirm. - name: temporal task-queue versioning replace-assignment-rule - summary: Update assignment rule at index (Deprecated) + summary: Update assignment rule at index + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Change an assignment rule for this Task Queue. By default, this enforces one unconditional rule (no hint filter or percentage). Otherwise, the operation @@ -3365,12 +3345,6 @@ commands: ``` Percent may vary between 0 and 100 (default). - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: build-id type: string @@ -3397,7 +3371,9 @@ commands: description: Bypass the validation that one unconditional rule remains. - name: temporal task-queue versioning replace-redirect-rule - summary: Change the target for a Build ID's redirect (Deprecated) + summary: Change the target for a Build ID's redirect + deprecated: true + deprecation-message: This API has been deprecated by Worker Deployment. description: | Updates a Build ID's redirect rule on a Task Queue by replacing its target Build ID: @@ -3408,12 +3384,6 @@ commands: --source-build-id YourSourceBuildId \ --target-build-id YourNewTargetBuildId ``` - - ``` - +---------------------------------------------------------------------+ - | CAUTION: This API has been deprecated by Worker Deployment. | - +---------------------------------------------------------------------+ - ``` options: - name: source-build-id type: string @@ -3492,8 +3462,8 @@ commands: --queue-rps-limit-reason \ --fairness-key-rps-limit-default \ --fairness-key-rps-limit-reason \ - --fairness-key-weight-set HighPriority=2.0 \ - --fairness-key-weight-set LowPriority=0.5 + --fairness-key-weight HighPriority=2.0 \ + --fairness-key-weight LowPriority=0.5 ``` This command supports updating: @@ -3507,8 +3477,8 @@ commands: Weights control the relative share of capacity each key receives. To unset a rate limit, pass in 'default', for example: --queue-rps-limit default - To unset specific fairness weights, use --fairness-key-weight-unset - To unset all fairness weights, use --fairness-key-weight-unset-all + To unset a specific fairness weight, use --fairness-key-weight =default + To unset all fairness weights, use --fairness-key-weight-clear-all options: - name: task-queue type: string @@ -3665,7 +3635,7 @@ commands: - name: temporal workflow delete summary: Remove Workflow Execution description: | - Delete a Workflow Executions and its Event History: + Delete a Workflow Execution and its Event History: ``` temporal workflow delete \ @@ -3675,6 +3645,11 @@ commands: The removal executes asynchronously. If the Execution is Running, the Service terminates it before deletion. + WARNING: Deleting Workflow Executions in a global Namespace removes them from + all replicas. Requests sent to a passive cluster are forwarded to the active + cluster by default; to target the passive cluster directly, specify + `--grpc-meta xdc-redirection=false`. + Visit https://docs.temporal.io/visibility to read more about Search Attributes and Query creation. See `temporal batch --help` for a quick reference. option-sets: