diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4ebbf0d4e..7226d8733 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,9 +6,9 @@ updates: interval: weekly cooldown: default-days: 14 + allow: + - dependency-type: direct ignore: - - dependency-name: "*" - dependency-type: indirect # Temporal dependencies are managed manually to ensure coordinated upgrades across all temporal packages - dependency-name: "go.temporal.io/*" diff --git a/.github/workflows/build-and-publish-docker.yml b/.github/workflows/build-and-publish-docker.yml index f6bc513ab..502c103d1 100644 --- a/.github/workflows/build-and-publish-docker.yml +++ b/.github/workflows/build-and-publish-docker.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -36,7 +36,7 @@ jobs: env: INPUT_VERSION: ${{ inputs.version }} INPUT_TAG_LATEST: ${{ inputs.tag_latest }} - uses: actions/github-script@v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const inputVersion = process.env.INPUT_VERSION; @@ -82,14 +82,14 @@ jobs: ls -lh dist/arm64/temporal - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub if: inputs.publish - uses: docker/login-action@v3 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3a9ee87a3..e6728fd07 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,8 @@ name: Continuous Integration on: pull_request: + merge_group: + types: [checks_requested] push: branches: - main @@ -12,14 +14,14 @@ permissions: jobs: validate-server-version: name: Validate Server Version - if: github.event_name == 'push' || github.base_ref == 'main' + if: github.event_name == 'push' || github.event_name == 'merge_group' || github.base_ref == 'main' 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 +55,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 +74,7 @@ jobs: run: gotestsum --junitfile junit-xml/${{matrix.os}}.xml -- ./... - name: Upload junit-xml artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() with: name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--${{matrix.os}} @@ -108,7 +110,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 +119,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..118c44fb1 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -2,6 +2,8 @@ name: Govulncheck on: pull_request: + merge_group: + types: [checks_requested] permissions: contents: read @@ -11,8 +13,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/cliext/client.go b/cliext/client.go index db6506078..48eaa7508 100644 --- a/cliext/client.go +++ b/cliext/client.go @@ -29,6 +29,11 @@ type ClientOptionsBuilder struct { // Logger is the slog logger to use for the client. If set, it will be // wrapped with the SDK's structured logger adapter. Logger *slog.Logger + + // PayloadCodec is populated by Build when a remote payload codec is + // configured. Callers can use it to decode payloads outside the gRPC + // interceptor chain (e.g. payloads nested inside opaque proto bytes). + PayloadCodec converter.PayloadCodec } type oauthCredentials struct { @@ -127,6 +132,11 @@ func (b *ClientOptionsBuilder) Build(ctx context.Context) (client.Options, error profile.APIKey = cfg.ApiKey } + // Set client authority on profile if provided + if cfg.ClientAuthority != "" { + profile.Authority = cfg.ClientAuthority + } + // Handle gRPC metadata from flags. if len(cfg.GrpcMeta) > 0 { grpcMetaFromArg, err := parseKeyValuePairs(cfg.GrpcMeta) @@ -248,13 +258,18 @@ func (b *ClientOptionsBuilder) Build(ctx context.Context) (client.Options, error if err != nil { return client.Options{}, fmt.Errorf("invalid codec headers: %w", err) } - interceptor, err := newPayloadCodecInterceptor( + payloadCodec := newRemotePayloadCodec( profile.Namespace, profile.Codec.Endpoint, profile.Codec.Auth, codecHeaders) + interceptor, err := converter.NewPayloadCodecGRPCClientInterceptor( + converter.PayloadCodecGRPCClientInterceptorOptions{ + Codecs: []converter.PayloadCodec{payloadCodec}, + }) if err != nil { return client.Options{}, fmt.Errorf("failed creating payload codec interceptor: %w", err) } clientOpts.ConnectionOptions.DialOptions = append( clientOpts.ConnectionOptions.DialOptions, grpc.WithChainUnaryInterceptor(interceptor)) + b.PayloadCodec = payloadCodec } // Set connect timeout for GetSystemInfo if provided. @@ -278,16 +293,17 @@ func parseKeyValuePairs(pairs []string) (map[string]string, error) { return result, nil } -// newPayloadCodecInterceptor creates a gRPC interceptor for remote payload codec. -func newPayloadCodecInterceptor( +// newRemotePayloadCodec constructs a remote payload codec from the configured endpoint, +// auth, and headers. The returned codec can be used both inside a gRPC interceptor and +// to decode payloads nested inside opaque proto bytes (e.g. system Nexus operation inputs). +func newRemotePayloadCodec( namespace string, codecEndpoint string, codecAuth string, codecHeaders map[string]string, -) (grpc.UnaryClientInterceptor, error) { +) converter.PayloadCodec { codecEndpoint = strings.ReplaceAll(codecEndpoint, "{namespace}", namespace) - - payloadCodec := converter.NewRemotePayloadCodec( + return converter.NewRemotePayloadCodec( converter.RemotePayloadCodecOptions{ Endpoint: codecEndpoint, ModifyRequest: func(req *http.Request) error { @@ -302,11 +318,6 @@ func newPayloadCodecInterceptor( }, }, ) - return converter.NewPayloadCodecGRPCClientInterceptor( - converter.PayloadCodecGRPCClientInterceptorOptions{ - Codecs: []converter.PayloadCodec{payloadCodec}, - }, - ) } func (c *oauthCredentials) getToken(ctx context.Context) (string, error) { diff --git a/cliext/config.oauth.go b/cliext/config.oauth.go index 9593bdde0..d98b9011a 100644 --- a/cliext/config.oauth.go +++ b/cliext/config.oauth.go @@ -141,11 +141,7 @@ func resolveConfigAndProfile(configFilePath, profileName string, envLookup envco configFilePath, _ = envLookup.LookupEnv("TEMPORAL_CONFIG_FILE") } if configFilePath == "" { - var err error - configFilePath, err = envconfig.DefaultConfigFilePath() - if err != nil { - return "", "", fmt.Errorf("failed to get default config path: %w", err) - } + configFilePath = envconfig.DefaultConfigFilePath() } // Resolve profile name. diff --git a/cmd/gen-docs/main.go b/cmd/gen-docs/main.go index 11ea430e0..f6825f328 100644 --- a/cmd/gen-docs/main.go +++ b/cmd/gen-docs/main.go @@ -10,7 +10,8 @@ import ( "github.com/temporalio/cli/internal/commandsgen" ) -// stringSlice implements flag.Value to support multiple -input flags +// stringSlice implements flag.Value to support flags that may be specified +// multiple times (e.g. -input, -subdir). type stringSlice []string func (s *stringSlice) String() string { @@ -32,10 +33,12 @@ func run() error { var ( outputDir string inputFiles stringSlice + subdirs stringSlice ) flag.Var(&inputFiles, "input", "Input YAML file (can be specified multiple times)") flag.StringVar(&outputDir, "output", ".", "Output directory for docs") + flag.Var(&subdirs, "subdir", "Write the subcommands of this command into a subdirectory of separate files instead of a single file (can be specified multiple times)") flag.Parse() if len(inputFiles) == 0 { @@ -60,13 +63,18 @@ func run() error { return fmt.Errorf("failed parsing YAML: %w", err) } - docs, err := commandsgen.GenerateDocsFiles(cmds) + docs, err := commandsgen.GenerateDocsFiles(cmds, subdirs) if err != nil { return fmt.Errorf("failed generating docs: %w", err) } for filename, content := range docs { filePath := filepath.Join(outputDir, filename+".mdx") + // Filenames may contain a path separator (e.g. "cloud/namespace") when + // -subdir is used, so ensure the parent directory exists. + if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil { + return fmt.Errorf("failed creating directory for %s: %w", filePath, err) + } if err := os.WriteFile(filePath, content, 0644); err != nil { return fmt.Errorf("failed writing %s: %w", filePath, err) } diff --git a/go.mod b/go.mod index 39af1d4ff..02ef784dd 100644 --- a/go.mod +++ b/go.mod @@ -1,117 +1,128 @@ module github.com/temporalio/cli -go 1.26.2 +go 1.26.4 require ( github.com/BurntSushi/toml v1.4.0 github.com/alitto/pond v1.9.2 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/dustin/go-humanize v1.0.1 - github.com/fatih/color v1.18.0 + github.com/fatih/color v1.19.0 github.com/google/uuid v1.6.0 - github.com/mattn/go-isatty v0.0.20 + github.com/mattn/go-isatty v0.0.21 github.com/nexus-rpc/sdk-go v0.6.0 github.com/olekukonko/tablewriter v0.0.5 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 github.com/temporalio/cli/cliext v0.0.0 - github.com/temporalio/ui-server/v2 v2.49.1 - go.temporal.io/api v1.62.8 - go.temporal.io/sdk v1.41.1 - go.temporal.io/sdk/contrib/envconfig v1.0.0 - go.temporal.io/server v1.31.0 - golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 + github.com/temporalio/ui-server/v2 v2.52.0 + go.temporal.io/api v1.63.0 + go.temporal.io/sdk v1.44.1 + go.temporal.io/sdk/contrib/envconfig v1.0.2 + go.temporal.io/server v1.32.0-158.0 + golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f golang.org/x/mod v0.35.0 - golang.org/x/term v0.41.0 - golang.org/x/tools v0.43.0 - google.golang.org/grpc v1.79.3 - google.golang.org/protobuf v1.36.10 + golang.org/x/term v0.43.0 + golang.org/x/tools v0.44.0 + google.golang.org/grpc v1.81.1 + google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - modernc.org/sqlite v1.44.3 + modernc.org/sqlite v1.51.0 ) replace github.com/temporalio/cli/cliext => ./cliext require ( cel.dev/expr v0.25.1 // indirect - cloud.google.com/go v0.121.6 // indirect - cloud.google.com/go/auth v0.17.0 // indirect + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.20.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect - cloud.google.com/go/iam v1.5.3 // indirect - cloud.google.com/go/longrunning v0.7.0 // indirect - cloud.google.com/go/monitoring v1.24.2 // indirect - cloud.google.com/go/run v1.15.0 // indirect - cloud.google.com/go/storage v1.56.0 // indirect - dario.cat/mergo v1.0.1 // indirect - filippo.io/edwards25519 v1.1.1 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect + cloud.google.com/go/iam v1.9.0 // indirect + cloud.google.com/go/longrunning v0.11.0 // indirect + cloud.google.com/go/monitoring v1.27.0 // indirect + cloud.google.com/go/run v1.19.0 // indirect + cloud.google.com/go/storage v1.62.1 // indirect + dario.cat/mergo v1.0.2 // indirect + filippo.io/edwards25519 v1.2.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.56.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.56.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect - github.com/apache/thrift v0.21.0 // indirect - github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.13 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.13 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect - github.com/aws/aws-sdk-go-v2/service/ecs v1.72.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect - github.com/aws/aws-sdk-go-v2/service/lambda v1.88.0 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.98.0 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect - github.com/aws/smithy-go v1.24.2 // indirect + github.com/apache/thrift v0.23.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.6 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.16 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.15 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 // indirect + github.com/aws/aws-sdk-go-v2/service/ecs v1.78.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22 // indirect + github.com/aws/aws-sdk-go-v2/service/lambda v1.89.1 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.99.1 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 // indirect + github.com/aws/smithy-go v1.25.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c // indirect github.com/cactus/go-statsd-client/v5 v5.1.0 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect + github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect github.com/coreos/go-oidc/v3 v3.13.0 // indirect github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.1 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-sql-driver/mysql v1.9.1 // indirect + github.com/go-openapi/jsonpointer v0.23.1 // indirect + github.com/go-openapi/jsonreference v0.21.5 // indirect + github.com/go-openapi/swag v0.26.0 // indirect + github.com/go-openapi/swag/cmdutils v0.26.0 // indirect + github.com/go-openapi/swag/conv v0.26.0 // indirect + github.com/go-openapi/swag/fileutils v0.26.0 // indirect + github.com/go-openapi/swag/jsonname v0.26.0 // indirect + github.com/go-openapi/swag/jsonutils v0.26.0 // indirect + github.com/go-openapi/swag/loading v0.26.0 // indirect + github.com/go-openapi/swag/mangling v0.26.0 // indirect + github.com/go-openapi/swag/netutils v0.26.0 // indirect + github.com/go-openapi/swag/stringutils v0.26.0 // indirect + github.com/go-openapi/swag/typeutils v0.26.0 // indirect + github.com/go-openapi/swag/yamlutils v0.26.0 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/gocql/gocql v1.7.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/mock v1.7.0-rc.1 // indirect github.com/golang/snappy v1.0.0 // indirect - github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b // indirect - github.com/google/gnostic-models v0.7.0 // indirect + github.com/gomarkdown/markdown v0.0.0-20260411013819-759bbc3e3207 // indirect + github.com/google/gnostic-models v0.7.1 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect - github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect + github.com/googleapis/gax-go/v2 v2.22.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect @@ -123,13 +134,13 @@ require ( github.com/jmoiron/sqlx v1.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/compress v1.18.5 // indirect github.com/labstack/echo/v4 v4.13.4 // indirect github.com/labstack/gommon v0.4.2 // indirect - github.com/lib/pq v1.10.9 // indirect - github.com/mailru/easyjson v0.9.0 // indirect + github.com/lib/pq v1.12.3 // indirect + github.com/mailru/easyjson v0.9.2 // indirect github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-runewidth v0.0.23 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -137,6 +148,7 @@ require ( github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect + github.com/nexus-rpc/nexus-proto-annotations v0.1.0 // indirect github.com/olivere/elastic/v7 v7.0.32 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -145,21 +157,20 @@ require ( github.com/prometheus/client_golang v1.21.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.16.0 // indirect - github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/prometheus/procfs v0.20.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron v1.2.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect github.com/sony/gobreaker v1.0.0 // indirect - github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cast v1.10.0 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/objx v0.5.3 // indirect github.com/temporalio/ringpop-go v0.0.0-20250130211428-b97329e994f7 // indirect github.com/temporalio/sqlparser v0.0.0-20231115171017-f4060bcfa6cb // indirect - github.com/temporalio/tchannel-go v1.22.1-0.20240528171429-1db37fdea938 // indirect + github.com/temporalio/tchannel-go v1.22.1-0.20260129151045-8706a1ab5f61 // indirect github.com/tidwall/btree v1.8.1 // indirect github.com/twmb/murmur3 v1.1.8 // indirect github.com/uber-common/bark v1.3.0 // indirect @@ -168,53 +179,53 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.57.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect go.opentelemetry.io/otel/sdk v1.43.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect - go.opentelemetry.io/proto/otlp v1.7.1 // indirect - go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2 // indirect + go.opentelemetry.io/proto/otlp v1.10.0 // indirect + go.temporal.io/auto-scaled-workers v0.0.0-20260622220320-9b1e3849116d // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/fx v1.24.0 // indirect go.uber.org/mock v0.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/net v0.52.0 // indirect - golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.14.0 // indirect - google.golang.org/api v0.256.0 // indirect - google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/text v0.37.0 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/api v0.276.0 // indirect + google.golang.org/genproto v0.0.0-20260420184626-e10c466a9529 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/validator.v2 v2.0.1 // indirect - k8s.io/api v0.35.1 // indirect - k8s.io/apimachinery v0.35.1 // indirect - k8s.io/client-go v0.35.1 // indirect - k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect - k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect - modernc.org/libc v1.67.6 // indirect + k8s.io/api v0.35.4 // indirect + k8s.io/apimachinery v0.35.4 // indirect + k8s.io/client-go v0.35.4 // indirect + k8s.io/klog/v2 v2.140.0 // indirect + k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f // indirect + k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 // indirect + modernc.org/libc v1.72.3 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.4.0 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index cc18ea5d4..5f544e484 100644 --- a/go.sum +++ b/go.sum @@ -1,45 +1,45 @@ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= -cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= -cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= -cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= +cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= -cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= -cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= -cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= -cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E= -cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY= -cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= -cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= -cloud.google.com/go/run v1.15.0 h1:4cwyNv9SUQEsQOf5/DfPKyMWYSA52p38/o119BgMhO4= -cloud.google.com/go/run v1.15.0/go.mod h1:rgFHMdAopLl++57vzeqA+a1o2x0/ILZnEacRD6nC0EA= -cloud.google.com/go/storage v1.56.0 h1:iixmq2Fse2tqxMbWhLWC9HfBj1qdxqAmiK8/eqtsLxI= -cloud.google.com/go/storage v1.56.0/go.mod h1:Tpuj6t4NweCLzlNbw9Z9iwxEkrSem20AetIeH/shgVU= -cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= -cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +cloud.google.com/go/iam v1.9.0 h1:89wyjxT6DL4b5rk/Nk8eBC9DHqf+JiMstrn5IEYxFw4= +cloud.google.com/go/iam v1.9.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4= +cloud.google.com/go/logging v1.16.0 h1:MMNgYRvZ/pEwiNSkcoJTKWfAbAJDqCqAMJiarZx+/CI= +cloud.google.com/go/logging v1.16.0/go.mod h1:ZGKnpBaURITh+g/uom2VhbiFoFWvejcrHPDhxFtU/gI= +cloud.google.com/go/longrunning v0.11.0 h1:fE4XVLJQj+gRnw1HrbDyQXXgC0aiqY3wxP7DDU4cWk0= +cloud.google.com/go/longrunning v0.11.0/go.mod h1:8nqFBPOO1U/XkhWl0I19AMZEphrHi73VNABIpKYaTwM= +cloud.google.com/go/monitoring v1.27.0 h1:BhYwMqao+e5Nn7JtWMM9m6zRtKtVUK6kJWMizXChkLU= +cloud.google.com/go/monitoring v1.27.0/go.mod h1:72NOVjJXHY/HBfoLT0+qlCZBT059+9VXLeAnL2PeeVM= +cloud.google.com/go/run v1.19.0 h1:kjXZKDwrUOeUYDd7/0TZ/iKsG3rJ3Lq3cyksTspcNSU= +cloud.google.com/go/run v1.19.0/go.mod h1:Z5wHbyFirI8XU48EPs5XJf/qmVm1SXZEhuS8EvZOuQU= +cloud.google.com/go/storage v1.62.1 h1:Os0G3XbUbjZumkpDUf2Y0rLoXJTCF1kU2kWUujKYXD8= +cloud.google.com/go/storage v1.62.1/go.mod h1:cpYz/kRVZ+UQAF1uHeea10/9ewcRbxGoGNKsS9daSXA= +cloud.google.com/go/trace v1.14.0 h1:jUtnmOrNcu5XJNk4Gz0fv+v5sM0weaOa3z5MPQUjRXs= +cloud.google.com/go/trace v1.14.0/go.mod h1:r+bdAn16dKLSV1G2D5v3e58IlQlizfxWrUfjx7kM7X0= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw= -filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.56.0 h1:O2sXMyJh8b7devAGdE+163xtRurt0RVpB6DIzX5vGfg= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.56.0/go.mod h1:hEpiGU18xf70qb3jbTcIggWAiEfX/cOIVc2OTe4OegA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.56.0 h1:ZIT85vKP7LBS84XJ0WdJ3dPOX3iz4j3c0+lpajGQMyo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.56.0/go.mod h1:rqP9UEhOXv9WhQ7Gjz+G5y/pf8+BJZW5/Ts0AhE0PwE= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.56.0 h1:0YP0+/ixwu+Uqeu/FGiBZNQ19huiUxxiPXIc9WsLKuQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.56.0/go.mod h1:6ZZMQhZKDvUvkJw2rc+oDP90tMMzuU/J+5HG1ZmPOmE= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -51,50 +51,48 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3 github.com/alitto/pond v1.9.2 h1:9Qb75z/scEZVCoSU+osVmQ0I0JOeLfdTDafrbcJ8CLs= github.com/alitto/pond v1.9.2/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= -github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= -github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= -github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= -github.com/aws/aws-sdk-go-v2/config v1.32.13 h1:5KgbxMaS2coSWRrx9TX/QtWbqzgQkOdEa3sZPhBhCSg= -github.com/aws/aws-sdk-go-v2/config v1.32.13/go.mod h1:8zz7wedqtCbw5e9Mi2doEwDyEgHcEE9YOJp6a8jdSMY= -github.com/aws/aws-sdk-go-v2/credentials v1.19.13 h1:mA59E3fokBvyEGHKFdnpNNrvaR351cqiHgRg+JzOSRI= -github.com/aws/aws-sdk-go-v2/credentials v1.19.13/go.mod h1:yoTXOQKea18nrM69wGF9jBdG4WocSZA1h38A+t/MAsk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y= -github.com/aws/aws-sdk-go-v2/service/ecs v1.72.1 h1:Pciw9l/TbLpOjvTT9vm4IzHAyl2xQMBGlS44d0TvXXE= -github.com/aws/aws-sdk-go-v2/service/ecs v1.72.1/go.mod h1:DdtkqcURi9GM8f9HVLzJLTvS0h0k1qYg39vKQFmeR/k= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ= -github.com/aws/aws-sdk-go-v2/service/lambda v1.88.0 h1:u66DMbJWDFXs9458RAHNtq2d0gyqcZFV4mzRwfjM358= -github.com/aws/aws-sdk-go-v2/service/lambda v1.88.0/go.mod h1:ogjbkxFgFOjG3dYFQ8irC92gQfpfMDcy1RDKNSZWXNU= -github.com/aws/aws-sdk-go-v2/service/s3 v1.98.0 h1:foqo/ocQ7WqKwy3FojGtZQJo0FR4vto9qnz9VaumbCo= -github.com/aws/aws-sdk-go-v2/service/s3 v1.98.0/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 h1:GcLE9ba5ehAQma6wlopUesYg/hbcOhFNWTjELkiWkh4= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.14/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 h1:mP49nTpfKtpXLt5SLn8Uv8z6W+03jYVoOSAl/c02nog= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw= -github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= -github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/apache/thrift v0.23.0 h1:wKR6YnefQSEnxpEfmgTPuJibNG4bF0p2TK34tHLWi3s= +github.com/apache/thrift v0.23.0/go.mod h1:zPt6WxgvTOM6hF92y8C+MkEM5LMxZuk4JcQOiU4Esvs= +github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg= +github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9 h1:adBsCIIpLbLmYnkQU+nAChU5yhVTvu5PerROm+/Kq2A= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9/go.mod h1:uOYhgfgThm/ZyAuJGNQ5YgNyOlYfqnGpTHXvk3cpykg= +github.com/aws/aws-sdk-go-v2/config v1.32.16 h1:Q0iQ7quUgJP0F/SCRTieScnaMdXr9h/2+wze1u3cNeM= +github.com/aws/aws-sdk-go-v2/config v1.32.16/go.mod h1:duCCnJEFqpt2RC6no1iK6q+8HpwOAkiUua0pY507dQc= +github.com/aws/aws-sdk-go-v2/credentials v1.19.15 h1:fyvgWTszojq8hEnMi8PPBTvZdTtEVmAVyo+NFLHBhH4= +github.com/aws/aws-sdk-go-v2/credentials v1.19.15/go.mod h1:gJiYyMOjNg8OEdRWOf3CrFQxM2a98qmrtjx1zuiQfB8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 h1:IOGsJ1xVWhsi+ZO7/NW8OuZZBtMJLZbk4P5HDjJO0jQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22/go.mod h1:b+hYdbU+jGKfXE8kKM6g1+h+L/Go3vMvzlxBsiuGsxg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 h1:FPXsW9+gMuIeKmz7j6ENWcWtBGTe1kH8r9thNt5Uxx4= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23/go.mod h1:7J8iGMdRKk6lw2C+cMIphgAnT8uTwBwNOsGkyOCm80U= +github.com/aws/aws-sdk-go-v2/service/ecs v1.78.1 h1:9zSVr4X6X8JNTxSMip2RORaBB+Mu0/IfzNu3iRWZE9c= +github.com/aws/aws-sdk-go-v2/service/ecs v1.78.1/go.mod h1:1DlTqkp+8uc5At3UXyJAvJXFaWoMmxSHcp2Zdor0qGw= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 h1:HtOTYcbVcGABLOVuPYaIihj6IlkqubBwFj10K5fxRek= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8/go.mod h1:VsK9abqQeGlzPgUr+isNWzPlK2vKe9INMLWnY65f5Xs= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14 h1:xnvDEnw+pnj5mctWiYuFbigrEzSm35x7k4KS/ZkCANg= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14/go.mod h1:yS5rNogD8e0Wu9+l3MUwr6eENBzEeGejvINpN5PAYfY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 h1:PUmZeJU6Y1Lbvt9WFuJ0ugUK2xn6hIWUBBbKuOWF30s= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22/go.mod h1:nO6egFBoAaoXze24a2C0NjQCvdpk8OueRoYimvEB9jo= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22 h1:SE+aQ4DEqG53RRCAIHlCf//B2ycxGH7jFkpnAh/kKPM= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22/go.mod h1:ES3ynECd7fYeJIL6+oax+uIEljmfps0S70BaQzbMd/o= +github.com/aws/aws-sdk-go-v2/service/lambda v1.89.1 h1:JxHLwNK5mIKsh2Q0APTSijdzkk5ccI4gyvYdar1JU/0= +github.com/aws/aws-sdk-go-v2/service/lambda v1.89.1/go.mod h1:7qoh/MlWG5QCnZwq9bvdXomEAkmumayXcjEjIemIV7U= +github.com/aws/aws-sdk-go-v2/service/s3 v1.99.1 h1:kU/eBN5+MWNo/LcbNa4hWDdN76hdcd7hocU5kvu7IsU= +github.com/aws/aws-sdk-go-v2/service/s3 v1.99.1/go.mod h1:Fw9aqhJicIVee1VytBBjH+l+5ov6/PhbtIK/u3rt/ls= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 h1:a1Fq/KXn75wSzoJaPQTgZO0wHGqE9mjFnylnqEPTchA= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.10/go.mod h1:p6+MXNxW7IA6dMgHfTAzljuwSKD0NCm/4lbS4t6+7vI= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 h1:x6bKbmDhsgSZwv6q19wY/u3rLk/3FGjJWyqKcIRufpE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.16/go.mod h1:CudnEVKRtLn0+3uMV0yEXZ+YZOKnAtUJ5DmDhilVnIw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 h1:oK/njaL8GtyEihkWMD4k3VgHCT64RQKkZwh0DG5j8ak= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20/go.mod h1:JHs8/y1f3zY7U5WcuzoJ/yAYGYtNIVPKLIbp61euvmg= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 h1:ks8KBcZPh3PYISr5dAiXCM5/Thcuxk8l+PG4+A0exds= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.0/go.mod h1:pFw33T0WLvXU3rw1WBkpMlkgIn54eCB5FYLhjDc9Foo= +github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U= +github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/benbjohnson/clock v0.0.0-20160125162948-a620c1cc9866/go.mod h1:UMqtWQTnOe4byzwe7Zhwh8f8s+36uszN51sJrSIZlTE= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -114,12 +112,14 @@ github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go. github.com/cactus/go-statsd-client/v4 v4.0.0/go.mod h1:m73kwJp6TN0Ja9P6ycdZhWM1MlfxY/95WZ//IptPQ+Y= github.com/cactus/go-statsd-client/v5 v5.1.0 h1:sbbdfIl9PgisjEoXzvXI1lwUKWElngsjJKaZeC021P4= github.com/cactus/go-statsd-client/v5 v5.1.0/go.mod h1:COEvJ1E+/E2L4q6QE5CkjWPi4eeDw9maJBMIuMPBZbY= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/coreos/go-oidc/v3 v3.13.0 h1:M66zd0pcc5VxvBNM4pB331Wrsanby+QomQYjN8HamW8= github.com/coreos/go-oidc/v3 v3.13.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -134,22 +134,22 @@ github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa5 github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= -github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= -github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= -github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= +github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= -github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= -github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= +github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -157,10 +157,10 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/go-faker/faker/v4 v4.6.0 h1:6aOPzNptRiDwD14HuAnEtlTa+D1IfFuEHO8+vEFwjTs= -github.com/go-faker/faker/v4 v4.6.0/go.mod h1:ZmrHuVtTTm2Em9e0Du6CJ9CADaLEzGXW62z1YqFH0m0= +github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ= +github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-faker/faker/v4 v4.7.0 h1:VboC02cXHl/NuQh5lM2W8b87yp4iFXIu59x4w0RZi4E= +github.com/go-faker/faker/v4 v4.7.0/go.mod h1:u1dIRP5neLB6kTzgyVjdBOV5R1uP7BdxkcWk7tiKQXk= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= @@ -169,19 +169,43 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= +github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= +github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= +github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= +github.com/go-openapi/swag v0.26.0 h1:GVDXCmfvhfu1BxiHo8/FA+BbKmhecHnG3varjON5/RI= +github.com/go-openapi/swag v0.26.0/go.mod h1:82g3193sZJRbocs7bNCqGfIgq8pkuwVwCfhKIRlEQF0= +github.com/go-openapi/swag/cmdutils v0.26.0 h1:iowihOcvq7y4egO8cOq0dmfohz6wfeQ63U1EnuhO2TU= +github.com/go-openapi/swag/cmdutils v0.26.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= +github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= +github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= +github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= +github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= +github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w= +github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M= +github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= +github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= +github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko= +github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg= +github.com/go-openapi/swag/mangling v0.26.0 h1:Du2YC4YLA/Y5m/YKQd7AnY5qq0wRKSFZTTt8ktFaXcQ= +github.com/go-openapi/swag/mangling v0.26.0/go.mod h1:jifS7W9vbg+pw63bT+GI53otluMQL3CeemuyCHKwVx0= +github.com/go-openapi/swag/netutils v0.26.0 h1:CmZp+ZT7HrmFwrC3GdGsXBq2+42T1bjKBapcqVpIs3c= +github.com/go-openapi/swag/netutils v0.26.0/go.mod h1:5iK+Ok3ZohWWex1C50BFTPexi03UaPwjW4Oj8kgrpwo= +github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= +github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= +github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= +github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= +github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ= +github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE= +github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= +github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= -github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus= github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -197,10 +221,10 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b h1:EY/KpStFl60qA17CptGXhwfZ+k1sFNJIUNR8DdbcuUk= -github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= -github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= -github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/gomarkdown/markdown v0.0.0-20260411013819-759bbc3e3207 h1:p7t34F7K4OCRQblcDhNJnP46Uaarz3z2cLcvOZYxWn8= +github.com/gomarkdown/markdown v0.0.0-20260411013819-759bbc3e3207/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -216,20 +240,22 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= -github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas= +github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= +github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= +github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= @@ -257,12 +283,11 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -275,18 +300,18 @@ github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcX github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= +github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M= +github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= +github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= +github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -305,6 +330,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/nexus-rpc/nexus-proto-annotations v0.1.0 h1:2fELd+9sqUtNu6Fg//pw8YFsxOvp8vZ8hfP0nHhNI80= +github.com/nexus-rpc/nexus-proto-annotations v0.1.0/go.mod h1:n3UjF1bPCW8llR8tHvbxJ+27yPWrhpo8w/Yg1IOuY0Y= github.com/nexus-rpc/sdk-go v0.6.0 h1:QRgnP2zTbxEbiyWG/aXH8uSC5LV/Mg1fqb19jb4DBlo= github.com/nexus-rpc/sdk-go v0.6.0/go.mod h1:FHdPfVQwRuJFZFTF0Y2GOAxCrbIBNrcPna9slkGKPYk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -312,10 +339,6 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= -github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= -github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= -github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -335,16 +358,13 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= -github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rcrowley/go-metrics v0.0.0-20141108142129-dee209f2455f/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= -github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= +github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= @@ -358,12 +378,12 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.0.2-0.20170726183946-abee6f9b0679/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -373,18 +393,13 @@ github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMps github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/temporalio/ringpop-go v0.0.0-20250130211428-b97329e994f7 h1:lEebX/hZss+TSH3EBwhztnBavJVj7pWGJOH8UgKHS0w= @@ -392,10 +407,12 @@ github.com/temporalio/ringpop-go v0.0.0-20250130211428-b97329e994f7/go.mod h1:RE github.com/temporalio/sqlparser v0.0.0-20231115171017-f4060bcfa6cb h1:YzHH/U/dN7vMP+glybzcXRTczTrgfdRisNTzAj7La04= github.com/temporalio/sqlparser v0.0.0-20231115171017-f4060bcfa6cb/go.mod h1:143qKdh3G45IgV9p+gbAwp3ikRDI8mxsijFiXDfuxsw= github.com/temporalio/tchannel-go v1.22.1-0.20220818200552-1be8d8cffa5b/go.mod h1:c+V9Z/ZgkzAdyGvHrvC5AsXgN+M9Qwey04cBdKYzV7U= -github.com/temporalio/tchannel-go v1.22.1-0.20240528171429-1db37fdea938 h1:sEJGhmDo+0FaPWM6f0v8Tjia0H5pR6/Baj6+kS78B+M= -github.com/temporalio/tchannel-go v1.22.1-0.20240528171429-1db37fdea938/go.mod h1:ezRQRwu9KQXy8Wuuv1aaFFxoCNz5CeNbVOOkh3xctbY= -github.com/temporalio/ui-server/v2 v2.49.1 h1:OFbEpfCdSEEFYe7YdVZPTk3l4idT8ReStB6F788iiP0= -github.com/temporalio/ui-server/v2 v2.49.1/go.mod h1:BgMLfNqd11tohA1gjgEeZLlVvRSnV/PAKiMQYTij6IE= +github.com/temporalio/tchannel-go v1.22.1-0.20260129151045-8706a1ab5f61 h1:v9EBEMJggmXGbVcIAjGQpKgEB+a9E/Q0brJ6fGWJvhQ= +github.com/temporalio/tchannel-go v1.22.1-0.20260129151045-8706a1ab5f61/go.mod h1:ezRQRwu9KQXy8Wuuv1aaFFxoCNz5CeNbVOOkh3xctbY= +github.com/temporalio/ui-server/v2 v2.50.1 h1:dlyg96lFwOIaiXywDzPnSXVf8GPULLfrXXVXGqRsvJY= +github.com/temporalio/ui-server/v2 v2.50.1/go.mod h1:jiWHDxRe4RDXLxGwenfV+FSgjXat81okVTTZPQOlc3c= +github.com/temporalio/ui-server/v2 v2.52.0 h1:BJ5e1teLOITbn4G9zZTuDOsROonI0uRThGlzaQ1aMxs= +github.com/temporalio/ui-server/v2 v2.52.0/go.mod h1:HKjuLT3J/hM1nRN6Vge2Z63tPWPNUVR/2+5CMbkBQns= github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA= github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= @@ -422,26 +439,28 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/collector/pdata v1.34.0 h1:2vwYftckXe7pWxI9mfSo+tw3wqdGNrYpMbDx/5q6rw8= -go.opentelemetry.io/collector/pdata v1.34.0/go.mod h1:StPHMFkhLBellRWrULq0DNjv4znCDJZP6La4UuC+JHI= -go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= -go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/collector/featuregate v1.56.0 h1:NjcbOZkdCSXddAJmFLdO+pv1gmAgrU6sC5PBga2KlKI= +go.opentelemetry.io/collector/featuregate v1.56.0/go.mod h1:4ga1QBMPEejXXmpyJS8lmaRpknJ3Lb9Bvk6e420bUFU= +go.opentelemetry.io/collector/pdata v1.56.0 h1:W+QAfN2Iz8SNss1T5JNzRWFnw+7oP1vXBQH9ZuOJkXY= +go.opentelemetry.io/collector/pdata v1.56.0/go.mod h1:usR9utboXufbD1rp1oJy+3smQXXpZ+CsI3WN7QsiOs0= +go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU= +go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25FXWm/gmmY3BX258z5nuk= go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= @@ -450,18 +469,18 @@ go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfC go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= -go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= -go.temporal.io/api v1.62.8 h1:g8RAZmdebYODoNa2GLA4M4TsXNe1096WV3n26C4+fdw= -go.temporal.io/api v1.62.8/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= -go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2 h1:1hKeH3GyR6YD6LKMHGCZ76t6h1Sgha0hXVQBxWi3dlQ= -go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2/go.mod h1:T8dnzVPeO+gaUTj9eDgm/lT2lZH4+JXNvrGaQGyVi50= -go.temporal.io/sdk v1.41.1 h1:yOpvsHyDD1lNuwlGBv/SUodCPhjv9nDeC9lLHW/fJUA= -go.temporal.io/sdk v1.41.1/go.mod h1:/InXQT5guZ6AizYzpmzr5avQ/GMgq1ZObcKlKE2AhTc= -go.temporal.io/sdk/contrib/envconfig v1.0.0 h1:1Q/swVgB4EW/p3k7rI9/4hpU4/DC57FSRbU90+UisXw= -go.temporal.io/sdk/contrib/envconfig v1.0.0/go.mod h1:Pj4N1lwUEvxap6quBm8GrVMSUMJhSZkVtxjt3AYnPPg= -go.temporal.io/server v1.31.0 h1:FKLodreaMXUxYc3zr6xxwxtpGz1WH/t7O0IWxV1d1x0= -go.temporal.io/server v1.31.0/go.mod h1:MTQAw8uMU3ooSHyg/62JsNu/j8lK34SfKMTXkexYcw8= +go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= +go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +go.temporal.io/api v1.63.0 h1:YZFOTA0/thRUIUC4qunAWdHhPh/IG4vy/+WjfEvT+ZE= +go.temporal.io/api v1.63.0/go.mod h1:0k75tRljEuELWGeXjEZZO7zYqBln4+1FrG6+IMOMy7Q= +go.temporal.io/auto-scaled-workers v0.0.0-20260622220320-9b1e3849116d h1:f7+FCJHSrYWz9zvJp2OxKo8Fu/dsBUdnZZA+m5CEOS0= +go.temporal.io/auto-scaled-workers v0.0.0-20260622220320-9b1e3849116d/go.mod h1:HOnbQTZCW18EPcutFHTkZrDcGO4tUjQ8N2pjDyrGhrY= +go.temporal.io/sdk v1.44.1 h1:Mt2OZLZpqkzDIdg9YyQzO0Rb/HqCDnnqHlIAGAJ5gqM= +go.temporal.io/sdk v1.44.1/go.mod h1:vkApR12F9/Y8OR+hkxe7WyXQFuCX6clhzqnAk6rzDAM= +go.temporal.io/sdk/contrib/envconfig v1.0.2 h1:MGHfsuPUtsf7X9M6WYn3zYJj/mWsuYHnA1uuiL0KEuE= +go.temporal.io/sdk/contrib/envconfig v1.0.2/go.mod h1:MuMiH7hksps2uXnmKuAWaP9P6WbkSDy62kl64t1VJVg= +go.temporal.io/server v1.32.0-158.0 h1:M+rXAmFztzBrIXNYPZyUPJFZO6yHPKP1lRTrQNUKvbs= +go.temporal.io/server v1.32.0-158.0/go.mod h1:5HHOkpEzSikoCT1CL54dtJC+qpyAwHKz47bHZHJAIp4= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -482,8 +501,8 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -492,15 +511,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= -golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -526,10 +545,10 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -550,21 +569,19 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -572,10 +589,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -590,30 +607,30 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= -google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/api v0.276.0 h1:nVArUtfLEihtW+b0DdcqRGK1xoEm2+ltAihyztq7MKY= +google.golang.org/api v0.276.0/go.mod h1:Fnag/EWUPIcJXuIkP1pjoTgS5vdxlk3eeemL7Do6bvw= +google.golang.org/genproto v0.0.0-20260420184626-e10c466a9529 h1:QoMBg0moLIlB/eucPzc+ID5SgPZWuirtjAn3l8nW2Dg= +google.golang.org/genproto v0.0.0-20260420184626-e10c466a9529/go.mod h1:EjLmDZ8liSLBrCTK5vP+bGIxRQHE3ovGvOI0CzGk1PI= +google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 h1:zUWMZsvo/IJcD1t6MNCPO/azZTwz0TvwCBqr5aifoVY= +google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529/go.mod h1:a5OGAgyRr4lqco7AG9hQM9Fwh0N2ZV4grR0eXFEsXQg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 h1:XF8+t6QQiS0o9ArVan/HW8Q7cycNPGsJf6GA2nXxYAg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -633,42 +650,42 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q= -k8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM= -k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU= -k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= -k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM= -k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= -modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= -modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM= -modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA= -modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= +k8s.io/api v0.35.4 h1:P7nFYKl5vo9AGUp1Z+Pmd3p2tA7bX2wbFWCvDeRv988= +k8s.io/api v0.35.4/go.mod h1:yl4lqySWOgYJJf9RERXKUwE9g2y+CkuwG+xmcOK8wXU= +k8s.io/apimachinery v0.35.4 h1:xtdom9RG7e+yDp71uoXoJDWEE2eOiHgeO4GdBzwWpds= +k8s.io/apimachinery v0.35.4/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= +k8s.io/client-go v0.35.4 h1:DN6fyaGuzK64UvnKO5fOA6ymSjvfGAnCAHAR0C66kD8= +k8s.io/client-go v0.35.4/go.mod h1:2Pg9WpsS4NeOpoYTfHHfMxBG8zFMSAUi4O/qoiJC3nY= +k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= +k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= +k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f h1:4Qiq0YAoQATdgmHALJWz9rJ4fj20pB3xebpB4CFNhYM= +k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= +k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM= +k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= +modernc.org/cc/v4 v4.28.2 h1:3tQ0lf2ADtoby2EtSP+J7IE2SHwEJdP8ioR59wx7XpY= +modernc.org/cc/v4 v4.28.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= +modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ= +modernc.org/ccgo/v4 v4.34.0/go.mod h1:AS5WYMyBakQ+fhsHhtP8mWB82KTGPkNNJDGfGQCe0/A= +modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= +modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE= -modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= +modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= -modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI= -modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE= +modernc.org/libc v1.72.3 h1:ZnDF4tXn4NBXFutMMQC4vtbTFSXhhKzR73fv0beZEAU= +modernc.org/libc v1.72.3/go.mod h1:dn0dZNnnn1clLyvRxLxYExxiKRZIRENOfqQ8XEeg4Qs= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= -modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= +modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.44.3 h1:+39JvV/HWMcYslAwRxHb8067w+2zowvFOUrOWIy9PjY= -modernc.org/sqlite v1.44.3/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= +modernc.org/sqlite v1.51.0 h1:aH/MMSoayAIhozZ7uJbVTT9QO/VhzBf0J9tymmmuC/U= +modernc.org/sqlite v1.51.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= @@ -678,7 +695,7 @@ sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5E sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.4.0 h1:qmp2e3ZfFi1/jJbDGpD4mt3wyp6PE1NfKHCYLqgNQJo= +sigs.k8s.io/structured-merge-diff/v6 v6.4.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= 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/docs.go b/internal/commandsgen/docs.go index b3857503e..d699c23f6 100644 --- a/internal/commandsgen/docs.go +++ b/internal/commandsgen/docs.go @@ -8,17 +8,33 @@ import ( "strings" ) -func GenerateDocsFiles(commands Commands) (map[string][]byte, error) { +// GenerateDocsFiles generates the MDX documentation files from parsed commands. +// +// subdirNames lists command names whose subcommands should each be written to +// their own file within a subdirectory instead of being combined into a single +// file. For example, passing "cloud" produces cloud/namespace.mdx, cloud/user.mdx, +// etc. rather than a single, unmanageably large cloud.mdx. +// +// Index/landing pages are intentionally not generated: on the docs site they are +// hand-maintained (custom ordering, front matter, embedded components), so +// generating them would overwrite that curated content. +func GenerateDocsFiles(commands Commands, subdirNames []string) (map[string][]byte, error) { optionSetMap := make(map[string]OptionSets) for i, optionSet := range commands.OptionSets { optionSetMap[optionSet.Name] = commands.OptionSets[i] } + splitParents := make(map[string]bool) + for _, name := range subdirNames { + splitParents[name] = true + } + w := &docWriter{ fileMap: make(map[string]*bytes.Buffer), optionSetMap: optionSetMap, allCommands: commands.CommandList, globalFlagsMap: make(map[string]map[string]Option), + splitParents: splitParents, } // sorted ascending by full name of command (activity complete, batch list, etc) @@ -45,13 +61,33 @@ type docWriter struct { optionSetMap map[string]OptionSets optionsStack [][]Option globalFlagsMap map[string]map[string]Option // fileName -> optionName -> Option + splitParents map[string]bool // command names whose subcommands are split into a subdirectory } func (c *Command) writeDoc(w *docWriter) error { w.processOptions(c) - // If this is a root command, write a new file depth := c.depth() + + // A split parent (e.g. "cloud") has no standalone file; its children are + // each written into a subdirectory instead. + if w.splitParents[c.FullName] { + return nil + } + // If any ancestor is a split parent, route this command into that subdirectory. + // FullName segments are separated by single spaces (never multiple), so + // splitting on " " yields the exact command path. + if depth >= 1 { + parts := strings.Split(c.FullName, " ") + for i := len(parts) - 1; i >= 1; i-- { + ancestor := strings.Join(parts[:i], " ") + if w.splitParents[ancestor] { + c.writeSplitDoc(w, ancestor) + return nil + } + } + } + if depth == 1 { w.writeCommand(c) } else if depth > 1 { @@ -80,8 +116,7 @@ func (w *docWriter) writeCommand(c *Command) { } w.fileMap[fileName].WriteString("---") w.fileMap[fileName].WriteString("\n\n") - w.fileMap[fileName].WriteString("{/* NOTE: This is an auto-generated file. Any edit to this file will be overwritten.\n") - w.fileMap[fileName].WriteString("This file is generated from https://github.com/temporalio/cli/blob/main/internal/commandsgen/commands.yml via internal/cmd/gen-docs */}\n\n") + w.fileMap[fileName].WriteString(autoGeneratedNotice) // Add introductory paragraph w.fileMap[fileName].WriteString(fmt.Sprintf("This page provides a reference for the `temporal` CLI `%s` command. ", fileName)) w.fileMap[fileName].WriteString("The flags applicable to each subcommand are presented in a table within the heading for the subcommand. ") @@ -92,45 +127,146 @@ func (w *docWriter) writeSubcommand(c *Command) { fileName := c.fileName() prefix := strings.Repeat("#", c.depth()) w.fileMap[fileName].WriteString(prefix + " " + c.leafName() + "\n\n") - w.fileMap[fileName].WriteString(c.Description + "\n\n") + w.fileMap[fileName].WriteString(escapeMDXDescription(c.Description) + "\n\n") if w.isLeafCommand(c) { - // gather options from command and all options available from parent commands - var options = make([]Option, 0) - var globalOptions = make([]Option, 0) - for i, o := range w.optionsStack { - if i == len(w.optionsStack)-1 { - options = append(options, o...) - } else { - globalOptions = append(globalOptions, o...) - } - } - - // alphabetize options - sort.Slice(options, func(i, j int) bool { - return options[i].Name < options[j].Name - }) + w.writeLeafOptions(fileName) + } +} - // Write command-specific flags or global flags message - if len(options) > 0 { - w.fileMap[fileName].WriteString("Use the following options to change the behavior of this command. ") - w.fileMap[fileName].WriteString("You can also use any of the [global flags](#global-flags) that apply to all subcommands.\n\n") - w.writeOptionsTable(options, c) +// writeLeafOptions writes the option table (or the global-flags fallback message) +// for a leaf command, and records its inherited options for the file's Global +// Flags section. The command's own options are the top frame of the options +// stack; everything below it is inherited from parent commands. +// +// The reference to the "#global-flags" anchor is only emitted when there are +// inherited options, since that is what causes a Global Flags section (and thus +// the anchor) to be generated for the file. A command with no inherited options +// (e.g. a top-level command in a split subdirectory) would otherwise link to an +// anchor that never exists. +func (w *docWriter) writeLeafOptions(fileName string) { + var options, globalOptions []Option + for i, o := range w.optionsStack { + if i == len(w.optionsStack)-1 { + options = append(options, o...) } else { - w.fileMap[fileName].WriteString("Use [global flags](#global-flags) to customize the connection to the Temporal Service for this command.\n\n") + globalOptions = append(globalOptions, o...) } + } + + sort.Slice(options, func(i, j int) bool { + return options[i].Name < options[j].Name + }) + + buf := w.fileMap[fileName] + hasGlobal := len(globalOptions) > 0 + switch { + case len(options) > 0 && hasGlobal: + buf.WriteString("Use the following options to change the behavior of this command. ") + buf.WriteString("You can also use any of the [global flags](#global-flags) that apply to all subcommands.\n\n") + w.writeOptionsTable(options, fileName) + case len(options) > 0: + buf.WriteString("Use the following options to change the behavior of this command.\n\n") + w.writeOptionsTable(options, fileName) + case hasGlobal: + buf.WriteString("Use [global flags](#global-flags) to customize the connection to the Temporal Service for this command.\n\n") + } + + w.collectGlobalFlags(fileName, globalOptions) +} + +// writeSplitDoc routes a command that lives under a split parent to the right +// writer: direct children get their own file, deeper subcommands are appended +// as headings within their parent's file. +func (c *Command) writeSplitDoc(w *docWriter, splitRoot string) { + splitDepth := len(strings.Split(splitRoot, " ")) + relativeDepth := len(strings.Split(c.FullName, " ")) - splitDepth + + switch { + case relativeDepth == 1: + w.writeSplitCommand(c, splitRoot) + case relativeDepth > 1: + w.writeSplitSubcommand(c, splitRoot) + } +} + +// splitFileName returns the file path for a command within a split parent. +// For example, with splitRoot "cloud" and command "cloud namespace", it returns +// "cloud/namespace". A multi-word split root is hyphenated into a single +// directory: splitRoot "one two" with command "one two three" returns +// "one-two/three" (not "one/two/three"). +func splitFileName(c *Command, splitRoot string) string { + splitParts := strings.Split(splitRoot, " ") + cmdParts := strings.Split(c.FullName, " ") + if len(cmdParts) <= len(splitParts) { + return "" + } + return strings.Join(splitParts, "-") + "/" + cmdParts[len(splitParts)] +} + +func (w *docWriter) writeSplitCommand(c *Command, splitRoot string) { + fileName := splitFileName(c, splitRoot) + splitParts := strings.Split(splitRoot, " ") + cmdParts := strings.Split(c.FullName, " ") + leafName := cmdParts[len(splitParts)] + + buf := &bytes.Buffer{} + w.fileMap[fileName] = buf + buf.WriteString("---\n") + buf.WriteString("id: " + leafName + "\n") + buf.WriteString("title: Temporal CLI " + c.FullName + " command reference\n") + buf.WriteString("sidebar_label: " + leafName + "\n") + buf.WriteString("description: " + c.Docs.DescriptionHeader + "\n") + buf.WriteString("toc_max_heading_level: 4\n") + buf.WriteString("keywords:\n") + for _, keyword := range c.Docs.Keywords { + buf.WriteString(" - " + keyword + "\n") + } + buf.WriteString("tags:\n") + for _, tag := range c.Docs.Tags { + buf.WriteString(" - " + tag + "\n") + } + buf.WriteString("---") + buf.WriteString("\n\n") + buf.WriteString(autoGeneratedNotice) + + buf.WriteString(escapeMDXDescription(c.Description) + "\n\n") + + if w.isLeafCommand(c) { + w.writeLeafOptions(fileName) + } else { + buf.WriteString(fmt.Sprintf("This page provides a reference for the `temporal %s` commands. ", c.FullName)) + buf.WriteString("The flags applicable to each subcommand are presented in a table within the heading for the subcommand. ") + buf.WriteString("Refer to [Global Flags](#global-flags) for flags that you can use with every subcommand.\n\n") + } +} + +func (w *docWriter) writeSplitSubcommand(c *Command, splitRoot string) { + fileName := splitFileName(c, splitRoot) + splitParts := strings.Split(splitRoot, " ") + cmdParts := strings.Split(c.FullName, " ") + relativeDepth := len(cmdParts) - len(splitParts) + prefix := strings.Repeat("#", relativeDepth) + // Use the path relative to the split root (e.g. "ha update", "cert-ca create") + // rather than just the final segment. Within one aggregated file this keeps + // each heading's generated anchor unique and stable, which other docs link to + // (e.g. /cloud/high-availability/enable -> cloud/namespace#ha-update). + leafName := strings.Join(cmdParts[len(splitParts)+1:], " ") + + buf := w.fileMap[fileName] + buf.WriteString(prefix + " " + leafName + "\n\n") + buf.WriteString(escapeMDXDescription(c.Description) + "\n\n") - // Collect global flags for later (deduplicated) - w.collectGlobalFlags(fileName, globalOptions) + if w.isLeafCommand(c) { + w.writeLeafOptions(fileName) } } -func (w *docWriter) writeOptionsTable(options []Option, c *Command) { +func (w *docWriter) writeOptionsTable(options []Option, fileName string) { if len(options) == 0 { return } - fileName := c.fileName() buf := w.fileMap[fileName] // Command-specific flags: 3 columns (no Default) @@ -254,11 +390,76 @@ func (w *docWriter) isLeafCommand(c *Command) bool { return true } +const autoGeneratedNotice = "{/* NOTE: This is an auto-generated file. Any edit to this file will be overwritten.\n" + + "This file is generated from the CLI command definitions via cmd/gen-docs. */}\n\n" + +// jsonInSingleQuotes matches a single-quoted string containing curly braces, +// e.g. 'YourKey={"a":"b"}' or '{"a":"b"}'. MDX interprets the braces as a JSX +// expression, so such examples must be wrapped in backticks (option table cells) +// or backslash-escaped (description body text). +var jsonInSingleQuotes = regexp.MustCompile(`'[^']*\{[^']*\}[^']*'`) + +// angleBracketPlaceholder matches lowercase angle-bracket placeholders such as +// , , or . Restricting the +// match to lowercase, multi-character names avoids escaping legitimate inline +// HTML that authors may intentionally include. +var angleBracketPlaceholder = regexp.MustCompile(`<([a-z][a-z0-9_:.-]+)>`) + +// headingID matches a Markdown heading line ending with a custom ID, e.g. +// "### Some heading {#custom-id}". The Docusaurus MDX parser (3.10+) treats the +// bare {#custom-id} as a JSX expression and fails to compile it. +var headingID = regexp.MustCompile(`^(#{1,6}\s+.+?)\s+\{#([\w-]+)\}\s*$`) + +// encodeJSONExample wraps single-quoted JSON examples in backticks so MDX renders +// them as inline code rather than parsing the curly braces as a JSX expression. +// This handles option description table cells; escapeMDXDescription handles the +// same class of issue for command description body text. func encodeJSONExample(v string) string { - // example: 'YourKey={"your": "value"}' - // results in an mdx acorn rendering error - // and wrapping in backticks lets it render - re := regexp.MustCompile(`('[a-zA-Z0-9]*={.*}')`) - v = re.ReplaceAllString(v, "`$1`") - return v + return jsonInSingleQuotes.ReplaceAllString(v, "`$0`") +} + +// escapeMDXDescription escapes patterns in a command description that are valid +// CommonMark but break MDX (JSX) compilation under Docusaurus 3.10+: +// +// - bare angle-bracket placeholders like , which MDX +// parses as an unclosed JSX/HTML tag; +// - curly braces in single-quoted JSON examples like '{"a":"b"}', which MDX +// parses as a JSX expression; and +// - custom heading IDs like "## Heading {#id}", where MDX parses the {#id} as +// a JSX expression and fails on the '#'. These are converted to Docusaurus's +// MDX-compatible {/* #id */} comment form, which preserves the custom anchor. +// +// Content inside fenced code blocks (```) and inline code spans (backticks) is +// left untouched, since MDX does not interpret those as JSX. Fence markers are +// assumed to be on their own line (the standard CommonMark form the command +// descriptions use): a ``` opening or closing a block must start the line. +func escapeMDXDescription(desc string) string { + lines := strings.Split(desc, "\n") + inCodeBlock := false + for i, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "```") { + inCodeBlock = !inCodeBlock + continue + } + if inCodeBlock { + continue + } + + line = headingID.ReplaceAllString(line, "$1 {/* #$2 */}") + + // Escape only outside inline code spans. Splitting on backticks yields + // alternating outside/inside segments (assuming balanced backticks). + segments := strings.Split(line, "`") + for j := range segments { + if j%2 != 0 { + continue // inside an inline code span + } + segments[j] = angleBracketPlaceholder.ReplaceAllString(segments[j], `\<$1\>`) + segments[j] = jsonInSingleQuotes.ReplaceAllStringFunc(segments[j], func(m string) string { + return strings.NewReplacer("{", `\{`, "}", `\}`).Replace(m) + }) + } + lines[i] = strings.Join(segments, "`") + } + return strings.Join(lines, "\n") } diff --git a/internal/commandsgen/docs_test.go b/internal/commandsgen/docs_test.go new file mode 100644 index 000000000..8e9391b79 --- /dev/null +++ b/internal/commandsgen/docs_test.go @@ -0,0 +1,184 @@ +package commandsgen + +import ( + "strings" + "testing" +) + +func TestEscapeMDXDescription(t *testing.T) { + cases := []struct { + name string + in string + want string + }{ + { + name: "bare angle-bracket placeholder is escaped", + in: "Pass the cert via --ca-certificate to authenticate.", + want: `Pass the cert via --ca-certificate \ to authenticate.`, + }, + { + name: "placeholder with separators and colon is escaped", + in: "Set --limit and --reason .", + want: `Set --limit \ and --reason \.`, + }, + { + name: "angle brackets inside a fenced code block are left untouched", + in: "Example:\n\n```\ntemporal x --cert \n```\n", + want: "Example:\n\n```\ntemporal x --cert \n```\n", + }, + { + name: "angle brackets inside an inline code span are left untouched", + in: "Use `--cert ` here but escape there.", + want: "Use `--cert ` here but escape \\ there.", + }, + { + name: "custom heading id is converted to MDX comment form", + in: "### Resetting activities that heartbeat {#reset-heartbeats}\n\nSee [details](#reset-heartbeats).", + want: "### Resetting activities that heartbeat {/* #reset-heartbeats */}\n\nSee [details](#reset-heartbeats).", + }, + { + name: "non-heading line with {#id}-like text is not treated as a heading", + in: "See [details](#reset-heartbeats).", + want: "See [details](#reset-heartbeats).", + }, + { + name: "bare single-quoted JSON has its braces escaped", + in: `Provide default '{"some-key": "some-value"}' inline.`, + want: `Provide default '\{"some-key": "some-value"\}' inline.`, + }, + { + name: "single-quoted JSON inside a fence is left untouched", + in: "```\ntemporal x --input '{\"some-key\": \"some-value\"}'\n```", + want: "```\ntemporal x --input '{\"some-key\": \"some-value\"}'\n```", + }, + { + name: "plain prose is unchanged", + in: "This command does something useful with no special characters.", + want: "This command does something useful with no special characters.", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if got := escapeMDXDescription(tc.in); got != tc.want { + t.Errorf("escapeMDXDescription()\n got: %q\nwant: %q", got, tc.want) + } + }) + } +} + +func TestEncodeJSONExample(t *testing.T) { + cases := []struct { + name string + in string + want string + }{ + { + name: "key=json example is wrapped", + in: `Example: 'YourKey={"your": "value"}'.`, + want: "Example: `'YourKey={\"your\": \"value\"}'`.", + }, + { + name: "standalone json example is wrapped", + in: `Example: '{"some-key": "some-value"}'.`, + want: "Example: `'{\"some-key\": \"some-value\"}'`.", + }, + { + name: "nested json example is wrapped", + in: `Example: '{"a": {"b": "c"}}'.`, + want: "Example: `'{\"a\": {\"b\": \"c\"}}'`.", + }, + { + name: "no json is unchanged", + in: "A plain description without JSON.", + want: "A plain description without JSON.", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if got := encodeJSONExample(tc.in); got != tc.want { + t.Errorf("encodeJSONExample()\n got: %q\nwant: %q", got, tc.want) + } + }) + } +} + +// splitFixture is a minimal command tree with a "cloud" root, mirroring how the +// cloud CLI extension is structured, used to exercise -subdir splitting. +const splitFixture = ` +commands: + - name: cloud + summary: Cloud + description: Manage Temporal Cloud. + - name: cloud thing + summary: Thing + description: | + Manage things. + docs: + keywords: + - thing + description-header: Manage things + tags: + - Cloud + - name: cloud thing sub + summary: Sub + description: | + Do a thing with a bare outside code. + options: + - name: flag + type: string + description: A flag. +` + +func generateSplitFixture(t *testing.T, subdirs []string) map[string][]byte { + t.Helper() + cmds, err := ParseCommands([]byte(splitFixture)) + if err != nil { + t.Fatalf("ParseCommands: %v", err) + } + docs, err := GenerateDocsFiles(cmds, subdirs) + if err != nil { + t.Fatalf("GenerateDocsFiles: %v", err) + } + return docs +} + +func TestGenerateDocsFilesSubdir(t *testing.T) { + docs := generateSplitFixture(t, []string{"cloud"}) + + if _, ok := docs["cloud/thing"]; !ok { + t.Errorf("expected split file cloud/thing, got keys: %v", keys(docs)) + } + if _, ok := docs["thing"]; ok { + t.Errorf("did not expect a flat 'thing' file when splitting cloud") + } + if _, ok := docs["cloud"]; ok { + t.Errorf("split parent 'cloud' should not produce a standalone file") + } + // Index pages are hand-maintained on the docs site, so gen-docs never emits them. + if _, ok := docs["cloud/index"]; ok { + t.Errorf("gen-docs should not emit a cloud/index page") + } + if _, ok := docs["index"]; ok { + t.Errorf("gen-docs should not emit a top-level index page") + } + + // The deeper subcommand's description is appended to its parent file and + // must be MDX-escaped there. + thing := string(docs["cloud/thing"]) + if !strings.Contains(thing, `\`) { + t.Errorf("expected escaped placeholder in cloud/thing, got:\n%s", thing) + } + if !strings.Contains(thing, "## sub") { + t.Errorf("expected 'sub' heading in cloud/thing, got:\n%s", thing) + } +} + +func keys(m map[string][]byte) []string { + out := make([]string, 0, len(m)) + for k := range m { + out = append(out, k) + } + return out +} 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/devserver/server.go b/internal/devserver/server.go index 610bd628a..e6f6e7081 100644 --- a/internal/devserver/server.go +++ b/internal/devserver/server.go @@ -76,6 +76,7 @@ type StartOptions struct { UIPort int // Required if UIIP is non-empty UIAssetPath string UICodecEndpoint string + UIDisableNewsFetch bool PublicPath string DatabaseFile string MetricsPort int @@ -176,6 +177,7 @@ func (s *StartOptions) buildUIServer() *uiserver.Server { Codec: uiconfig.Codec{Endpoint: s.UICodecEndpoint}, CORS: uiconfig.CORS{CookieInsecure: true}, HideLogs: true, + DisableNewsFetch: s.UIDisableNewsFetch, })) } diff --git a/internal/temporalcli/client.go b/internal/temporalcli/client.go index 751c61c06..230ff5a35 100644 --- a/internal/temporalcli/client.go +++ b/internal/temporalcli/client.go @@ -21,8 +21,17 @@ import ( // so often used by callers after this call to know the currently configured // namespace. func dialClient(cctx *CommandContext, c *cliext.ClientOptions) (client.Client, error) { + cl, _, err := dialClientWithCodec(cctx, c) + return cl, err +} + +// dialClientWithCodec is like [dialClient] but also returns the configured remote +// payload codec, or nil if no codec is configured. The codec is the same instance +// used by the gRPC interceptor; callers can use it to decode payloads nested inside +// opaque proto bytes (e.g. the request/response of a system Nexus operation). +func dialClientWithCodec(cctx *CommandContext, c *cliext.ClientOptions) (client.Client, converter.PayloadCodec, error) { if cctx.RootCommand == nil { - return nil, fmt.Errorf("root command unexpectedly missing when dialing client") + return nil, nil, fmt.Errorf("root command unexpectedly missing when dialing client") } // Set default identity if not provided @@ -47,7 +56,7 @@ func dialClient(cctx *CommandContext, c *cliext.ClientOptions) (client.Client, e } clientOpts, err := builder.Build(cctx) if err != nil { - return nil, err + return nil, nil, err } // We do not put codec on data converter here, it is applied via @@ -78,14 +87,14 @@ func dialClient(cctx *CommandContext, c *cliext.ClientOptions) (client.Client, e cl, err := client.DialContext(dialCtx, clientOpts) if err != nil { - return nil, err + return nil, nil, err } // Since this namespace value is used by many commands after this call, // we are mutating it to be the derived one c.Namespace = clientOpts.Namespace - return cl, nil + return cl, builder.PayloadCodec, nil } func fixedHeaderOverrideInterceptor( diff --git a/internal/temporalcli/commands.config.go b/internal/temporalcli/commands.config.go index e8a423dd0..fb4dc1d81 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 @@ -253,6 +259,7 @@ var envConfigPropsToFieldNames = map[string]string{ "address": "Address", "namespace": "Namespace", "api_key": "APIKey", + "authority": "Authority", "tls": "TLS", "tls.disabled": "Disabled", "tls.client_cert_path": "ClientCertPath", @@ -311,10 +318,8 @@ func writeEnvConfigFile(cctx *CommandContext, conf *envconfig.ClientConfig) erro if configFile == "" { configFile, _ = cctx.Options.EnvLookup.LookupEnv("TEMPORAL_CONFIG_FILE") if configFile == "" { - var err error - if configFile, err = envconfig.DefaultConfigFilePath(); err != nil { - return err - } + configFile = envconfig.DefaultConfigFilePath() + } } diff --git a/internal/temporalcli/commands.config_test.go b/internal/temporalcli/commands.config_test.go index 5cc89ec86..ee6e484b7 100644 --- a/internal/temporalcli/commands.config_test.go +++ b/internal/temporalcli/commands.config_test.go @@ -25,6 +25,7 @@ func TestConfig_Get(t *testing.T) { address = "my-address" namespace = "my-namespace" api_key = "my-api-key" +authority = "my-authority" codec = { endpoint = "my-endpoint", auth = "my-auth" } grpc_meta = { some-heAder1 = "some-value1", some-header2 = "some-value2", some_heaDer3 = "some-value3" } some_future_key = "some future value not handled" @@ -74,6 +75,7 @@ disable_host_verification = true`)) "address": "my-address", "namespace": "my-namespace", "api_key": "my-api-key", + "authority": "my-authority", "codec.endpoint": "my-endpoint", "codec.auth": "my-auth", "grpc_meta.some-header1": "some-value1", @@ -117,6 +119,7 @@ disable_host_verification = true`)) h.JSONEq(`{ "address": "my-address", "api_key": "my-api-key", + "authority": "my-authority", "codec": { "auth": "my-auth", "endpoint": "my-endpoint" @@ -151,6 +154,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() @@ -320,6 +347,7 @@ func TestConfig_Set(t *testing.T) { "address": "my-address", "namespace": "my-namespace", "api_key": "my-api-key", + "authority": "my-authority", "codec.endpoint": "my-endpoint", "codec.auth": "my-auth", "grpc_meta.sOme_header1": "some-value1", @@ -350,6 +378,7 @@ func TestConfig_Set(t *testing.T) { "address": "my-address", "namespace": "my-namespace", "api_key": "my-api-key", + "authority": "my-authority", "tls": map[string]any{ "disabled": true, "client_cert_path": "my-client-cert-path", diff --git a/internal/temporalcli/commands.extension.go b/internal/temporalcli/commands.extension.go index 794242263..b00900810 100644 --- a/internal/temporalcli/commands.extension.go +++ b/internal/temporalcli/commands.extension.go @@ -25,6 +25,14 @@ var cliArgsToParseForExtension = map[string]bool{ "command-timeout": true, } +type ExtensionNonZeroExit struct { + *exec.ExitError +} + +func (err ExtensionNonZeroExit) Unwrap() error { + return err.ExitError +} + // tryExecuteExtension tries to execute an extension command if the command is not a built-in command. // It returns an error if the extension command fails, and a boolean indicating whether an extension was executed. func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bool) { @@ -77,8 +85,8 @@ func tryExecuteExtension(cctx *CommandContext, tcmd *TemporalCommand) (error, bo if ctx.Err() != nil { return fmt.Errorf("program interrupted"), true } - if _, ok := err.(*exec.ExitError); ok { - return nil, true + if exitError, ok := err.(*exec.ExitError); ok { + return ExtensionNonZeroExit{exitError}, true } return fmt.Errorf("extension %s failed: %w", extPath, err), true } diff --git a/internal/temporalcli/commands.extension_test.go b/internal/temporalcli/commands.extension_test.go index 266a8fcd1..2590be0cc 100644 --- a/internal/temporalcli/commands.extension_test.go +++ b/internal/temporalcli/commands.extension_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/temporalio/cli/internal/temporalcli" "golang.org/x/tools/imports" ) @@ -232,7 +233,7 @@ func TestExtension_FailsOnNonExecutableCommand(t *testing.T) { h := newExtensionHarness(t) // Create file without execute permission. path := filepath.Join(h.binDir, "temporal-foo") - err := os.WriteFile(path, []byte("a text file"), 0644) + err := os.WriteFile(path, []byte("a text file"), 0o644) require.NoError(t, err) res := h.Execute("foo") @@ -248,7 +249,10 @@ func TestExtension_PassesThroughNonZeroExit(t *testing.T) { res := h.Execute("foo") assert.Equal(t, "Args: temporal-foo \n", res.Stdout.String()) - assert.NoError(t, res.Err) + var exitError temporalcli.ExtensionNonZeroExit + if assert.ErrorAs(t, res.Err, &exitError) { + assert.Equal(t, 42, exitError.ExitCode()) + } } func TestExtension_FailsOnCommandTimeout(t *testing.T) { @@ -306,7 +310,7 @@ func (h *extensionHarness) createExtension(name string, code ...string) string { // Write source file. srcPath := filepath.Join(h.binDir, name+".go") - require.NoError(h.t, os.WriteFile(srcPath, formatted, 0644)) + require.NoError(h.t, os.WriteFile(srcPath, formatted, 0o644)) // Build executable. binPath := filepath.Join(h.binDir, name) diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 03424647a..16e075dc6 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -341,6 +341,58 @@ func (v *NexusEndpointConfigOptions) BuildFlags(f *pflag.FlagSet) { f.StringVar(&v.TargetUrl, "target-url", "", "An external Nexus Endpoint that receives forwarded Nexus requests. May be used as an alternative to `--target-namespace` and `--target-task-queue`. EXPERIMENTAL.") } +type NexusOperationReferenceOptions struct { + OperationId string + RunId string + FlagSet *pflag.FlagSet +} + +func (v *NexusOperationReferenceOptions) BuildFlags(f *pflag.FlagSet) { + v.FlagSet = f + f.StringVar(&v.OperationId, "operation-id", "", "Nexus Operation ID. Required.") + _ = cobra.MarkFlagRequired(f, "operation-id") + f.StringVarP(&v.RunId, "run-id", "r", "", "Run ID of the Nexus Operation.") +} + +type NexusOperationStartOptions struct { + Endpoint string + Service string + Operation string + OperationId string + ScheduleToCloseTimeout cliext.FlagDuration + ScheduleToStartTimeout cliext.FlagDuration + StartToCloseTimeout cliext.FlagDuration + IdConflictPolicy cliext.FlagStringEnum + IdReusePolicy cliext.FlagStringEnum + SearchAttribute []string + StaticSummary string + FlagSet *pflag.FlagSet +} + +func (v *NexusOperationStartOptions) BuildFlags(f *pflag.FlagSet) { + v.FlagSet = f + f.StringVar(&v.Endpoint, "endpoint", "", "Nexus Endpoint name. Required.") + _ = cobra.MarkFlagRequired(f, "endpoint") + f.StringVar(&v.Service, "service", "", "Nexus Service name. Required.") + _ = cobra.MarkFlagRequired(f, "service") + f.StringVar(&v.Operation, "operation", "", "Nexus Operation name. Required.") + _ = cobra.MarkFlagRequired(f, "operation") + f.StringVar(&v.OperationId, "operation-id", "", "Nexus Operation ID. Required.") + _ = cobra.MarkFlagRequired(f, "operation-id") + v.ScheduleToCloseTimeout = 0 + f.Var(&v.ScheduleToCloseTimeout, "schedule-to-close-timeout", "Total time the operation is allowed to run.") + v.ScheduleToStartTimeout = 0 + f.Var(&v.ScheduleToStartTimeout, "schedule-to-start-timeout", "Maximum time to wait for an operation to be started (or completed synchronously) by a handler.") + v.StartToCloseTimeout = 0 + f.Var(&v.StartToCloseTimeout, "start-to-close-timeout", "Maximum time to wait for an asynchronous operation to complete after it has been started.") + v.IdConflictPolicy = cliext.NewFlagStringEnum([]string{"Fail", "UseExisting", "TerminateExisting"}, "") + f.Var(&v.IdConflictPolicy, "id-conflict-policy", "Policy for handling an Operation ID conflict with a running operation. Accepted values: Fail, UseExisting, TerminateExisting.") + v.IdReusePolicy = cliext.NewFlagStringEnum([]string{"AllowDuplicate", "RejectDuplicate"}, "") + f.Var(&v.IdReusePolicy, "id-reuse-policy", "Policy for re-using an Operation ID from a previously closed operation. Accepted values: AllowDuplicate, RejectDuplicate.") + f.StringArrayVar(&v.SearchAttribute, "search-attribute", nil, "Search Attribute in `KEY=VALUE` format. Keys must be identifiers, and values must be JSON values. For example: 'YourKey={\"your\": \"value\"}'. Can be passed multiple times.") + f.StringVar(&v.StaticSummary, "static-summary", "", "Static summary for the Nexus Operation for human consumption in UIs. Uses Temporal Markdown formatting, should be a single line. EXPERIMENTAL.") +} + type QueryModifiersOptions struct { RejectCondition cliext.FlagStringEnum Headers []string @@ -363,11 +415,11 @@ type WorkflowUpdateOptionsOptions struct { func (v *WorkflowUpdateOptionsOptions) BuildFlags(f *pflag.FlagSet) { v.FlagSet = f - v.VersioningOverrideBehavior = cliext.NewFlagStringEnum([]string{"pinned", "auto_upgrade"}, "") - f.Var(&v.VersioningOverrideBehavior, "versioning-override-behavior", "Override the versioning behavior of a Workflow. Accepted values: pinned, auto_upgrade. Required.") + v.VersioningOverrideBehavior = cliext.NewFlagStringEnum([]string{"pinned", "one_time", "auto_upgrade"}, "") + f.Var(&v.VersioningOverrideBehavior, "versioning-override-behavior", "Override the versioning behavior of a Workflow. Accepted values: pinned, one_time, auto_upgrade. Required.") _ = cobra.MarkFlagRequired(f, "versioning-override-behavior") - f.StringVar(&v.VersioningOverrideDeploymentName, "versioning-override-deployment-name", "", "When overriding to a `pinned` behavior, specifies the Deployment Name of the version to target.") - f.StringVar(&v.VersioningOverrideBuildId, "versioning-override-build-id", "", "When overriding to a `pinned` behavior, specifies the Build ID of the version to target.") + f.StringVar(&v.VersioningOverrideDeploymentName, "versioning-override-deployment-name", "", "When overriding to a `pinned` or `one_time` behavior, specifies the Deployment Name of the version to target.") + f.StringVar(&v.VersioningOverrideBuildId, "versioning-override-build-id", "", "When overriding to a `pinned` or `one_time` behavior, specifies the Build ID of the version to target.") } type ActivityReferenceOptions struct { @@ -461,6 +513,7 @@ func NewTemporalCommand(cctx *CommandContext) *TemporalCommand { s.Command.AddCommand(&NewTemporalBatchCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalConfigCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalEnvCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalOperatorCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalScheduleCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalServerCommand(cctx, &s).Command) @@ -735,9 +788,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.") @@ -1369,6 +1422,285 @@ func NewTemporalEnvSetCommand(cctx *CommandContext, parent *TemporalEnvCommand) return &s } +type TemporalNexusCommand struct { + Parent *TemporalCommand + Command cobra.Command + cliext.ClientOptions +} + +func NewTemporalNexusCommand(cctx *CommandContext, parent *TemporalCommand) *TemporalNexusCommand { + var s TemporalNexusCommand + s.Parent = parent + s.Command.Use = "nexus" + s.Command.Short = "Start, list, and operate on Nexus Operations" + if hasHighlighting { + s.Command.Long = "Nexus Operation commands perform operations on Nexus\nOperation Executions:\n\n\x1b[1mtemporal nexus [command] [options]\x1b[0m\n\nFor example:\n\n\x1b[1mtemporal nexus operation list\x1b[0m" + } else { + s.Command.Long = "Nexus Operation commands perform operations on Nexus\nOperation Executions:\n\n```\ntemporal nexus [command] [options]\n```\n\nFor example:\n\n```\ntemporal nexus operation list\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewTemporalNexusOperationCommand(cctx, &s).Command) + s.ClientOptions.BuildFlags(s.Command.PersistentFlags()) + return &s +} + +type TemporalNexusOperationCommand struct { + Parent *TemporalNexusCommand + Command cobra.Command +} + +func NewTemporalNexusOperationCommand(cctx *CommandContext, parent *TemporalNexusCommand) *TemporalNexusOperationCommand { + var s TemporalNexusOperationCommand + s.Parent = parent + s.Command.Use = "operation" + s.Command.Short = "Commands for managing Nexus Operations" + if hasHighlighting { + s.Command.Long = "These commands manage Nexus Operation Executions.\n\nNexus Operation commands follow this syntax:\n\n\x1b[1mtemporal nexus operation [command] [options]\x1b[0m" + } else { + s.Command.Long = "These commands manage Nexus Operation Executions.\n\nNexus Operation commands follow this syntax:\n\n```\ntemporal nexus operation [command] [options]\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewTemporalNexusOperationCancelCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationCountCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationDescribeCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationExecuteCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationListCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationResultCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationStartCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalNexusOperationTerminateCommand(cctx, &s).Command) + return &s +} + +type TemporalNexusOperationCancelCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationReferenceOptions + Reason string +} + +func NewTemporalNexusOperationCancelCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationCancelCommand { + var s TemporalNexusOperationCancelCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "cancel [flags]" + s.Command.Short = "Request cancellation of a Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Request cancellation of a Nexus Operation.\n\n\x1b[1mtemporal nexus operation cancel \\\n --operation-id YourOperationId\x1b[0m\n\nThe Operation handler determines how to handle the\ncancellation request." + } else { + s.Command.Long = "Request cancellation of a Nexus Operation.\n\n```\ntemporal nexus operation cancel \\\n --operation-id YourOperationId\n```\n\nThe Operation handler determines how to handle the\ncancellation request." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.Reason, "reason", "", "Reason for cancellation.") + s.NexusOperationReferenceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationCountCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + Query string +} + +func NewTemporalNexusOperationCountCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationCountCommand { + var s TemporalNexusOperationCountCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "count [flags]" + s.Command.Short = "Count Nexus Operations matching a query (Experimental)" + if hasHighlighting { + s.Command.Long = "Return a count of Nexus Operations. Use \x1b[1m--query\x1b[0m\nto filter the operations to be counted.\n\n\x1b[1mtemporal nexus operation count \\\n --query 'Endpoint=\"YourEndpoint\"'\x1b[0m\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." + } else { + s.Command.Long = "Return a count of Nexus Operations. Use `--query`\nto filter the operations to be counted.\n\n```\ntemporal nexus operation count \\\n --query 'Endpoint=\"YourEndpoint\"'\n```\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVarP(&s.Query, "query", "q", "", "Query to filter Nexus Operation Executions to count.") + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationDescribeCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationReferenceOptions + Raw bool +} + +func NewTemporalNexusOperationDescribeCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationDescribeCommand { + var s TemporalNexusOperationDescribeCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "describe [flags]" + s.Command.Short = "Show detailed info for a Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Display detailed information about a specific Nexus\nOperation Execution.\n\n\x1b[1mtemporal nexus operation describe \\\n --operation-id YourOperationId\x1b[0m" + } else { + s.Command.Long = "Display detailed information about a specific Nexus\nOperation Execution.\n\n```\ntemporal nexus operation describe \\\n --operation-id YourOperationId\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().BoolVar(&s.Raw, "raw", false, "Print properties without changing their format.") + s.NexusOperationReferenceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationExecuteCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationStartOptions + PayloadInputOptions +} + +func NewTemporalNexusOperationExecuteCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationExecuteCommand { + var s TemporalNexusOperationExecuteCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "execute [flags]" + s.Command.Short = "Start a new Nexus Operation and wait for its result (Experimental)" + if hasHighlighting { + s.Command.Long = "Start a new Nexus Operation Execution and block until\nit completes. The result is output to stdout.\n\n\x1b[1mtemporal nexus operation execute \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\x1b[0m" + } else { + s.Command.Long = "Start a new Nexus Operation Execution and block until\nit completes. The result is output to stdout.\n\n```\ntemporal nexus operation execute \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\n```" + } + s.Command.Args = cobra.NoArgs + s.NexusOperationStartOptions.BuildFlags(s.Command.Flags()) + s.PayloadInputOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationListCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + Query string + Limit int + PageSize int +} + +func NewTemporalNexusOperationListCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationListCommand { + var s TemporalNexusOperationListCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "list [flags]" + s.Command.Short = "List Nexus Operations matching a query (Experimental)" + if hasHighlighting { + s.Command.Long = "List Nexus Operations. Use \x1b[1m--query\x1b[0m to filter results.\n\n\x1b[1mtemporal nexus operation list \\\n --query 'Endpoint=\"YourEndpoint\"'\x1b[0m\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." + } else { + s.Command.Long = "List Nexus Operations. Use `--query` to filter results.\n\n```\ntemporal nexus operation list \\\n --query 'Endpoint=\"YourEndpoint\"'\n```\n\nVisit https://docs.temporal.io/visibility to read more about\nSearch Attributes and queries." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVarP(&s.Query, "query", "q", "", "Query to filter the Nexus Operation Executions to list.") + s.Command.Flags().IntVar(&s.Limit, "limit", 0, "Maximum number of Nexus Operation Executions to display.") + s.Command.Flags().IntVar(&s.PageSize, "page-size", 0, "Maximum number of Nexus Operation Executions to fetch at a time from the server.") + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationResultCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationReferenceOptions +} + +func NewTemporalNexusOperationResultCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationResultCommand { + var s TemporalNexusOperationResultCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "result [flags]" + s.Command.Short = "Wait for and output the result of a Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Wait for a Nexus Operation to complete and output\nthe result.\n\n\x1b[1mtemporal nexus operation result \\\n --operation-id YourOperationId\x1b[0m" + } else { + s.Command.Long = "Wait for a Nexus Operation to complete and output\nthe result.\n\n```\ntemporal nexus operation result \\\n --operation-id YourOperationId\n```" + } + s.Command.Args = cobra.NoArgs + s.NexusOperationReferenceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationStartCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationStartOptions + PayloadInputOptions +} + +func NewTemporalNexusOperationStartCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationStartCommand { + var s TemporalNexusOperationStartCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "start [flags]" + s.Command.Short = "Start a new Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Start a new Nexus Operation. Outputs the\nOperation ID and Run ID.\n\n\x1b[1mtemporal nexus operation start \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\x1b[0m" + } else { + s.Command.Long = "Start a new Nexus Operation. Outputs the\nOperation ID and Run ID.\n\n```\ntemporal nexus operation start \\\n --endpoint YourEndpoint \\\n --service YourService \\\n --operation YourOperation \\\n --operation-id YourOperationId \\\n --input '{\"some-key\": \"some-value\"}'\n```" + } + s.Command.Args = cobra.NoArgs + s.NexusOperationStartOptions.BuildFlags(s.Command.Flags()) + s.PayloadInputOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type TemporalNexusOperationTerminateCommand struct { + Parent *TemporalNexusOperationCommand + Command cobra.Command + NexusOperationReferenceOptions + Reason string +} + +func NewTemporalNexusOperationTerminateCommand(cctx *CommandContext, parent *TemporalNexusOperationCommand) *TemporalNexusOperationTerminateCommand { + var s TemporalNexusOperationTerminateCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "terminate [flags]" + s.Command.Short = "Forcefully end a Nexus Operation (Experimental)" + if hasHighlighting { + s.Command.Long = "Terminate a Nexus Operation.\n\n\x1b[1mtemporal nexus operation terminate \\\n --operation-id YourOperationId \\\n --reason YourReason\x1b[0m\n\nOperation handlers cannot see or respond to terminations." + } else { + s.Command.Long = "Terminate a Nexus Operation.\n\n```\ntemporal nexus operation terminate \\\n --operation-id YourOperationId \\\n --reason YourReason\n```\n\nOperation handlers cannot see or respond to terminations." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.Reason, "reason", "", "Reason for termination. Defaults to a message with the current user's name.") + s.NexusOperationReferenceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type TemporalOperatorCommand struct { Parent *TemporalCommand Command cobra.Command @@ -2103,6 +2435,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 +2603,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 @@ -2405,6 +2771,7 @@ type TemporalServerStartDevCommand struct { UiPublicPath string UiAssetPath string UiCodecEndpoint string + UiDisableNewsFetch bool SqlitePragma []string DynamicConfigValue []string LogConfig bool @@ -2435,6 +2802,7 @@ func NewTemporalServerStartDevCommand(cctx *CommandContext, parent *TemporalServ s.Command.Flags().StringVar(&s.UiPublicPath, "ui-public-path", "", "The public base path for the Web UI. Defaults to `/`.") s.Command.Flags().StringVar(&s.UiAssetPath, "ui-asset-path", "", "UI custom assets path.") s.Command.Flags().StringVar(&s.UiCodecEndpoint, "ui-codec-endpoint", "", "UI remote codec HTTP endpoint.") + s.Command.Flags().BoolVar(&s.UiDisableNewsFetch, "ui-disable-news-fetch", false, "Disable the Web UI newsfeed. When set, the UI will not request the newsfeed and the button to open the newsfeed panel is hidden.") s.Command.Flags().StringArrayVar(&s.SqlitePragma, "sqlite-pragma", nil, "SQLite pragma statements in \"PRAGMA=VALUE\" format.") s.Command.Flags().StringArrayVar(&s.DynamicConfigValue, "dynamic-config-value", nil, "Dynamic configuration value using `KEY=VALUE` pairs. Keys must be identifiers, and values must be JSON values. For example: `YourKey=\"YourString\"` Can be passed multiple times.") s.Command.Flags().BoolVar(&s.LogConfig, "log-config", false, "Print the server config to stderr.") @@ -2548,9 +2916,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 +3012,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 +3045,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 +3102,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 +3166,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 +3199,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 +3232,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 +3258,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 +3293,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 +3328,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 +3362,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 +3395,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 +3425,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 +3456,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 +3493,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 +3530,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.") @@ -3202,6 +3600,7 @@ func NewTemporalWorkerDeploymentCommand(cctx *CommandContext, parent *TemporalWo s.Command.AddCommand(&NewTemporalWorkerDeploymentManagerIdentityCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalWorkerDeploymentSetCurrentVersionCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalWorkerDeploymentSetRampingVersionCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalWorkerDeploymentUpdateVersionMetadataCommand(cctx, &s).Command) return &s } @@ -3249,9 +3648,9 @@ func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, paren s.Command.Use = "create-version [flags]" s.Command.Short = "Create a new Worker Deployment Version" if hasHighlighting { - s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n\x1b[1mtemporal worker deployment create-version [options]\x1b[0m\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n\x1b[1mtemporal worker deployment create-version [options]\x1b[0m\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." } else { - s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n```\ntemporal worker deployment create-version [options]\n```\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\n```\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n```\ntemporal worker deployment create-version [options]\n```\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\n```\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." } s.Command.Args = cobra.NoArgs s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") @@ -3556,6 +3955,41 @@ func NewTemporalWorkerDeploymentSetRampingVersionCommand(cctx *CommandContext, p return &s } +type TemporalWorkerDeploymentUpdateVersionComputeConfigCommand struct { + Parent *TemporalWorkerDeploymentCommand + Command cobra.Command + DeploymentVersionOptions + AwsLambdaFunctionArn string + AwsLambdaAssumeRoleArn string + AwsLambdaAssumeRoleExternalId string + Remove bool +} + +func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandContext, parent *TemporalWorkerDeploymentCommand) *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand { + var s TemporalWorkerDeploymentUpdateVersionComputeConfigCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "update-version-compute-config [flags]" + s.Command.Short = "Update compute configuration for a Version" + if hasHighlighting { + s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\x1b[0m\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\x1b[0m\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + } else { + s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\n```\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\n```\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\n```\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") + s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleArn, "aws-lambda-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is specified.") + s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleExternalId, "aws-lambda-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-lambda-function-arn is specified.") + s.Command.Flags().BoolVar(&s.Remove, "remove", false, "Removes any compute configuration associated with this Worker Deployment Version.") + s.DeploymentVersionOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type TemporalWorkerDeploymentUpdateVersionMetadataCommand struct { Parent *TemporalWorkerDeploymentCommand Command cobra.Command @@ -4631,16 +5065,16 @@ func NewTemporalWorkflowUpdateOptionsCommand(cctx *CommandContext, parent *Tempo s.Command.Use = "update-options [flags]" s.Command.Short = "Change Workflow Execution Options" if hasHighlighting { - s.Command.Long = "Modify properties of Workflow Executions:\n\n\x1b[1mtemporal workflow update-options [options]\x1b[0m\n\nIt can override the Worker Deployment configuration of a\nWorkflow Execution, which controls Worker Versioning.\n\nFor example, to force Workers in the current Deployment execute the\nnext Workflow Task change behavior to \x1b[1mauto_upgrade\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior auto_upgrade\x1b[0m\n\nor to pin the workflow execution to a Worker Deployment, set behavior\nto \x1b[1mpinned\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior pinned \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\x1b[0m\n\nTo remove any previous overrides, set the behavior to\n\x1b[1munspecified\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior unspecified\x1b[0m\n\nTo see the current override use \x1b[1mtemporal workflow describe\x1b[0m" + s.Command.Long = "Modify properties of Workflow Executions:\n\n\x1b[1mtemporal workflow update-options [options]\x1b[0m\n\nIt can override the Worker Deployment configuration of a\nWorkflow Execution, which controls Worker Versioning.\n\nFor example, to force Workers in the current Deployment execute the\nnext Workflow Task change behavior to \x1b[1mauto_upgrade\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior auto_upgrade\x1b[0m\n\nor to pin the workflow execution to a Worker Deployment, set behavior\nto \x1b[1mpinned\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior pinned \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\x1b[0m\n\nor to move the workflow execution to a Worker Deployment Version until\nthe next Workflow Task completes there, set behavior to \x1b[1mone_time\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior one_time \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\x1b[0m\n\nTo remove any previous overrides, set the behavior to\n\x1b[1munspecified\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior unspecified\x1b[0m\n\nTo see the current override use \x1b[1mtemporal workflow describe\x1b[0m" } else { - s.Command.Long = "Modify properties of Workflow Executions:\n\n```\ntemporal workflow update-options [options]\n```\n\nIt can override the Worker Deployment configuration of a\nWorkflow Execution, which controls Worker Versioning.\n\nFor example, to force Workers in the current Deployment execute the\nnext Workflow Task change behavior to `auto_upgrade`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior auto_upgrade\n```\n\nor to pin the workflow execution to a Worker Deployment, set behavior\nto `pinned`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior pinned \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\n```\n\nTo remove any previous overrides, set the behavior to\n`unspecified`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior unspecified\n```\n\nTo see the current override use `temporal workflow describe`" + s.Command.Long = "Modify properties of Workflow Executions:\n\n```\ntemporal workflow update-options [options]\n```\n\nIt can override the Worker Deployment configuration of a\nWorkflow Execution, which controls Worker Versioning.\n\nFor example, to force Workers in the current Deployment execute the\nnext Workflow Task change behavior to `auto_upgrade`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior auto_upgrade\n```\n\nor to pin the workflow execution to a Worker Deployment, set behavior\nto `pinned`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior pinned \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\n```\n\nor to move the workflow execution to a Worker Deployment Version until\nthe next Workflow Task completes there, set behavior to `one_time`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior one_time \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\n```\n\nTo remove any previous overrides, set the behavior to\n`unspecified`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior unspecified\n```\n\nTo see the current override use `temporal workflow describe`" } s.Command.Args = cobra.NoArgs - s.VersioningOverrideBehavior = cliext.NewFlagStringEnum([]string{"unspecified", "pinned", "auto_upgrade"}, "") - s.Command.Flags().Var(&s.VersioningOverrideBehavior, "versioning-override-behavior", "Override the versioning behavior of a Workflow. Accepted values: unspecified, pinned, auto_upgrade. Required.") + s.VersioningOverrideBehavior = cliext.NewFlagStringEnum([]string{"unspecified", "pinned", "one_time", "auto_upgrade"}, "") + s.Command.Flags().Var(&s.VersioningOverrideBehavior, "versioning-override-behavior", "Override the versioning behavior of a Workflow. Accepted values: unspecified, pinned, one_time, auto_upgrade. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "versioning-override-behavior") - s.Command.Flags().StringVar(&s.VersioningOverrideDeploymentName, "versioning-override-deployment-name", "", "When overriding to a `pinned` behavior, specifies the Deployment Name of the version to target.") - s.Command.Flags().StringVar(&s.VersioningOverrideBuildId, "versioning-override-build-id", "", "When overriding to a `pinned` behavior, specifies the Build ID of the version to target.") + s.Command.Flags().StringVar(&s.VersioningOverrideDeploymentName, "versioning-override-deployment-name", "", "When overriding to a `pinned` or `one_time` behavior, specifies the Deployment Name of the version to target.") + s.Command.Flags().StringVar(&s.VersioningOverrideBuildId, "versioning-override-build-id", "", "When overriding to a `pinned` or `one_time` behavior, specifies the Build ID of the version to target.") s.SingleWorkflowOrBatchOptions.BuildFlags(s.Command.Flags()) s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { diff --git a/internal/temporalcli/commands.go b/internal/temporalcli/commands.go index 116de4800..eaf08dbdf 100644 --- a/internal/temporalcli/commands.go +++ b/internal/temporalcli/commands.go @@ -145,6 +145,11 @@ func (c *CommandContext) preprocessOptions() error { if c.Err() != nil { err = fmt.Errorf("program interrupted") } + if exitError, ok := errors.AsType[ExtensionNonZeroExit](err); ok { + // An extension failed after being found and successfully started. Here we defer + // to its own error handling logic, and just copy the exit code through. + os.Exit(exitError.ExitCode()) + } fmt.Fprintf(c.Options.Stderr, "Error: %v\n", err) os.Exit(1) } @@ -497,6 +502,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 { @@ -519,7 +529,7 @@ var buildInfo string func VersionString() string { // To add build-time information to the version string, use // go build -ldflags "-X github.com/temporalio/cli/internal.buildInfo=" - var bi = buildInfo + bi := buildInfo if bi != "" { bi = fmt.Sprintf(", %s", bi) } diff --git a/internal/temporalcli/commands.nexus_operation.go b/internal/temporalcli/commands.nexus_operation.go new file mode 100644 index 000000000..6c0fcada3 --- /dev/null +++ b/internal/temporalcli/commands.nexus_operation.go @@ -0,0 +1,508 @@ +package temporalcli + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/fatih/color" + "github.com/temporalio/cli/internal/printer" + "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + "go.temporal.io/api/failure/v1" + "go.temporal.io/api/serviceerror" + "go.temporal.io/api/workflowservice/v1" + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/converter" +) + +func (c *TemporalNexusOperationStartCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + handle, err := startNexusOperation(cctx, cl, &c.NexusOperationStartOptions, &c.PayloadInputOptions) + if err != nil { + return err + } + return printNexusOperationExecution(cctx, &c.NexusOperationStartOptions, handle.GetID(), handle.GetRunID(), c.Parent.Parent.Namespace) +} + +func (c *TemporalNexusOperationExecuteCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + handle, err := startNexusOperation(cctx, cl, &c.NexusOperationStartOptions, &c.PayloadInputOptions) + if err != nil { + return err + } + if !cctx.JSONOutput { + if err := printNexusOperationExecution(cctx, &c.NexusOperationStartOptions, handle.GetID(), handle.GetRunID(), c.Parent.Parent.Namespace); err != nil { + cctx.Logger.Error("Failed printing execution info", "error", err) + } + } + return getNexusOperationResult(cctx, cl, c.Parent.Parent.Namespace, handle.GetID(), handle.GetRunID()) +} + +func (c *TemporalNexusOperationResultCommand) run(cctx *CommandContext, _ []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + return getNexusOperationResult(cctx, cl, c.Parent.Parent.Namespace, c.OperationId, c.RunId) +} + +func startNexusOperation( + cctx *CommandContext, + cl client.Client, + opts *NexusOperationStartOptions, + inputOpts *PayloadInputOptions, +) (client.NexusOperationHandle, error) { + nexusCl, startOpts, err := buildNexusStartOptions(opts, inputOpts) + if err != nil { + return nil, err + } + nCl, err := cl.NewNexusClient(nexusCl) + if err != nil { + return nil, err + } + handle, err := nCl.ExecuteOperation(cctx, startOpts.operation, startOpts.input, startOpts.options) + if err != nil { + return nil, fmt.Errorf("failed starting nexus operation: %w", err) + } + return handle, nil +} + +func printNexusOperationExecution(cctx *CommandContext, opts *NexusOperationStartOptions, operationID, runID, namespace string) error { + if !cctx.JSONOutput { + cctx.Printer.Println(color.MagentaString("Started Nexus Operation:")) + } + return cctx.Printer.PrintStructured(struct { + Endpoint string `json:"endpoint"` + Service string `json:"service"` + Operation string `json:"operation"` + OperationId string `json:"operationId"` + RunId string `json:"runId"` + Namespace string `json:"namespace"` + }{ + Endpoint: opts.Endpoint, + Service: opts.Service, + Operation: opts.Operation, + OperationId: operationID, + RunId: runID, + Namespace: namespace, + }, printer.StructuredOptions{}) +} + +func getNexusOperationResult(cctx *CommandContext, cl client.Client, namespace, operationID, runID string) error { + resp, err := pollNexusOperationOutcome(cctx, cl, namespace, operationID, runID) + if err != nil { + var notFound *serviceerror.NotFound + if errors.As(err, ¬Found) { + return fmt.Errorf("nexus operation not found: %s", operationID) + } + return fmt.Errorf("failed polling nexus operation result: %w", err) + } + + // Use the run ID from the response if the caller didn't supply one. + if runID == "" { + runID = resp.GetRunId() + } + + switch v := resp.GetOutcome().(type) { + case *workflowservice.PollNexusOperationExecutionResponse_Result: + return printNexusOperationResult(cctx, operationID, runID, v.Result) + case *workflowservice.PollNexusOperationExecutionResponse_Failure: + if err := printNexusOperationFailure(cctx, operationID, runID, v.Failure); err != nil { + cctx.Logger.Error("Nexus operation failed, and printing the output also failed", "error", err) + } + return fmt.Errorf("nexus operation failed") + default: + return fmt.Errorf("unexpected nexus operation outcome type: %T", v) + } +} + +// Matches the SDK's pollNexusOperationTimeout in internal_nexus_client.go. +const pollNexusOperationTimeout = 60 * time.Second + +// pollNexusOperationOutcome polls for a nexus operation result using a +// hand-rolled loop rather than handle.Get() because handle.Get() deserializes +// the result into a Go value and converts failures to Go errors, losing the +// raw proto payloads. +func pollNexusOperationOutcome(cctx *CommandContext, cl client.Client, namespace, operationID, runID string) (*workflowservice.PollNexusOperationExecutionResponse, error) { + for { + pollCtx, cancel := context.WithTimeout(cctx, pollNexusOperationTimeout) + resp, err := cl.WorkflowService().PollNexusOperationExecution(pollCtx, &workflowservice.PollNexusOperationExecutionRequest{ + Namespace: namespace, + OperationId: operationID, + RunId: runID, + WaitStage: enumspb.NEXUS_OPERATION_WAIT_STAGE_CLOSED, + }) + if err != nil { + // check pollCtx.Err() first because it is set by cancel() + pollTimedOut := pollCtx.Err() != nil + cancel() + if cctx.Err() != nil { + return nil, cctx.Err() + } + if pollTimedOut { + continue + } + return nil, err + } + cancel() + if resp.GetOutcome() != nil { + return resp, nil + } + } +} + +func printNexusOperationResult(cctx *CommandContext, operationID, runID string, result *common.Payload) error { + if cctx.JSONOutput { + var resultJSON json.RawMessage + var err error + if cctx.JSONShorthandPayloads { + var valuePtr any + if err = converter.GetDefaultDataConverter().FromPayload(result, &valuePtr); err != nil { + return fmt.Errorf("nexus operation completed, but failed decoding result for json output: %w", err) + } + resultJSON, err = json.Marshal(valuePtr) + } else { + resultJSON, err = cctx.MarshalProtoJSON(result) + } + if err != nil { + return fmt.Errorf("nexus operation completed, but failed marshaling result for json output: %w", err) + } + return cctx.Printer.PrintStructured(struct { + OperationId string `json:"operationId"` + RunId string `json:"runId"` + Status string `json:"status"` + Result json.RawMessage `json:"result"` + }{ + OperationId: operationID, + RunId: runID, + Status: "COMPLETED", + Result: resultJSON, + }, printer.StructuredOptions{}) + } + + cctx.Printer.Println(color.MagentaString("Results:")) + var valuePtr any + if err := converter.GetDefaultDataConverter().FromPayload(result, &valuePtr); err != nil { + return fmt.Errorf("nexus operation completed, but failed decoding result: %w", err) + } + resultJSON, err := json.Marshal(valuePtr) + if err != nil { + return fmt.Errorf("nexus operation completed, but failed marshaling result: %w", err) + } + return cctx.Printer.PrintStructured(struct { + Status string + Result json.RawMessage `cli:",cardOmitEmpty"` + }{ + Status: color.GreenString("COMPLETED"), + Result: resultJSON, + }, printer.StructuredOptions{}) +} + +func printNexusOperationFailure(cctx *CommandContext, operationID, runID string, f *failure.Failure) error { + if cctx.JSONOutput { + failureJSON, err := cctx.MarshalProtoJSON(f) + if err != nil { + return fmt.Errorf("nexus operation failed, but failed marshaling failure for json output: %w", err) + } + return cctx.Printer.PrintStructured(struct { + OperationId string `json:"operationId"` + RunId string `json:"runId"` + Status string `json:"status"` + Failure json.RawMessage `json:"failure"` + }{ + OperationId: operationID, + RunId: runID, + Status: "FAILED", + Failure: failureJSON, + }, printer.StructuredOptions{}) + } + + cctx.Printer.Println(color.MagentaString("Results:")) + return cctx.Printer.PrintStructured(struct { + Status string + Failure string `cli:",cardOmitEmpty"` + }{ + Status: color.RedString("FAILED"), + Failure: cctx.MarshalFriendlyFailureBodyText(f, " "), + }, printer.StructuredOptions{}) +} + +func (c *TemporalNexusOperationDescribeCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + handle := cl.GetNexusOperationHandle(client.GetNexusOperationHandleOptions{ + OperationID: c.OperationId, + RunID: c.RunId, + }) + + desc, err := handle.Describe(cctx, client.DescribeNexusOperationOptions{}) + if err != nil { + return fmt.Errorf("failed describing nexus operation: %w", err) + } + + if c.Raw || cctx.JSONOutput { + return cctx.Printer.PrintStructured(desc.RawInfo, printer.StructuredOptions{}) + } + return printNexusOperationDescription(cctx, desc) +} + +func printNexusOperationDescription(cctx *CommandContext, desc *client.NexusOperationExecutionDescription) error { + summary, _ := desc.GetSummary() + d := struct { + OperationId string + RunId string + Endpoint string + Service string + Operation string + Status string + State string `cli:",cardOmitEmpty"` + Attempt int32 + ScheduleToCloseTimeout time.Duration `cli:",cardOmitEmpty"` + ScheduledTime time.Time `cli:",cardOmitEmpty"` + CloseTime time.Time `cli:",cardOmitEmpty"` + ExpirationTime time.Time `cli:",cardOmitEmpty"` + BlockedReason string `cli:",cardOmitEmpty"` + OperationToken string `cli:",cardOmitEmpty"` + Identity string `cli:",cardOmitEmpty"` + Summary string `cli:",cardOmitEmpty"` + }{ + OperationId: desc.OperationID, + RunId: desc.OperationRunID, + Endpoint: desc.Endpoint, + Service: desc.Service, + Operation: desc.Operation, + Status: desc.Status.String(), + State: desc.State.String(), + Attempt: desc.Attempt, + ScheduleToCloseTimeout: desc.ScheduleToCloseTimeout, + ScheduledTime: desc.ScheduledTime, + CloseTime: desc.CloseTime, + ExpirationTime: desc.ExpirationTime, + BlockedReason: desc.BlockedReason, + OperationToken: desc.OperationToken, + Identity: desc.Identity, + Summary: summary, + } + return cctx.Printer.PrintStructured(d, printer.StructuredOptions{}) +} + +func (c *TemporalNexusOperationCancelCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + handle := cl.GetNexusOperationHandle(client.GetNexusOperationHandleOptions{ + OperationID: c.OperationId, + RunID: c.RunId, + }) + + err = handle.Cancel(cctx, client.CancelNexusOperationOptions{ + Reason: c.Reason, + }) + if err != nil { + return fmt.Errorf("failed to request nexus operation cancellation: %w", err) + } + cctx.Printer.Println("Cancellation requested") + return nil +} + +func (c *TemporalNexusOperationTerminateCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + reason := c.Reason + if reason == "" { + reason = defaultReason() + } + handle := cl.GetNexusOperationHandle(client.GetNexusOperationHandleOptions{ + OperationID: c.OperationId, + RunID: c.RunId, + }) + if err := handle.Terminate(cctx, client.TerminateNexusOperationOptions{Reason: reason}); err != nil { + return fmt.Errorf("failed to terminate nexus operation: %w", err) + } + cctx.Printer.Println("Nexus Operation terminated") + return nil +} + +func (c *TemporalNexusOperationListCommand) run(cctx *CommandContext, _ []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + if c.Limit > 0 && c.Limit < c.PageSize { + c.PageSize = c.Limit + } + + cctx.Printer.StartList() + defer cctx.Printer.EndList() + + var nextPageToken []byte + var opsProcessed int + for pageIndex := 0; ; pageIndex++ { + resp, err := cl.WorkflowService().ListNexusOperationExecutions(cctx, &workflowservice.ListNexusOperationExecutionsRequest{ + Namespace: c.Parent.Parent.Namespace, + PageSize: int32(c.PageSize), + NextPageToken: nextPageToken, + Query: c.Query, + }) + if err != nil { + return fmt.Errorf("failed listing nexus operations: %w", err) + } + var textTable []map[string]any + for _, op := range resp.GetOperations() { + if c.Limit > 0 && opsProcessed >= c.Limit { + break + } + opsProcessed++ + if cctx.JSONOutput { + _ = cctx.Printer.PrintStructured(op, printer.StructuredOptions{}) + } else { + textTable = append(textTable, map[string]any{ + "Status": op.Status, + "OperationId": op.OperationId, + "Endpoint": op.Endpoint, + "Service": op.Service, + "Operation": op.Operation, + "StartTime": op.ScheduleTime.AsTime(), + }) + } + } + if len(textTable) > 0 { + _ = cctx.Printer.PrintStructured(textTable, printer.StructuredOptions{ + Fields: []string{"Status", "OperationId", "Endpoint", "Service", "Operation", "StartTime"}, + Table: &printer.TableOptions{NoHeader: pageIndex > 0}, + }) + } + nextPageToken = resp.GetNextPageToken() + if len(nextPageToken) == 0 || (c.Limit > 0 && opsProcessed >= c.Limit) { + return nil + } + } +} + +func (c *TemporalNexusOperationCountCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + resp, err := cl.WorkflowService().CountNexusOperationExecutions(cctx, &workflowservice.CountNexusOperationExecutionsRequest{ + Namespace: c.Parent.Parent.Namespace, + Query: c.Query, + }) + if err != nil { + return fmt.Errorf("failed counting nexus operations: %w", err) + } + groups := make([]countGroup, len(resp.Groups)) + for i, g := range resp.Groups { + groups[i] = g + } + if cctx.JSONOutput { + stripCountGroupMetadataType(groups) + return cctx.Printer.PrintStructured(resp, printer.StructuredOptions{}) + } + cctx.Printer.Printlnf("Total: %v", resp.Count) + printCountGroupsText(cctx, groups) + return nil +} + +// nexusStartInput holds the parsed inputs for starting a Nexus operation. +type nexusStartInput struct { + operation string + input any + options client.StartNexusOperationOptions +} + +func buildNexusStartOptions(s *NexusOperationStartOptions, p *PayloadInputOptions) (client.NexusClientOptions, *nexusStartInput, error) { + nexusCl := client.NexusClientOptions{ + Endpoint: s.Endpoint, + Service: s.Service, + } + + opts := client.StartNexusOperationOptions{ + ID: s.OperationId, + ScheduleToCloseTimeout: s.ScheduleToCloseTimeout.Duration(), + ScheduleToStartTimeout: s.ScheduleToStartTimeout.Duration(), + StartToCloseTimeout: s.StartToCloseTimeout.Duration(), + Summary: s.StaticSummary, + } + + if len(s.SearchAttribute) > 0 { + saMap, err := stringKeysJSONValues(s.SearchAttribute, false) + if err != nil { + return nexusCl, nil, fmt.Errorf("invalid search attribute values: %w", err) + } + if opts.SearchAttributes, err = mapToSearchAttributes(saMap); err != nil { + return nexusCl, nil, err + } + } + + if s.IdConflictPolicy.Value != "" { + v, err := stringToProtoEnum[enumspb.NexusOperationIdConflictPolicy]( + s.IdConflictPolicy.Value, + enumspb.NexusOperationIdConflictPolicy_shorthandValue, + enumspb.NexusOperationIdConflictPolicy_value) + if err != nil { + return nexusCl, nil, fmt.Errorf("invalid id-conflict-policy: %w", err) + } + opts.IDConflictPolicy = v + } + + if s.IdReusePolicy.Value != "" { + v, err := stringToProtoEnum[enumspb.NexusOperationIdReusePolicy]( + s.IdReusePolicy.Value, + enumspb.NexusOperationIdReusePolicy_shorthandValue, + enumspb.NexusOperationIdReusePolicy_value) + if err != nil { + return nexusCl, nil, fmt.Errorf("invalid id-reuse-policy: %w", err) + } + opts.IDReusePolicy = v + } + + // Build input payload + var input any + rawInput, err := p.buildRawInput() + if err != nil { + return nexusCl, nil, err + } + if len(rawInput) > 1 { + return nexusCl, nil, fmt.Errorf("nexus operations accept at most one input argument, got %d", len(rawInput)) + } + if len(rawInput) == 1 { + input = rawInput[0] + } + + return nexusCl, &nexusStartInput{ + operation: s.Operation, + input: input, + options: opts, + }, nil +} diff --git a/internal/temporalcli/commands.nexus_operation_test.go b/internal/temporalcli/commands.nexus_operation_test.go new file mode 100644 index 000000000..0418c22f4 --- /dev/null +++ b/internal/temporalcli/commands.nexus_operation_test.go @@ -0,0 +1,796 @@ +package temporalcli_test + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "testing" + "time" + + "github.com/google/uuid" + nexus "github.com/nexus-rpc/sdk-go/nexus" + "github.com/stretchr/testify/require" + nexuspb "go.temporal.io/api/nexus/v1" + "go.temporal.io/api/operatorservice/v1" + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/temporalnexus" + "go.temporal.io/sdk/workflow" +) + +func (s *SharedServerSuite) setupNexusEndpointAndWorker(t *testing.T) (string, *DevWorker) { + handlerWorkflow := func(ctx workflow.Context, input string) (string, error) { + return "got: " + input, nil + } + return s.setupNexusEndpointWithWorkflow(t, handlerWorkflow, "handler-") +} + +func (s *SharedServerSuite) setupNexusEndpointWithWorkflow( + t *testing.T, + handlerWorkflow func(workflow.Context, string) (string, error), + workflowIDPrefix string, +) (string, *DevWorker) { + endpointName := "test-ep-" + uuid.NewString()[:8] + + op := temporalnexus.NewWorkflowRunOperation( + "test-op", + handlerWorkflow, + func(ctx context.Context, input string, opts nexus.StartOperationOptions) (client.StartWorkflowOptions, error) { + return client.StartWorkflowOptions{ + ID: workflowIDPrefix + opts.RequestID, + }, nil + }, + ) + svc := nexus.NewService("test-service") + require.NoError(t, svc.Register(op)) + + w := s.DevServer.StartDevWorker(t, DevWorkerOptions{ + Workflows: []any{handlerWorkflow}, + NexusServices: []*nexus.Service{svc}, + }) + + _, err := s.Client.OperatorService().CreateNexusEndpoint(s.Context, &operatorservice.CreateNexusEndpointRequest{ + Spec: &nexuspb.EndpointSpec{ + Name: endpointName, + Target: &nexuspb.EndpointTarget{ + Variant: &nexuspb.EndpointTarget_Worker_{ + Worker: &nexuspb.EndpointTarget_Worker{ + Namespace: s.Namespace(), + TaskQueue: w.Options.TaskQueue, + }, + }, + }, + }, + }) + require.NoError(t, err) + + return endpointName, w +} + +func (s *SharedServerSuite) TestNexusOperationStart() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "start-op-" + uuid.NewString()[:8] + + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Started Nexus Operation") + s.Contains(res.Stdout.String(), opID) +} + +func (s *SharedServerSuite) TestNexusOperationExecute() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "exec-op-" + uuid.NewString()[:8] + + res := s.Execute( + "nexus", "operation", "execute", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "got: hello") +} + +func (s *SharedServerSuite) TestNexusOperationExecute_JSON() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "exec-json-op-" + uuid.NewString()[:8] + + res := s.Execute( + "nexus", "operation", "execute", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + "--output", "json", + ) + s.NoError(res.Err) + + var result struct { + OperationId string `json:"operationId"` + RunId string `json:"runId"` + Result json.RawMessage `json:"result"` + } + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &result)) + s.Equal(opID, result.OperationId) + s.Contains(string(result.Result), "got: hello") +} + +func (s *SharedServerSuite) TestNexusOperationDescribe() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "desc-op-" + uuid.NewString()[:8] + + // Start an operation first. + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // Describe it. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "describe", + "--address", s.Address(), + "--operation-id", opID, + ) + return res.Err == nil + }, 30*time.Second, 500*time.Millisecond) + + s.Contains(res.Stdout.String(), opID) + s.Contains(res.Stdout.String(), endpointName) + s.Contains(res.Stdout.String(), "test-service") +} + +func (s *SharedServerSuite) TestNexusOperationDescribe_JSON() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "desc-json-op-" + uuid.NewString()[:8] + + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "describe", + "--address", s.Address(), + "--operation-id", opID, + "--output", "json", + ) + return res.Err == nil + }, 30*time.Second, 500*time.Millisecond) + + s.NoError(res.Err) + s.Contains(res.Stdout.String(), opID) +} + +func (s *SharedServerSuite) TestNexusOperationCancel() { + blockingHandler := func(ctx workflow.Context, input string) (string, error) { + ctx.Done().Receive(ctx, nil) + return "", ctx.Err() + } + endpointName, w := s.setupNexusEndpointWithWorkflow(s.T(), blockingHandler, "cancel-handler-") + defer w.Stop() + + opID := "cancel-op-" + uuid.NewString()[:8] + + // Start operation. + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // Cancel it. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "cancel", + "--address", s.Address(), + "--operation-id", opID, + "--reason", "testing cancellation", + ) + return res.Err == nil + }, 30*time.Second, 500*time.Millisecond) + + s.Contains(res.Stdout.String(), "Cancellation requested") +} + +func (s *SharedServerSuite) TestNexusOperationTerminate() { + blockingHandler := func(ctx workflow.Context, input string) (string, error) { + ctx.Done().Receive(ctx, nil) + return "", ctx.Err() + } + endpointName, w := s.setupNexusEndpointWithWorkflow(s.T(), blockingHandler, "term-handler-") + defer w.Stop() + + opID := "term-op-" + uuid.NewString()[:8] + + // Start operation. + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // Terminate it. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "terminate", + "--address", s.Address(), + "--operation-id", opID, + "--reason", "testing termination", + ) + return res.Err == nil + }, 30*time.Second, 500*time.Millisecond) + + s.Contains(res.Stdout.String(), "Nexus Operation terminated") +} + +func (s *SharedServerSuite) TestNexusOperationList() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + // Start a couple of operations. + for i := 0; i < 2; i++ { + opID := fmt.Sprintf("list-op-%d-%s", i, uuid.NewString()[:8]) + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + } + + // List operations — wait until both are visible. + var res *CommandResult + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "list", + "--address", s.Address(), + ) + out := res.Stdout.String() + return res.Err == nil && + strings.Contains(out, "list-op-0") && + strings.Contains(out, "list-op-1") + }, 30*time.Second, 500*time.Millisecond) +} + +func (s *SharedServerSuite) TestNexusOperationCount() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "count-op-" + uuid.NewString()[:8] + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // Count operations. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "count", + "--address", s.Address(), + ) + return res.Err == nil && res.Stdout.String() != "" + }, 30*time.Second, 500*time.Millisecond) + + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Total:") +} + +func (s *SharedServerSuite) TestNexusOperationResult() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "result-op-" + uuid.NewString()[:8] + + // Start an operation. + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // Get result (blocks until completed). + res = s.Execute( + "nexus", "operation", "result", + "--address", s.Address(), + "--operation-id", opID, + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "got: hello") +} + +func (s *SharedServerSuite) TestNexusOperationResult_JSON() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "result-json-op-" + uuid.NewString()[:8] + + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + res = s.Execute( + "nexus", "operation", "result", + "--address", s.Address(), + "--operation-id", opID, + "--output", "json", + ) + s.NoError(res.Err) + + var result struct { + OperationId string `json:"operationId"` + Result json.RawMessage `json:"result"` + } + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &result)) + s.Equal(opID, result.OperationId) + s.Contains(string(result.Result), "got: hello") +} + +func (s *SharedServerSuite) TestNexusOperationStart_JSON() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "start-json-op-" + uuid.NewString()[:8] + + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + "--output", "json", + ) + s.NoError(res.Err) + + var result struct { + Endpoint string `json:"endpoint"` + Service string `json:"service"` + Operation string `json:"operation"` + OperationId string `json:"operationId"` + RunId string `json:"runId"` + } + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &result)) + s.Equal(endpointName, result.Endpoint) + s.Equal("test-service", result.Service) + s.Equal("test-op", result.Operation) + s.Equal(opID, result.OperationId) + s.NotEmpty(result.RunId) +} +func (s *SharedServerSuite) TestNexusOperationStart_OperationIDRequired() { + res := s.Execute( + "nexus", "operation", "start", + "--endpoint", "test-ep", + "--service", "test-service", + "--operation", "test-op", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "operation-id") +} + +func (s *SharedServerSuite) TestNexusOperationStart_ScheduleToCloseTimeout() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "timeout-op-" + uuid.NewString()[:8] + + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--schedule-to-close-timeout", "30m", + "--input", `"hello"`, + "--output", "json", + ) + s.NoError(res.Err) + + var result struct { + OperationId string `json:"operationId"` + } + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &result)) + s.Equal(opID, result.OperationId) + + // Verify the timeout is reflected in describe. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "describe", + "--address", s.Address(), + "--operation-id", opID, + "--output", "json", + ) + return res.Err == nil + }, 30*time.Second, 500*time.Millisecond) + s.Contains(res.Stdout.String(), opID) +} + +func (s *SharedServerSuite) TestNexusOperationStart_IdConflictPolicy() { + // Use a blocking handler so the operation stays running across the three + // start calls — IDConflictPolicy only triggers against an open operation. + blockingHandler := func(ctx workflow.Context, input string) (string, error) { + ctx.Done().Receive(ctx, nil) + return "", ctx.Err() + } + endpointName, w := s.setupNexusEndpointWithWorkflow(s.T(), blockingHandler, "conflict-handler-") + defer w.Stop() + + opID := "conflict-op-" + uuid.NewString()[:8] + + // Start first operation. + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // Start again with same ID and UseExisting policy — should succeed. + res = s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--id-conflict-policy", "UseExisting", + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // Start again with same ID and Fail policy — should fail. + res = s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--id-conflict-policy", "Fail", + "--input", `"hello"`, + ) + s.Error(res.Err) +} + +func (s *SharedServerSuite) TestNexusOperationList_JSON() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "list-json-op-" + uuid.NewString()[:8] + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "list", + "--address", s.Address(), + "--output", "json", + ) + return res.Err == nil && strings.Contains(res.Stdout.String(), opID) + }, 30*time.Second, 500*time.Millisecond) +} + +func (s *SharedServerSuite) TestNexusOperationList_Limit() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + // Start 3 operations. + for i := 0; i < 3; i++ { + opID := fmt.Sprintf("limit-op-%d-%s", i, uuid.NewString()[:8]) + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + } + + // List with limit=1, JSONL output so we can count entries. + var res *CommandResult + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "list", + "--address", s.Address(), + "--limit", "1", + "--output", "jsonl", + ) + return res.Err == nil && len(res.Stdout.String()) > 0 + }, 30*time.Second, 500*time.Millisecond) + + // Count JSON objects — each on its own line. + lines := 0 + for _, line := range strings.Split(strings.TrimSpace(res.Stdout.String()), "\n") { + if len(strings.TrimSpace(line)) > 0 { + lines++ + } + } + s.Equal(1, lines, "limit=1 should return exactly 1 operation") +} + +func (s *SharedServerSuite) TestNexusOperationCount_JSON() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "count-json-op-" + uuid.NewString()[:8] + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "count", + "--address", s.Address(), + "--output", "json", + ) + if res.Err != nil { + return false + } + // Proto JSON encodes int64 as a string; accept either form. + var result struct { + Count json.Number `json:"count"` + } + if err := json.Unmarshal(res.Stdout.Bytes(), &result); err != nil { + return false + } + n, err := result.Count.Int64() + return err == nil && n > 0 + }, 30*time.Second, 500*time.Millisecond) +} + +func (s *SharedServerSuite) TestNexusOperationCount_Query() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "count-query-op-" + uuid.NewString()[:8] + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // Count with a query that matches this specific operation. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "count", + "--address", s.Address(), + "--query", fmt.Sprintf(`OperationId = "%s"`, opID), + ) + return res.Err == nil && res.Stdout.String() != "" + }, 30*time.Second, 500*time.Millisecond) + + s.Contains(res.Stdout.String(), "Total:") +} + +func (s *SharedServerSuite) TestNexusOperationList_Query() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "query-op-" + uuid.NewString()[:8] + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + ) + s.NoError(res.Err) + + // List with query filter — wait until the operation appears. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "list", + "--address", s.Address(), + "--query", fmt.Sprintf(`OperationId = "%s"`, opID), + ) + return res.Err == nil && strings.Contains(res.Stdout.String(), opID) + }, 30*time.Second, 500*time.Millisecond) +} + +func (s *SharedServerSuite) TestNexusOperationStart_MissingRequiredFlags() { + // Missing --endpoint + res := s.Execute( + "nexus", "operation", "start", + "--service", "test-service", + "--operation", "test-op", + "--operation-id", "some-id", + ) + s.Error(res.Err) + + // Missing --service + res = s.Execute( + "nexus", "operation", "start", + "--endpoint", "test-ep", + "--operation", "test-op", + "--operation-id", "some-id", + ) + s.Error(res.Err) + + // Missing --operation + res = s.Execute( + "nexus", "operation", "start", + "--endpoint", "test-ep", + "--service", "test-service", + "--operation-id", "some-id", + ) + s.Error(res.Err) + + // Missing --operation-id + res = s.Execute( + "nexus", "operation", "start", + "--endpoint", "test-ep", + "--service", "test-service", + "--operation", "test-op", + ) + s.Error(res.Err) +} + +func (s *SharedServerSuite) TestNexusOperationExecute_MissingOperationID() { + res := s.Execute( + "nexus", "operation", "execute", + "--endpoint", "test-ep", + "--service", "test-service", + "--operation", "test-op", + ) + s.Error(res.Err) +} + +func (s *SharedServerSuite) TestNexusOperationStart_OptionalStartFlags() { + endpointName, w := s.setupNexusEndpointAndWorker(s.T()) + defer w.Stop() + + opID := "optional-flags-op-" + uuid.NewString()[:8] + summary := "this is the operation summary" + uniqueKW := "nexus-sa-" + uuid.NewString()[:8] + + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", endpointName, + "--service", "test-service", + "--operation", "test-op", + "--operation-id", opID, + "--input", `"hello"`, + "--static-summary", summary, + "--search-attribute", fmt.Sprintf(`CustomKeywordField="%s"`, uniqueKW), + "--schedule-to-close-timeout", "1m", + "--schedule-to-start-timeout", "30s", + "--start-to-close-timeout", "30s", + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), opID) + + // Describe and verify summary is reported back. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "describe", + "--address", s.Address(), + "--operation-id", opID, + ) + return res.Err == nil && strings.Contains(res.Stdout.String(), summary) + }, 30*time.Second, 500*time.Millisecond) + + // List with a query filter on the search attribute — confirms the SA was attached and indexed. + s.Eventually(func() bool { + res = s.Execute( + "nexus", "operation", "list", + "--address", s.Address(), + "--query", fmt.Sprintf(`CustomKeywordField = "%s"`, uniqueKW), + ) + return res.Err == nil && strings.Contains(res.Stdout.String(), opID) + }, 30*time.Second, 500*time.Millisecond) +} + +func (s *SharedServerSuite) TestNexusOperationStart_InvalidSearchAttribute() { + res := s.Execute( + "nexus", "operation", "start", + "--address", s.Address(), + "--endpoint", "test-ep", + "--service", "test-service", + "--operation", "test-op", + "--operation-id", "some-id", + "--search-attribute", "CustomKeywordField=not-valid-json", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "invalid search attribute") +} + 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..d3bf978d9 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" @@ -235,7 +236,7 @@ func (s *SharedServerSuite) TestSchedule_List() { out = res.Stdout.String() assert.Contains(t, out, schedId) }, 10*time.Second, time.Second) - s.ContainsOnSameLine(out, schedId, "DevWorkflow", "0s" /*jitter*/, "false", "nil" /*memo*/) + s.ContainsOnSameLine(out, schedId, "DevWorkflow", "0s" /*jitter*/, "false", "{}" /*memo*/) s.ContainsOnSameLine(out, "TestSchedule_List") // table @@ -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 90c5b2fe3..d0aa7d97a 100644 --- a/internal/temporalcli/commands.server.go +++ b/internal/temporalcli/commands.server.go @@ -92,6 +92,7 @@ func (t *TemporalServerStartDevCommand) run(cctx *CommandContext, args []string) } } opts.UIAssetPath, opts.UICodecEndpoint, opts.PublicPath = t.UiAssetPath, t.UiCodecEndpoint, t.UiPublicPath + opts.UIDisableNewsFetch = t.UiDisableNewsFetch } // Pragmas and dyn config var err error @@ -160,15 +161,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. @@ -191,7 +197,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) @@ -199,7 +205,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.system_nexus.go b/internal/temporalcli/commands.system_nexus.go new file mode 100644 index 000000000..da352b3c0 --- /dev/null +++ b/internal/temporalcli/commands.system_nexus.go @@ -0,0 +1,54 @@ +package temporalcli + +import ( + "context" + + commonpb "go.temporal.io/api/common/v1" + "go.temporal.io/api/proxy" + "go.temporal.io/api/workflowservice/v1" + "go.temporal.io/api/workflowservice/v1/workflowservicenexus" + "go.temporal.io/sdk/converter" + "google.golang.org/protobuf/proto" +) + +// systemNexusOpKey identifies a system Nexus operation by its (endpoint, operation) pair. +type systemNexusOpKey struct { + Endpoint string + Operation string +} + +// systemNexusOpTypes maps a system Nexus operation to the proto request and response types +// whose bytes are serialized in NexusOperationScheduled.Input and NexusOperationCompleted.Result. +type systemNexusOpTypes struct { + // NewRequest returns a fresh, zero-valued instance of the request proto. + NewRequest func() proto.Message + // NewResponse returns a fresh, zero-valued instance of the response proto. + NewResponse func() proto.Message +} + +// systemNexusOps is the global registry of known system Nexus operations on the +// __temporal_system endpoint. Add new entries here as the server adds support for more +// system operations. The keys' Operation values must match what the server records in +// NexusOperationScheduledEventAttributes.Operation. +// NOTE seankane: Part 2 of the System Operations work is to code generate this map from the +// go.temporal.io/api/workflowservice/v1/workflowservicenexus package. +var systemNexusOps = map[systemNexusOpKey]systemNexusOpTypes{ + { + Endpoint: temporalSystemNexusEndpoint, + Operation: workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.SignalWithStartWorkflowExecution.Name(), + }: { + NewRequest: func() proto.Message { return &workflowservice.SignalWithStartWorkflowExecutionRequest{} }, + NewResponse: func() proto.Message { return &workflowservice.SignalWithStartWorkflowExecutionResponse{} }, + }, +} + +// decodePayloadsInProto walks a proto message and applies codec.Decode to every Payload +// found inside it (including nested messages). The message is mutated in place. +func decodePayloadsInProto(ctx context.Context, msg proto.Message, codec converter.PayloadCodec) error { + return proxy.VisitPayloads(ctx, msg, proxy.VisitPayloadsOptions{ + SkipSearchAttributes: true, + Visitor: func(_ *proxy.VisitPayloadsContext, payloads []*commonpb.Payload) ([]*commonpb.Payload, error) { + return codec.Decode(payloads) + }, + }) +} diff --git a/internal/temporalcli/commands.system_nexus_test.go b/internal/temporalcli/commands.system_nexus_test.go new file mode 100644 index 000000000..ad160df2e --- /dev/null +++ b/internal/temporalcli/commands.system_nexus_test.go @@ -0,0 +1,332 @@ +package temporalcli + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + commonpb "go.temporal.io/api/common/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + "go.temporal.io/api/temporalproto" + "go.temporal.io/api/workflowservice/v1" + "google.golang.org/protobuf/proto" +) + +// markingCodec is a test [converter.PayloadCodec] that prefixes every payload's +// data with "decoded:" on Decode and tracks how many payloads it saw. It is used +// to verify that the codec is actually invoked on payloads nested inside opaque +// system Nexus operation bytes. +type markingCodec struct { + decodeCalls int +} + +func (c *markingCodec) Encode(payloads []*commonpb.Payload) ([]*commonpb.Payload, error) { + return payloads, nil +} + +func (c *markingCodec) Decode(payloads []*commonpb.Payload) ([]*commonpb.Payload, error) { + out := make([]*commonpb.Payload, len(payloads)) + for i, p := range payloads { + c.decodeCalls++ + out[i] = &commonpb.Payload{ + Metadata: p.Metadata, + Data: append([]byte("decoded:"), p.Data...), + } + } + return out, nil +} + +// failingCodec always returns an error from Decode; used to verify error propagation. +type failingCodec struct{} + +func (failingCodec) Encode(payloads []*commonpb.Payload) ([]*commonpb.Payload, error) { + return payloads, nil +} + +func (failingCodec) Decode(_ []*commonpb.Payload) ([]*commonpb.Payload, error) { + return nil, fmt.Errorf("codec decode failure for testing") +} + +func signalWithStartRequestPayload(t *testing.T, req *workflowservice.SignalWithStartWorkflowExecutionRequest) *commonpb.Payload { + t.Helper() + data, err := proto.Marshal(req) + require.NoError(t, err) + return &commonpb.Payload{ + Metadata: map[string][]byte{"encoding": []byte("binary/protobuf")}, + Data: data, + } +} + +func signalWithStartResponsePayload(t *testing.T, resp *workflowservice.SignalWithStartWorkflowExecutionResponse) *commonpb.Payload { + t.Helper() + data, err := proto.Marshal(resp) + require.NoError(t, err) + return &commonpb.Payload{ + Metadata: map[string][]byte{"encoding": []byte("binary/protobuf")}, + Data: data, + } +} + +func TestUnwrapAndInjectRequest_NilPayloadIsNoOp(t *testing.T) { + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.unwrapAndInjectRequest( + temporalSystemNexusEndpoint, "SignalWithStartWorkflowExecution", + nil, fields, temporalproto.CustomJSONMarshalOptions{})) + require.Empty(t, fields, "nil payload should not inject anything") +} + +func TestUnwrapAndInjectRequest_UnknownOperationIsNoOp(t *testing.T) { + p := &commonpb.Payload{Data: []byte("ignored")} + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.unwrapAndInjectRequest( + temporalSystemNexusEndpoint, "NotARealOperation", + p, fields, temporalproto.CustomJSONMarshalOptions{})) + require.Empty(t, fields, "unknown operation should be a no-op") +} + +func TestUnwrapAndInjectRequest_UnknownEndpointIsNoOp(t *testing.T) { + p := &commonpb.Payload{Data: []byte("ignored")} + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.unwrapAndInjectRequest( + "some-user-endpoint", "SignalWithStartWorkflowExecution", + p, fields, temporalproto.CustomJSONMarshalOptions{})) + require.Empty(t, fields, "non-system endpoint should be a no-op even if operation name matches") +} + +func TestUnwrapAndInjectRequest_BadProtoBytesReturnsError(t *testing.T) { + p := &commonpb.Payload{Data: []byte{0xff, 0xff, 0xff}} + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + err := iter.unwrapAndInjectRequest( + temporalSystemNexusEndpoint, "SignalWithStartWorkflowExecution", + p, fields, temporalproto.CustomJSONMarshalOptions{}) + require.Error(t, err) + require.ErrorContains(t, err, "failed unmarshaling system nexus payload") +} + +func TestUnwrapAndInjectResponse_NilPayloadIsNoOp(t *testing.T) { + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.unwrapAndInjectResponse( + temporalSystemNexusEndpoint, "SignalWithStartWorkflowExecution", + nil, fields, temporalproto.CustomJSONMarshalOptions{})) + require.Empty(t, fields) +} + +func TestUnwrapAndInjectResponse_UnknownOperationIsNoOp(t *testing.T) { + p := &commonpb.Payload{Data: []byte("ignored")} + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.unwrapAndInjectResponse( + temporalSystemNexusEndpoint, "NotARealOperation", + p, fields, temporalproto.CustomJSONMarshalOptions{})) + require.Empty(t, fields) +} + +func TestUnwrapAndInjectResponse_BadProtoBytesReturnsError(t *testing.T) { + p := &commonpb.Payload{Data: []byte{0xff, 0xff, 0xff}} + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + err := iter.unwrapAndInjectResponse( + temporalSystemNexusEndpoint, "SignalWithStartWorkflowExecution", + p, fields, temporalproto.CustomJSONMarshalOptions{}) + require.Error(t, err) + require.ErrorContains(t, err, "failed unmarshaling system nexus payload") +} + +func TestUnwrapAndInjectRequest_DecodesAllNestedPayloads(t *testing.T) { + // The Input/SignalInput fields hold the user-supplied payloads, which the codec + // should be applied to. The outer payload bytes are raw proto and are not codec-encoded. + inner1 := &commonpb.Payload{Metadata: map[string][]byte{"encoding": []byte("binary/plain")}, Data: []byte("hello")} + inner2 := &commonpb.Payload{Metadata: map[string][]byte{"encoding": []byte("binary/plain")}, Data: []byte("world")} + signalInner := &commonpb.Payload{Metadata: map[string][]byte{"encoding": []byte("binary/plain")}, Data: []byte("signal-arg")} + req := &workflowservice.SignalWithStartWorkflowExecutionRequest{ + Namespace: "ns", + WorkflowId: "wf", + Input: &commonpb.Payloads{Payloads: []*commonpb.Payload{inner1, inner2}}, + SignalInput: &commonpb.Payloads{Payloads: []*commonpb.Payload{signalInner}}, + } + p := signalWithStartRequestPayload(t, req) + + codec := &markingCodec{} + iter := &structuredHistoryIter{ctx: context.Background(), codec: codec} + fields := map[string]any{} + require.NoError(t, iter.unwrapAndInjectRequest( + temporalSystemNexusEndpoint, "SignalWithStartWorkflowExecution", + p, fields, temporalproto.CustomJSONMarshalOptions{})) + require.Equal(t, 3, codec.decodeCalls, "codec should have been invoked once per nested payload") + + unwrapped, ok := fields["unwrappedInput"].(map[string]any) + require.True(t, ok) + require.Equal(t, "ns", unwrapped["namespace"]) + require.Equal(t, "wf", unwrapped["workflowId"]) +} + +func TestUnwrapAndInjectRequest_CodecErrorPropagates(t *testing.T) { + req := &workflowservice.SignalWithStartWorkflowExecutionRequest{ + Input: &commonpb.Payloads{Payloads: []*commonpb.Payload{{Data: []byte("x")}}}, + } + p := signalWithStartRequestPayload(t, req) + iter := &structuredHistoryIter{ctx: context.Background(), codec: failingCodec{}} + fields := map[string]any{} + err := iter.unwrapAndInjectRequest( + temporalSystemNexusEndpoint, "SignalWithStartWorkflowExecution", + p, fields, temporalproto.CustomJSONMarshalOptions{}) + require.Error(t, err) + require.Equal(t, "failed decoding payloads in system nexus payload: codec decode failure for testing", err.Error()) +} + +func TestDecodePayloadsInProto_VisitsAllPayloads(t *testing.T) { + req := &workflowservice.SignalWithStartWorkflowExecutionRequest{ + Input: &commonpb.Payloads{Payloads: []*commonpb.Payload{ + {Data: []byte("a")}, + {Data: []byte("b")}, + }}, + SignalInput: &commonpb.Payloads{Payloads: []*commonpb.Payload{ + {Data: []byte("c")}, + }}, + } + codec := &markingCodec{} + require.NoError(t, decodePayloadsInProto(context.Background(), req, codec)) + require.Equal(t, 3, codec.decodeCalls) + require.Equal(t, []byte("decoded:a"), req.Input.Payloads[0].Data) + require.Equal(t, []byte("decoded:b"), req.Input.Payloads[1].Data) + require.Equal(t, []byte("decoded:c"), req.SignalInput.Payloads[0].Data) +} + +func TestInjectSystemNexusUnwrapped_ScheduledKnownOp(t *testing.T) { + req := &workflowservice.SignalWithStartWorkflowExecutionRequest{ + Namespace: "ns", + WorkflowId: "wf-xyz", + SignalName: "ping", + } + event := &historypb.HistoryEvent{ + EventId: 5, + EventType: enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, + Attributes: &historypb.HistoryEvent_NexusOperationScheduledEventAttributes{ + NexusOperationScheduledEventAttributes: &historypb.NexusOperationScheduledEventAttributes{ + Endpoint: temporalSystemNexusEndpoint, + Operation: "SignalWithStartWorkflowExecution", + Input: signalWithStartRequestPayload(t, req), + }, + }, + } + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.injectSystemNexusUnwrapped(event, fields, temporalproto.CustomJSONMarshalOptions{})) + + unwrapped, ok := fields["unwrappedInput"].(map[string]any) + require.True(t, ok, "expected unwrappedInput map to be set") + require.Equal(t, "ns", unwrapped["namespace"]) + require.Equal(t, "wf-xyz", unwrapped["workflowId"]) + require.Equal(t, "ping", unwrapped["signalName"]) +} + +func TestInjectSystemNexusUnwrapped_ScheduledUnknownEndpointSkipped(t *testing.T) { + req := &workflowservice.SignalWithStartWorkflowExecutionRequest{Namespace: "ns"} + event := &historypb.HistoryEvent{ + EventType: enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, + Attributes: &historypb.HistoryEvent_NexusOperationScheduledEventAttributes{ + NexusOperationScheduledEventAttributes: &historypb.NexusOperationScheduledEventAttributes{ + Endpoint: "user-endpoint", + Operation: "SignalWithStartWorkflowExecution", + Input: signalWithStartRequestPayload(t, req), + }, + }, + } + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.injectSystemNexusUnwrapped(event, fields, temporalproto.CustomJSONMarshalOptions{})) + _, ok := fields["unwrappedInput"] + require.False(t, ok, "non-system endpoint must not produce unwrappedInput") +} + +func TestInjectSystemNexusUnwrapped_CompletedUsesPriorScheduled(t *testing.T) { + resp := &workflowservice.SignalWithStartWorkflowExecutionResponse{ + RunId: "run-abc", + Started: true, + } + completed := &historypb.HistoryEvent{ + EventId: 6, + EventType: enumspb.EVENT_TYPE_NEXUS_OPERATION_COMPLETED, + Attributes: &historypb.HistoryEvent_NexusOperationCompletedEventAttributes{ + NexusOperationCompletedEventAttributes: &historypb.NexusOperationCompletedEventAttributes{ + ScheduledEventId: 5, + Result: signalWithStartResponsePayload(t, resp), + }, + }, + } + iter := &structuredHistoryIter{ + ctx: context.Background(), + systemNexusOps: map[int64]string{5: "SignalWithStartWorkflowExecution"}, + } + fields := map[string]any{} + require.NoError(t, iter.injectSystemNexusUnwrapped(completed, fields, temporalproto.CustomJSONMarshalOptions{})) + + unwrapped, ok := fields["unwrappedResult"].(map[string]any) + require.True(t, ok, "expected unwrappedResult map to be set") + require.Equal(t, "run-abc", unwrapped["runId"]) + require.Equal(t, true, unwrapped["started"]) +} + +func TestInjectSystemNexusUnwrapped_CompletedWithoutPriorScheduledSkipped(t *testing.T) { + completed := &historypb.HistoryEvent{ + EventType: enumspb.EVENT_TYPE_NEXUS_OPERATION_COMPLETED, + Attributes: &historypb.HistoryEvent_NexusOperationCompletedEventAttributes{ + NexusOperationCompletedEventAttributes: &historypb.NexusOperationCompletedEventAttributes{ + ScheduledEventId: 5, + Result: &commonpb.Payload{Data: []byte("garbage")}, + }, + }, + } + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.injectSystemNexusUnwrapped(completed, fields, temporalproto.CustomJSONMarshalOptions{})) + _, ok := fields["unwrappedResult"] + require.False(t, ok, "no prior scheduled means we don't know the op, so no unwrap") +} + +func TestInjectSystemNexusUnwrapped_NonNexusEventNoOp(t *testing.T) { + event := &historypb.HistoryEvent{ + EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, + Attributes: &historypb.HistoryEvent_WorkflowExecutionStartedEventAttributes{ + WorkflowExecutionStartedEventAttributes: &historypb.WorkflowExecutionStartedEventAttributes{}, + }, + } + iter := &structuredHistoryIter{ctx: context.Background()} + fields := map[string]any{} + require.NoError(t, iter.injectSystemNexusUnwrapped(event, fields, temporalproto.CustomJSONMarshalOptions{})) + require.Empty(t, fields) +} + +func TestInjectSystemNexusUnwrapped_AppliesCodecToScheduledInput(t *testing.T) { + req := &workflowservice.SignalWithStartWorkflowExecutionRequest{ + WorkflowId: "wf", + Input: &commonpb.Payloads{Payloads: []*commonpb.Payload{ + {Data: []byte("inner-input")}, + }}, + SignalInput: &commonpb.Payloads{Payloads: []*commonpb.Payload{ + {Data: []byte("inner-signal")}, + }}, + } + event := &historypb.HistoryEvent{ + EventType: enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, + Attributes: &historypb.HistoryEvent_NexusOperationScheduledEventAttributes{ + NexusOperationScheduledEventAttributes: &historypb.NexusOperationScheduledEventAttributes{ + Endpoint: temporalSystemNexusEndpoint, + Operation: "SignalWithStartWorkflowExecution", + Input: signalWithStartRequestPayload(t, req), + }, + }, + } + codec := &markingCodec{} + iter := &structuredHistoryIter{ctx: context.Background(), codec: codec} + fields := map[string]any{} + require.NoError(t, iter.injectSystemNexusUnwrapped(event, fields, temporalproto.CustomJSONMarshalOptions{})) + require.Equal(t, 2, codec.decodeCalls, "codec should run on both nested payloads") +} 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.go b/internal/temporalcli/commands.worker.deployment.go index a13d7dfc9..3dc25be79 100644 --- a/internal/temporalcli/commands.worker.deployment.go +++ b/internal/temporalcli/commands.worker.deployment.go @@ -22,6 +22,7 @@ import ( "go.temporal.io/sdk/client" "go.temporal.io/sdk/converter" "go.temporal.io/sdk/worker" + "google.golang.org/protobuf/types/known/fieldmaskpb" ) type versionSummariesRowType struct { @@ -894,17 +895,21 @@ func validateAWSLambdaProviderDetails(details map[string]any) error { // awsLambdaProviderDetailsPayload returns the encoded Payload representing AWS // Lambda compute provider details. -func (c *TemporalWorkerDeploymentCreateVersionCommand) awsLambdaProviderDetailsPayload() (*commonpb.Payload, error) { +func awsLambdaProviderDetailsPayload( + functionARN string, + assumeRoleARN string, + assumeRoleExternalID string, +) (*commonpb.Payload, error) { // Map keys from temporal-auto-scaled-workers: // https://github.com/temporalio/temporal-auto-scaled-workers/blob/c4a7e69b6504365d7e5326b0b8e6cd95e3293f96/wci/workflow/compute_provider/aws_lambda.go#L16-L20 providerDetails := map[string]any{ - "arn": c.AwsLambdaFunctionArn, + "arn": functionARN, } - if c.AwsLambdaAssumeRoleArn != "" { - providerDetails["role"] = c.AwsLambdaAssumeRoleArn + if assumeRoleARN != "" { + providerDetails["role"] = assumeRoleARN } - if c.AwsLambdaAssumeRoleExternalId != "" { - providerDetails["role_external_id"] = c.AwsLambdaAssumeRoleExternalId + if assumeRoleExternalID != "" { + providerDetails["role_external_id"] = assumeRoleExternalID } err := validateAWSLambdaProviderDetails(providerDetails) if err != nil { @@ -929,7 +934,11 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, var cc *computepb.ComputeConfig if c.AwsLambdaFunctionArn != "" { - detailsPayload, err := c.awsLambdaProviderDetailsPayload() + detailsPayload, err := awsLambdaProviderDetailsPayload( + c.AwsLambdaFunctionArn, + c.AwsLambdaAssumeRoleArn, + c.AwsLambdaAssumeRoleExternalId, + ) if err != nil { return err } @@ -948,6 +957,9 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, }, }, } + } else { + // We do not allow creation of an "empty" WDV. + return fmt.Errorf("missing configuration for compute provider") } request := &workflowservice.CreateWorkerDeploymentVersionRequest{ Namespace: ns, @@ -969,6 +981,82 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, return nil } +func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + ns := c.Parent.Parent.Namespace + buildID := c.BuildId + identity := c.Parent.Parent.Identity + deploymentName := c.DeploymentName + requestID := uuid.NewString() + + request := &workflowservice.UpdateWorkerDeploymentVersionComputeConfigRequest{ + Namespace: ns, + DeploymentVersion: &deployment.WorkerDeploymentVersion{ + DeploymentName: deploymentName, + BuildId: buildID, + }, + Identity: identity, + RequestId: requestID, + } + + if c.Remove { + if c.AwsLambdaFunctionArn != "" || c.AwsLambdaAssumeRoleArn != "" || c.AwsLambdaAssumeRoleExternalId != "" { + return fmt.Errorf("--remove cannot be combined with --aws-lambda-function-arn, --aws-lambda-assume-role-arn, or --aws-lambda-assume-role-external-id") + } + request.RemoveComputeConfigScalingGroups = []string{"default"} + } else { + detailsPayload, err := awsLambdaProviderDetailsPayload( + c.AwsLambdaFunctionArn, + c.AwsLambdaAssumeRoleArn, + c.AwsLambdaAssumeRoleExternalId, + ) + if err != nil { + return err + } + sg := &computepb.ComputeConfigScalingGroup{ + Provider: &computepb.ComputeProvider{ + Type: "aws-lambda", + Details: detailsPayload, + }, + Scaler: &computepb.ComputeScaler{ + // Hard-coded: no-sync is the only supported algorithm + // in temporal-auto-scaled-workers as of 2026-04-01. + Type: "no-sync", + }, + } + updatePaths := []string{ + "provider.details", + } + ccScalingGroups := map[string]*computepb.ComputeConfigScalingGroupUpdate{ + "default": &computepb.ComputeConfigScalingGroupUpdate{ + ScalingGroup: sg, + UpdateMask: &fieldmaskpb.FieldMask{ + Paths: updatePaths, + }, + }, + } + request.ComputeConfigScalingGroups = ccScalingGroups + + } + + _, err = cl.WorkflowService().UpdateWorkerDeploymentVersionComputeConfig(cctx, request) + if err != nil { + return fmt.Errorf("error updating worker deployment version compute config: %w", err) + } + + if c.Remove { + cctx.Printer.Println("Successfully removed worker deployment version compute config") + } else { + cctx.Printer.Println("Successfully updated worker deployment version compute config") + } + return nil +} + func (c *TemporalWorkerDeploymentDeleteVersionCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) if err != nil { diff --git a/internal/temporalcli/commands.worker.deployment_test.go b/internal/temporalcli/commands.worker.deployment_test.go index 8b97854e3..b1323532f 100644 --- a/internal/temporalcli/commands.worker.deployment_test.go +++ b/internal/temporalcli/commands.worker.deployment_test.go @@ -1153,49 +1153,67 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_EmptyComputeConfig // worker deployment create-version` CLI command. noComputeConfigBuildID := uuid.NewString() + // Attempting to create a WDV with no compute provider configuration should + // fail. res := s.Execute( "worker", "deployment", "create-version", "--address", s.Address(), "--deployment-name", deploymentName, "--build-id", noComputeConfigBuildID, ) - s.NoError(res.Err) - s.Contains(res.Stdout.String(), "Successfully created worker deployment version") + s.Error(res.Err) + s.ErrorContains(res.Err, "missing configuration for compute provider") - // Wait for the deployment version to appear - s.EventuallyWithT(func(t *assert.CollectT) { + // TODO(jaypipes): Uncomment the test block below once support for Cloud + // Run is added. When we add another compute provider, we can test for the + // return of a duplicate WDV. Until that point, we cannot create an "empty" + // WDV to test the build ID existence. + + /* res := s.Execute( - "worker", "deployment", "describe-version", + "worker", "deployment", "create-version", "--address", s.Address(), "--deployment-name", deploymentName, "--build-id", noComputeConfigBuildID, ) - assert.NoError(t, res.Err) - }, 30*time.Second, 100*time.Millisecond) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Successfully created worker deployment version") + + // Wait for the deployment version to appear + s.EventuallyWithT(func(t *assert.CollectT) { + res := s.Execute( + "worker", "deployment", "describe-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", noComputeConfigBuildID, + ) + assert.NoError(t, res.Err) + }, 30*time.Second, 100*time.Millisecond) - // Check that there is no compute config returned for this WDV - res = s.Execute( - "worker", "deployment", "describe-version", - "--address", s.Address(), - "--deployment-name", deploymentName, - "--build-id", noComputeConfigBuildID, - "--output", "json", - ) - s.NoError(res.Err) - var jsonOut jsonDeploymentVersionInfoType - s.NoError(json.Unmarshal(res.Stdout.Bytes(), &jsonOut)) - s.Nil(jsonOut.ComputeConfig, "ComputeConfig should be nil.") + // Check that there is no compute config returned for this WDV + res = s.Execute( + "worker", "deployment", "describe-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", noComputeConfigBuildID, + "--output", "json", + ) + s.NoError(res.Err) + var jsonOut jsonDeploymentVersionInfoType + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &jsonOut)) + s.Nil(jsonOut.ComputeConfig, "ComputeConfig should be nil.") - // Attempting to create a WDV with the same BuildID should fail with a - // conflict error. - res = s.Execute( - "worker", "deployment", "create-version", - "--address", s.Address(), - "--deployment-name", deploymentName, - "--build-id", noComputeConfigBuildID, - ) - s.Error(res.Err) - s.ErrorContains(res.Err, "already exists") + // Attempting to create a WDV with the same BuildID should fail with a + // conflict error. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", noComputeConfigBuildID, + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "already exists") + */ } func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { @@ -1292,6 +1310,32 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { ) s.Error(res.Err) s.ErrorContains(res.Err, "missing required AWS Lambda provider detail: role") + + // Attempting to update the compute config for a non-existent WDV + // should fail. + nonExistingBuildID := "non-existing" + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", nonExistingBuildID, + "--aws-lambda-function-arn", invokeARN, + "--aws-lambda-assume-role-arn", assumeRoleARN, + "--aws-lambda-assume-role-external-id", assumeRoleExternalID, + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "build ID 'non-existing' not found") + + // Same for attempting to remove the compute config for a non-existent WDV. + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", nonExistingBuildID, + "--remove", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "build ID 'non-existing' not found") } // TODO(jaypipes): Enable this test when we have a way of ensuring AWS resource @@ -1384,4 +1428,30 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_LambdaComputeConfi jsonOut := jsonDeploymentVersionInfoType{} s.NoError(json.Unmarshal(res.Stdout.Bytes(), &jsonOut)) s.NotNil(jsonOut.ComputeConfig, "ComputeConfig should not be nil.") + + // We should be able to update the compute config. + invokeARN2 := "arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:2" + assumeRoleARN2 := "arn:aws:iam::123456789012:role/MyServiceRole2" + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + "--aws-lambda-function-arn", invokeARN2, + "--aws-lambda-assume-role-arn", assumeRoleARN2, + "--aws-lambda-assume-role-external-id", assumeRoleExternalID, + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Successfully updated worker deployment version compute config") + + // As well as remove the compute config. + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + "--remove", + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Successfully removed worker deployment version compute config") } diff --git a/internal/temporalcli/commands.workflow.go b/internal/temporalcli/commands.workflow.go index 20ebec8d1..b6736c4f3 100644 --- a/internal/temporalcli/commands.workflow.go +++ b/internal/temporalcli/commands.workflow.go @@ -137,9 +137,10 @@ func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args [] } } - if c.VersioningOverrideBehavior.Value == "pinned" { + if c.VersioningOverrideBehavior.Value == "pinned" || + c.VersioningOverrideBehavior.Value == "one_time" { if c.VersioningOverrideDeploymentName == "" || c.VersioningOverrideBuildId == "" { - return fmt.Errorf("missing deployment name and/or build id with 'pinned' behavior") + return fmt.Errorf("missing deployment name and/or build id with '%s' behavior", c.VersioningOverrideBehavior.Value) } } @@ -164,9 +165,10 @@ func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args [] overrideChange = &client.VersioningOverrideChange{ Value: &client.AutoUpgradeVersioningOverride{}, } + case "one_time": default: return fmt.Errorf( - "invalid deployment behavior: %v, valid values are: 'unspecified', 'pinned', and 'auto_upgrade'", + "invalid deployment behavior: %v, valid values are: 'unspecified', 'pinned', 'auto_upgrade', and 'one_time'", c.VersioningOverrideBehavior, ) } @@ -176,36 +178,39 @@ func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args [] return err } else if exec != nil { - _, err := cl.UpdateWorkflowExecutionOptions(cctx, client.UpdateWorkflowExecutionOptionsRequest{ - WorkflowId: exec.WorkflowId, - RunId: exec.RunId, - WorkflowExecutionOptionsChanges: client.WorkflowExecutionOptionsChanges{ - VersioningOverride: overrideChange, - }, - }) + if c.VersioningOverrideBehavior.Value == "one_time" { + err = c.updateWorkflowExecutionOptionsWithProto(cctx, cl, exec, oneTimeVersioningOverrideToProto(c.VersioningOverrideDeploymentName, c.VersioningOverrideBuildId)) + } else { + _, err = cl.UpdateWorkflowExecutionOptions(cctx, client.UpdateWorkflowExecutionOptionsRequest{ + WorkflowId: exec.WorkflowId, + RunId: exec.RunId, + WorkflowExecutionOptionsChanges: client.WorkflowExecutionOptionsChanges{ + VersioningOverride: overrideChange, + }, + }) + } if err != nil { return fmt.Errorf("failed to update workflow options: %w", err) } cctx.Printer.Println("Update workflow options succeeded") } else { // Run batch - var workflowExecutionOptions *workflowpb.WorkflowExecutionOptions - protoMask, err := fieldmaskpb.New(workflowExecutionOptions, "versioning_override") - if err != nil { - return fmt.Errorf("invalid field mask: %w", err) - } - var protoVerOverride *workflowpb.VersioningOverride - if overrideChange != nil { + if c.VersioningOverrideBehavior.Value == "one_time" { + protoVerOverride = oneTimeVersioningOverrideToProto(c.VersioningOverrideDeploymentName, c.VersioningOverrideBuildId) + } else if overrideChange != nil { protoVerOverride = versioningOverrideToProto(overrideChange.Value) } + workflowExecutionOptions, protoMask, err := workflowExecutionOptionsForVersioningOverride(protoVerOverride) + if err != nil { + return fmt.Errorf("invalid field mask: %w", err) + } + batchReq.Operation = &workflowservice.StartBatchOperationRequest_UpdateWorkflowOptionsOperation{ UpdateWorkflowOptionsOperation: &batch.BatchOperationUpdateWorkflowExecutionOptions{ - Identity: c.Parent.Identity, - WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - VersioningOverride: protoVerOverride, - }, - UpdateMask: protoMask, + Identity: c.Parent.Identity, + WorkflowExecutionOptions: workflowExecutionOptions, + UpdateMask: protoMask, }, } if err := startBatchJob(cctx, cl, batchReq); err != nil { @@ -215,6 +220,43 @@ func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args [] return nil } +func (c *TemporalWorkflowUpdateOptionsCommand) updateWorkflowExecutionOptionsWithProto( + ctx context.Context, + cl client.Client, + exec *common.WorkflowExecution, + override *workflowpb.VersioningOverride, +) error { + workflowExecutionOptions, protoMask, err := workflowExecutionOptionsForVersioningOverride(override) + if err != nil { + return fmt.Errorf("invalid field mask: %w", err) + } + + _, err = cl.WorkflowService().UpdateWorkflowExecutionOptions(ctx, &workflowservice.UpdateWorkflowExecutionOptionsRequest{ + Namespace: c.Parent.Namespace, + WorkflowExecution: &common.WorkflowExecution{ + WorkflowId: exec.GetWorkflowId(), + RunId: exec.GetRunId(), + }, + WorkflowExecutionOptions: workflowExecutionOptions, + UpdateMask: protoMask, + Identity: c.Parent.Identity, + }) + return err +} + +func workflowExecutionOptionsForVersioningOverride( + override *workflowpb.VersioningOverride, +) (*workflowpb.WorkflowExecutionOptions, *fieldmaskpb.FieldMask, error) { + workflowExecutionOptions := &workflowpb.WorkflowExecutionOptions{ + VersioningOverride: override, + } + protoMask, err := fieldmaskpb.New(workflowExecutionOptions, "versioning_override") + if err != nil { + return nil, nil, err + } + return workflowExecutionOptions, protoMask, nil +} + func (c *TemporalWorkflowMetadataCommand) run(cctx *CommandContext, _ []string) error { return queryHelper(cctx, c.Parent, PayloadInputOptions{}, metadataQueryName, nil, c.RejectCondition, c.WorkflowReferenceOptions) @@ -569,13 +611,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 { @@ -750,10 +798,7 @@ func versioningOverrideToProto(versioningOverride client.VersioningOverride) *wo Override: &workflowpb.VersioningOverride_Pinned{ Pinned: &workflowpb.VersioningOverride_PinnedOverride{ Behavior: workflowpb.VersioningOverride_PINNED_OVERRIDE_BEHAVIOR_PINNED, - Version: &deploymentpb.WorkerDeploymentVersion{ - DeploymentName: v.Version.DeploymentName, - BuildId: v.Version.BuildID, - }, + Version: workerDeploymentVersionToProto(v.Version.DeploymentName, v.Version.BuildID), }, }, } @@ -766,3 +811,20 @@ func versioningOverrideToProto(versioningOverride client.VersioningOverride) *wo return nil } } + +func workerDeploymentVersionToProto(deploymentName, buildID string) *deploymentpb.WorkerDeploymentVersion { + return &deploymentpb.WorkerDeploymentVersion{ + DeploymentName: deploymentName, + BuildId: buildID, + } +} + +func oneTimeVersioningOverrideToProto(deploymentName, buildID string) *workflowpb.VersioningOverride { + return &workflowpb.VersioningOverride{ + Override: &workflowpb.VersioningOverride_OneTime{ + OneTime: &workflowpb.VersioningOverride_OneTimeOverride{ + TargetDeploymentVersion: workerDeploymentVersionToProto(deploymentName, buildID), + }, + }, + } +} diff --git a/internal/temporalcli/commands.workflow_exec.go b/internal/temporalcli/commands.workflow_exec.go index e115a5303..cef46fdf2 100644 --- a/internal/temporalcli/commands.workflow_exec.go +++ b/internal/temporalcli/commands.workflow_exec.go @@ -25,7 +25,9 @@ import ( "go.temporal.io/api/temporalproto" "go.temporal.io/api/workflowservice/v1" "go.temporal.io/sdk/client" + "go.temporal.io/sdk/converter" "go.temporal.io/sdk/temporal" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" ) @@ -40,7 +42,7 @@ func (c *TemporalWorkflowStartCommand) run(cctx *CommandContext, args []string) } func (c *TemporalWorkflowExecuteCommand) run(cctx *CommandContext, args []string) error { - cl, err := dialClient(cctx, &c.Parent.ClientOptions) + cl, codec, err := dialClientWithCodec(cctx, &c.Parent.ClientOptions) if err != nil { return err } @@ -63,6 +65,7 @@ func (c *TemporalWorkflowExecuteCommand) run(cctx *CommandContext, args []string runID: run.GetRunID(), includeDetails: c.Detailed, follow: true, + codec: codec, } if err := iter.print(cctx); err != nil && cctx.Err() == nil { return fmt.Errorf("displaying history failed: %w", err) @@ -704,6 +707,68 @@ func coloredEventType(e enums.EventType) string { return fn(e.String()) } +const temporalSystemNexusEndpoint = "__temporal_system" + +// systemNexusOpDisplayName returns a display name for Nexus operation events on the +// "__temporal_system" endpoint, replacing the generic NexusOperation* name with the +// actual operation name + event-type suffix. Returns "" for all other events. +// Populates s.systemNexusOps when it encounters a qualifying Scheduled event. +func (s *structuredHistoryIter) systemNexusOpDisplayName(event *history.HistoryEvent) string { + if s.systemNexusOps == nil { + s.systemNexusOps = make(map[int64]string) + } + switch event.EventType { + case enums.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: + attr := event.GetNexusOperationScheduledEventAttributes() + if attr == nil || attr.Endpoint != temporalSystemNexusEndpoint { + return "" + } + s.systemNexusOps[event.EventId] = attr.Operation + return attr.Operation + "Scheduled" + case enums.EVENT_TYPE_NEXUS_OPERATION_STARTED: + attr := event.GetNexusOperationStartedEventAttributes() + if attr == nil { + return "" + } + if op, ok := s.systemNexusOps[attr.ScheduledEventId]; ok { + return op + "Started" + } + case enums.EVENT_TYPE_NEXUS_OPERATION_COMPLETED: + attr := event.GetNexusOperationCompletedEventAttributes() + if attr == nil { + return "" + } + if op, ok := s.systemNexusOps[attr.ScheduledEventId]; ok { + return op + "Completed" + } + case enums.EVENT_TYPE_NEXUS_OPERATION_FAILED: + attr := event.GetNexusOperationFailedEventAttributes() + if attr == nil { + return "" + } + if op, ok := s.systemNexusOps[attr.ScheduledEventId]; ok { + return op + "Failed" + } + case enums.EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: + attr := event.GetNexusOperationTimedOutEventAttributes() + if attr == nil { + return "" + } + if op, ok := s.systemNexusOps[attr.ScheduledEventId]; ok { + return op + "TimedOut" + } + case enums.EVENT_TYPE_NEXUS_OPERATION_CANCELED: + attr := event.GetNexusOperationCanceledEventAttributes() + if attr == nil { + return "" + } + if op, ok := s.systemNexusOps[attr.ScheduledEventId]; ok { + return op + "Canceled" + } + } + return "" +} + type structuredHistoryIter struct { ctx context.Context client client.Client @@ -725,6 +790,14 @@ type structuredHistoryIter struct { reverseBuf []*history.HistoryEvent reverseNextToken []byte reverseStarted bool + + // maps NexusOperationScheduled eventId → operation name for __temporal_system endpoint events + systemNexusOps map[int64]string + + // codec is the remote payload codec configured for this client, or nil if none. Used to + // decode payloads nested inside system Nexus operation request/response bytes so they + // can be rendered alongside the rest of the event fields. + codec converter.PayloadCodec } func (s *structuredHistoryIter) print(cctx *CommandContext) error { @@ -751,7 +824,11 @@ func (s *structuredHistoryIter) print(cctx *CommandContext) error { first = false // Print section heading - cctx.Printer.Printlnf("--------------- [%v] %v ---------------", event.EventId, event.EventType) + eventTypeName := event.EventType.String() + if name := s.systemNexusOpDisplayName(event); name != "" { + eventTypeName = name + } + cctx.Printer.Printlnf("--------------- [%v] %v ---------------", event.EventId, eventTypeName) // Convert the event to dot-delimited-field/value and print one per line fields, err := s.flattenFields(cctx, event) if err != nil { @@ -786,10 +863,14 @@ func (s *structuredHistoryIter) Next() (any, error) { return nil, nil } // Build data + typeName := s.systemNexusOpDisplayName(event) + if typeName == "" { + typeName = coloredEventType(event.EventType) + } data := structuredHistoryEvent{ ID: event.EventId, Time: event.EventTime.AsTime().Format(time.RFC3339), - Type: coloredEventType(event.EventType), + Type: typeName, } // Follow continue as new (forward only; reverse traversal stays within the requested run) @@ -900,6 +981,12 @@ func (s *structuredHistoryIter) flattenFields( delete(fieldsMap, k) } } + // For system Nexus operation events, deserialize the request/response payload bytes + // into the typed proto, decode any payloads nested inside via the codec, and merge + // the decoded view into the output under "unwrappedInput" / "unwrappedResult". + if err := s.injectSystemNexusUnwrapped(event, fieldsMap, opts); err != nil { + return nil, err + } // Flatten JSON map and sort fields, err := s.flattenJSONValue(nil, "", fieldsMap) if err != nil { @@ -909,6 +996,101 @@ func (s *structuredHistoryIter) flattenFields( return fields, nil } +// injectSystemNexusUnwrapped, if the given event is a known system Nexus operation, +// deserializes the underlying request (on Scheduled) or response (on Completed) proto, +// decodes any payloads nested inside via the codec, and inserts the resulting JSON +// representation into fieldsMap under "unwrappedInput" / "unwrappedResult". +func (s *structuredHistoryIter) injectSystemNexusUnwrapped( + event *history.HistoryEvent, + fieldsMap map[string]any, + opts temporalproto.CustomJSONMarshalOptions, +) error { + switch event.EventType { + case enums.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: + attr := event.GetNexusOperationScheduledEventAttributes() + if attr == nil { + return nil + } + return s.unwrapAndInjectRequest(attr.GetEndpoint(), attr.GetOperation(), attr.GetInput(), fieldsMap, opts) + case enums.EVENT_TYPE_NEXUS_OPERATION_COMPLETED: + attr := event.GetNexusOperationCompletedEventAttributes() + if attr == nil { + return nil + } + op, ok := s.systemNexusOps[attr.GetScheduledEventId()] + if !ok { + return nil + } + return s.unwrapAndInjectResponse(temporalSystemNexusEndpoint, op, attr.GetResult(), fieldsMap, opts) + } + return nil +} + +// unwrapAndInjectRequest looks up the registered request proto for (endpoint, operation), +// then injects the decoded view under "unwrappedInput". No-op for unregistered ops. +func (s *structuredHistoryIter) unwrapAndInjectRequest( + endpoint, operation string, + payload *commonpb.Payload, + fieldsMap map[string]any, + opts temporalproto.CustomJSONMarshalOptions, +) error { + types, ok := systemNexusOps[systemNexusOpKey{Endpoint: endpoint, Operation: operation}] + if !ok { + return nil + } + return s.unwrapAndInject(types.NewRequest(), payload, fieldsMap, "unwrappedInput", opts) +} + +// unwrapAndInjectResponse looks up the registered response proto for (endpoint, operation), +// then injects the decoded view under "unwrappedResult". No-op for unregistered ops. +func (s *structuredHistoryIter) unwrapAndInjectResponse( + endpoint, operation string, + payload *commonpb.Payload, + fieldsMap map[string]any, + opts temporalproto.CustomJSONMarshalOptions, +) error { + types, ok := systemNexusOps[systemNexusOpKey{Endpoint: endpoint, Operation: operation}] + if !ok { + return nil + } + return s.unwrapAndInject(types.NewResponse(), payload, fieldsMap, "unwrappedResult", opts) +} + +// unwrapAndInject is the shared body: unmarshal payload bytes into the supplied proto, +// decode any payloads nested inside via the codec, marshal back to JSON, and inject the +// resulting map into fieldsMap[key]. A nil payload is a no-op. +func (s *structuredHistoryIter) unwrapAndInject( + msg proto.Message, + payload *commonpb.Payload, + fieldsMap map[string]any, + key string, + opts temporalproto.CustomJSONMarshalOptions, +) error { + if payload == nil { + return nil + } + if err := proto.Unmarshal(payload.Data, msg); err != nil { + return fmt.Errorf("failed unmarshaling system nexus payload: %w", err) + } + if s.codec != nil { + if err := decodePayloadsInProto(s.ctx, msg, s.codec); err != nil { + return fmt.Errorf("failed decoding payloads in system nexus payload: %w", err) + } + } + unwrappedJSON, err := opts.Marshal(msg) + if err != nil { + return fmt.Errorf("failed marshaling unwrapped system nexus payload: %w", err) + } + dec := json.NewDecoder(bytes.NewReader(unwrappedJSON)) + dec.UseNumber() + var unwrappedMap map[string]any + if err := dec.Decode(&unwrappedMap); err != nil { + return fmt.Errorf("failed unmarshaling unwrapped JSON for system nexus payload: %w", err) + } + fieldsMap[key] = unwrappedMap + return nil +} + func (s *structuredHistoryIter) flattenJSONValue( to []eventFieldValue, field string, 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_reset_update_options.go b/internal/temporalcli/commands.workflow_reset_update_options.go index ad6644604..53399578e 100644 --- a/internal/temporalcli/commands.workflow_reset_update_options.go +++ b/internal/temporalcli/commands.workflow_reset_update_options.go @@ -3,9 +3,7 @@ package temporalcli import ( "fmt" - deploymentpb "go.temporal.io/api/deployment/v1" workflowpb "go.temporal.io/api/workflow/v1" - "google.golang.org/protobuf/types/known/fieldmaskpb" ) func (c *TemporalWorkflowResetWithWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args []string) error { @@ -14,12 +12,14 @@ func (c *TemporalWorkflowResetWithWorkflowUpdateOptionsCommand) run(cctx *Comman return err } - if c.VersioningOverrideBehavior.Value == "pinned" { + if c.VersioningOverrideBehavior.Value == "pinned" || + c.VersioningOverrideBehavior.Value == "one_time" { if c.VersioningOverrideDeploymentName == "" || c.VersioningOverrideBuildId == "" { - return fmt.Errorf("deployment name and build id are required with 'pinned' behavior") + return fmt.Errorf("deployment name and build id are required with '%s' behavior", c.VersioningOverrideBehavior.Value) } } - if c.VersioningOverrideBehavior.Value != "pinned" { + if c.VersioningOverrideBehavior.Value != "pinned" && + c.VersioningOverrideBehavior.Value != "one_time" { if c.VersioningOverrideDeploymentName != "" || c.VersioningOverrideBuildId != "" { return fmt.Errorf("cannot set deployment name or build id with %v behavior", c.VersioningOverrideBehavior.Value) } @@ -31,28 +31,30 @@ func (c *TemporalWorkflowResetWithWorkflowUpdateOptionsCommand) run(cctx *Comman } defer cl.Close() - VersioningOverride := &workflowpb.VersioningOverride{} + var versioningOverride *workflowpb.VersioningOverride switch c.VersioningOverrideBehavior.Value { case "pinned": - VersioningOverride.Override = &workflowpb.VersioningOverride_Pinned{ - Pinned: &workflowpb.VersioningOverride_PinnedOverride{ - Behavior: workflowpb.VersioningOverride_PINNED_OVERRIDE_BEHAVIOR_PINNED, - Version: &deploymentpb.WorkerDeploymentVersion{ - DeploymentName: c.VersioningOverrideDeploymentName, - BuildId: c.VersioningOverrideBuildId, + versioningOverride = &workflowpb.VersioningOverride{ + Override: &workflowpb.VersioningOverride_Pinned{ + Pinned: &workflowpb.VersioningOverride_PinnedOverride{ + Behavior: workflowpb.VersioningOverride_PINNED_OVERRIDE_BEHAVIOR_PINNED, + Version: workerDeploymentVersionToProto(c.VersioningOverrideDeploymentName, c.VersioningOverrideBuildId), }, }, } + case "one_time": + versioningOverride = oneTimeVersioningOverrideToProto(c.VersioningOverrideDeploymentName, c.VersioningOverrideBuildId) case "auto_upgrade": - VersioningOverride.Override = &workflowpb.VersioningOverride_AutoUpgrade{ - AutoUpgrade: true, + versioningOverride = &workflowpb.VersioningOverride{ + Override: &workflowpb.VersioningOverride_AutoUpgrade{ + AutoUpgrade: true, + }, } default: - return fmt.Errorf("invalid deployment behavior: %v, valid values are: 'pinned', and 'auto_upgrade'", c.VersioningOverrideBehavior.Value) + return fmt.Errorf("invalid deployment behavior: %v, valid values are: 'pinned', 'auto_upgrade', and 'one_time'", c.VersioningOverrideBehavior.Value) } - var workflowExecutionOptions *workflowpb.WorkflowExecutionOptions - protoMask, err := fieldmaskpb.New(workflowExecutionOptions, "versioning_override") + workflowExecutionOptions, protoMask, err := workflowExecutionOptionsForVersioningOverride(versioningOverride) if err != nil { return fmt.Errorf("invalid field mask: %w", err) } @@ -60,10 +62,8 @@ func (c *TemporalWorkflowResetWithWorkflowUpdateOptionsCommand) run(cctx *Comman postOp := &workflowpb.PostResetOperation{ Variant: &workflowpb.PostResetOperation_UpdateWorkflowOptions_{ UpdateWorkflowOptions: &workflowpb.PostResetOperation_UpdateWorkflowOptions{ - WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - VersioningOverride: VersioningOverride, - }, - UpdateMask: protoMask, + WorkflowExecutionOptions: workflowExecutionOptions, + UpdateMask: protoMask, }, }, } diff --git a/internal/temporalcli/commands.workflow_reset_update_options_test.go b/internal/temporalcli/commands.workflow_reset_update_options_test.go index 8e2bf9a19..f6148e89c 100644 --- a/internal/temporalcli/commands.workflow_reset_update_options_test.go +++ b/internal/temporalcli/commands.workflow_reset_update_options_test.go @@ -3,16 +3,19 @@ package temporalcli_test import ( "context" "fmt" + "sync" "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.temporal.io/api/enums/v1" + workflowpb "go.temporal.io/api/workflow/v1" "go.temporal.io/api/workflowservice/v1" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" + "google.golang.org/grpc" ) func (s *SharedServerSuite) TestWorkflow_ResetWithWorkflowUpdateOptions_ValidatesArguments_MissingRequiredFlag() { @@ -54,6 +57,115 @@ func (s *SharedServerSuite) TestWorkflow_ResetWithWorkflowUpdateOptions_Validate require.Contains(s.T(), res.Err.Error(), "cannot set deployment name or build id with auto_upgrade behavior") } +func (s *SharedServerSuite) TestWorkflow_ResetWithWorkflowUpdateOptions_Single_OneTimeOverrideRequest() { + workflowID := "workflow-" + uuid.NewString() + deploymentName := "deployment-" + uuid.NewString() + buildID := "build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var resetRequest *workflowservice.ResetWorkflowExecutionRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.ResetWorkflowExecutionRequest); ok { + lastRequestLock.Lock() + resetRequest = r + lastRequestLock.Unlock() + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "reset", "with-workflow-update-options", + "--address", s.Address(), + "-w", workflowID, + "-e", "3", + "--reason", "test-reset-one-time", + "--versioning-override-behavior", "one_time", + "--versioning-override-deployment-name", deploymentName, + "--versioning-override-build-id", buildID, + ) + require.NoError(s.T(), res.Err) + + lastRequestLock.Lock() + req := resetRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + require.Equal(s.T(), s.Namespace(), req.GetNamespace()) + require.Equal(s.T(), workflowID, req.GetWorkflowExecution().GetWorkflowId()) + require.Equal(s.T(), int64(3), req.GetWorkflowTaskFinishEventId()) + s.requireOneTimePostResetOverride(req.GetPostResetOperations(), deploymentName, buildID) +} + +func (s *SharedServerSuite) TestWorkflow_ResetBatchWithWorkflowUpdateOptions_OneTimeOverrideRequest() { + query := "WorkflowType = 'YourWorkflowType'" + deploymentName := "deployment-" + uuid.NewString() + buildID := "build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var startBatchRequest *workflowservice.StartBatchOperationRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.StartBatchOperationRequest); ok { + lastRequestLock.Lock() + startBatchRequest = r + lastRequestLock.Unlock() + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "reset", "with-workflow-update-options", + "--address", s.Address(), + "--query", query, + "--yes", + "-t", "FirstWorkflowTask", + "--reason", "test-batch-reset-one-time", + "--versioning-override-behavior", "one_time", + "--versioning-override-deployment-name", deploymentName, + "--versioning-override-build-id", buildID, + ) + require.NoError(s.T(), res.Err) + + lastRequestLock.Lock() + req := startBatchRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + require.Equal(s.T(), s.Namespace(), req.GetNamespace()) + require.Equal(s.T(), query, req.GetVisibilityQuery()) + resetOperation := req.GetResetOperation() + require.NotNil(s.T(), resetOperation) + s.requireOneTimePostResetOverride(resetOperation.GetPostResetOperations(), deploymentName, buildID) +} + +func (s *SharedServerSuite) requireOneTimePostResetOverride(postOps []*workflowpb.PostResetOperation, deploymentName, buildID string) { + require.Len(s.T(), postOps, 1) + updateOptions := postOps[0].GetUpdateWorkflowOptions() + require.NotNil(s.T(), updateOptions) + require.Equal(s.T(), []string{"versioning_override"}, updateOptions.GetUpdateMask().GetPaths()) + override := updateOptions.GetWorkflowExecutionOptions().GetVersioningOverride() + require.NotNil(s.T(), override) + oneTime := override.GetOneTime() + require.NotNil(s.T(), oneTime) + require.Equal(s.T(), deploymentName, oneTime.GetTargetDeploymentVersion().GetDeploymentName()) + require.Equal(s.T(), buildID, oneTime.GetTargetDeploymentVersion().GetBuildId()) +} + func (s *SharedServerSuite) TestWorkflow_ResetWithWorkflowUpdateOptions_Single_AutoUpgradeBehavior() { var wfExecutions int s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { diff --git a/internal/temporalcli/commands.workflow_show_test.go b/internal/temporalcli/commands.workflow_show_test.go new file mode 100644 index 000000000..0f70449a5 --- /dev/null +++ b/internal/temporalcli/commands.workflow_show_test.go @@ -0,0 +1,173 @@ +package temporalcli + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" +) + +func TestSystemNexusOpDisplayName(t *testing.T) { + const op = "SignalWithStartWorkflowExecution" // present in the global systemNexusOps registry + const futureOp = "FutureUnregisteredOperation" // intentionally NOT in the global registry + + newScheduled := func(eventID int64, endpoint, operation string) *historypb.HistoryEvent { + return &historypb.HistoryEvent{ + EventId: eventID, + EventType: enums.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, + Attributes: &historypb.HistoryEvent_NexusOperationScheduledEventAttributes{ + NexusOperationScheduledEventAttributes: &historypb.NexusOperationScheduledEventAttributes{ + Endpoint: endpoint, Operation: operation, + }, + }, + } + } + // terminalEvent returns a NexusOperation* event of the given type referencing scheduledEventID. + // Returns nil for unsupported event types. + terminalEvent := func(eventType enums.EventType, scheduledEventID int64) *historypb.HistoryEvent { + e := &historypb.HistoryEvent{EventId: scheduledEventID + 1, EventType: eventType} + switch eventType { + case enums.EVENT_TYPE_NEXUS_OPERATION_STARTED: + e.Attributes = &historypb.HistoryEvent_NexusOperationStartedEventAttributes{ + NexusOperationStartedEventAttributes: &historypb.NexusOperationStartedEventAttributes{ScheduledEventId: scheduledEventID}, + } + case enums.EVENT_TYPE_NEXUS_OPERATION_COMPLETED: + e.Attributes = &historypb.HistoryEvent_NexusOperationCompletedEventAttributes{ + NexusOperationCompletedEventAttributes: &historypb.NexusOperationCompletedEventAttributes{ScheduledEventId: scheduledEventID}, + } + case enums.EVENT_TYPE_NEXUS_OPERATION_FAILED: + e.Attributes = &historypb.HistoryEvent_NexusOperationFailedEventAttributes{ + NexusOperationFailedEventAttributes: &historypb.NexusOperationFailedEventAttributes{ScheduledEventId: scheduledEventID}, + } + case enums.EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: + e.Attributes = &historypb.HistoryEvent_NexusOperationTimedOutEventAttributes{ + NexusOperationTimedOutEventAttributes: &historypb.NexusOperationTimedOutEventAttributes{ScheduledEventId: scheduledEventID}, + } + case enums.EVENT_TYPE_NEXUS_OPERATION_CANCELED: + e.Attributes = &historypb.HistoryEvent_NexusOperationCanceledEventAttributes{ + NexusOperationCanceledEventAttributes: &historypb.NexusOperationCanceledEventAttributes{ScheduledEventId: scheduledEventID}, + } + } + return e + } + + // priorScheduled returns a setup that creates an iter and pre-processes the given Scheduled + // event. In forward traversal this matches the natural event order; in reverse traversal it + // simulates the iter having pre-scanned the buffered history. + priorScheduled := func(scheduled *historypb.HistoryEvent) func(reverse bool) *structuredHistoryIter { + return func(reverse bool) *structuredHistoryIter { + s := &structuredHistoryIter{reverse: reverse} + s.systemNexusOpDisplayName(scheduled) + return s + } + } + empty := func(reverse bool) *structuredHistoryIter { + return &structuredHistoryIter{reverse: reverse} + } + + schedSystem := newScheduled(5, temporalSystemNexusEndpoint, op) + schedSystemFutureOp := newScheduled(5, temporalSystemNexusEndpoint, futureOp) + schedNonSystem := newScheduled(7, "some-other-endpoint", op) + + tests := []struct { + name string + setup func(reverse bool) *structuredHistoryIter + event *historypb.HistoryEvent + wantName string + wantEmpty bool + }{ + // Scheduled cases + { + name: "scheduled system endpoint records op and returns name", + setup: empty, + event: schedSystem, + wantName: op + "Scheduled", + }, + { + name: "scheduled non-system endpoint returns empty", + setup: empty, + event: schedNonSystem, + wantEmpty: true, + }, + { + // Display name does not consult the global systemNexusOps registry; any op on the + // __temporal_system endpoint produces a display name. (Payload unwrap-and-inject is + // the layer that requires registry membership; see TestUnwrapAndInjectRequest_*.) + name: "scheduled system endpoint with op not in registry still returns name", + setup: empty, + event: schedSystemFutureOp, + wantName: futureOp + "Scheduled", + }, + // Terminal cases with a prior Scheduled in the instance map + { + name: "started after system scheduled", + setup: priorScheduled(schedSystem), + event: terminalEvent(enums.EVENT_TYPE_NEXUS_OPERATION_STARTED, 5), + wantName: op + "Started", + }, + { + // Confirms terminal events use the iter's instance map (which captured the unregistered + // op from its Scheduled event), regardless of global-registry membership. + name: "started after system scheduled with op not in registry", + setup: priorScheduled(schedSystemFutureOp), + event: terminalEvent(enums.EVENT_TYPE_NEXUS_OPERATION_STARTED, 5), + wantName: futureOp + "Started", + }, + { + name: "completed after system scheduled", + setup: priorScheduled(schedSystem), + event: terminalEvent(enums.EVENT_TYPE_NEXUS_OPERATION_COMPLETED, 5), + wantName: op + "Completed", + }, + { + name: "completed with no prior scheduled returns empty", + setup: empty, + event: terminalEvent(enums.EVENT_TYPE_NEXUS_OPERATION_COMPLETED, 5), + wantEmpty: true, + }, + { + name: "failed after system scheduled", + setup: priorScheduled(schedSystem), + event: terminalEvent(enums.EVENT_TYPE_NEXUS_OPERATION_FAILED, 5), + wantName: op + "Failed", + }, + { + name: "timed out after system scheduled", + setup: priorScheduled(schedSystem), + event: terminalEvent(enums.EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT, 5), + wantName: op + "TimedOut", + }, + { + name: "canceled after system scheduled", + setup: priorScheduled(schedSystem), + event: terminalEvent(enums.EVENT_TYPE_NEXUS_OPERATION_CANCELED, 5), + wantName: op + "Canceled", + }, + } + + // The function is direction-agnostic: given the same prior state, it returns the same + // display name whether iter.reverse is true or false. Running each case in both modes + // pins that contract so a future change can't silently introduce direction-sensitive + // branching without breaking these tests. + for _, reverse := range []bool{false, true} { + modeName := "forward" + if reverse { + modeName = "reverse" + } + t.Run(modeName, func(t *testing.T) { + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + iter := tc.setup(reverse) + require.Equal(t, reverse, iter.reverse, "test setup must mark iter reverse=%v", reverse) + name := iter.systemNexusOpDisplayName(tc.event) + if tc.wantEmpty { + require.Empty(t, name) + } else { + require.Equal(t, tc.wantName, name) + } + }) + } + }) + } +} diff --git a/internal/temporalcli/commands.workflow_test.go b/internal/temporalcli/commands.workflow_test.go index 20d6d256b..d1d728853 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" @@ -158,7 +159,7 @@ func (s *SharedServerSuite) testSignalBatchWorkflow(json bool) *CommandResult { s.CommandHarness.Stdin.WriteString("y\n") } res := s.Execute(args...) - s.NoError(res.Err) + require.NoError(s.T(), res.Err) // Confirm that all workflows complete with the signal value for _, run := range runs { @@ -201,7 +202,7 @@ func (s *SharedServerSuite) TestWorkflow_Delete_SingleWorkflowSuccess() { "--workflow-id", run.GetID(), "--yes", ) - s.NoError(res.Err) + require.NoError(s.T(), 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") @@ -250,7 +251,7 @@ func (s *SharedServerSuite) TestWorkflow_Delete_BatchWorkflowSuccess() { "-y", ) s.NoError(res.Err) - s.Contains(res.Stdout.String(), "Start batch against approximately 2 workflow(s)") + s.Contains(res.Stdout.String(), "Start batch against workflows matching query") s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") @@ -387,6 +388,51 @@ func (s *SharedServerSuite) TestWorkflow_Terminate_BatchWorkflowSuccess_JSON() { s.NotEmpty(jsonRes["batchJobId"]) } +func (s *SharedServerSuite) TestWorkflow_Terminate_BatchWorkflow_SkipsCountWhenYes() { + s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { + ctx.Done().Receive(ctx, nil) + return nil, ctx.Err() + }) + + searchAttr := "keyword-" + uuid.NewString() + run, err := s.Client.ExecuteWorkflow( + s.Context, + client.StartWorkflowOptions{ + TaskQueue: s.Worker().Options.TaskQueue, + SearchAttributes: map[string]any{"CustomKeywordField": searchAttr}, + }, + DevWorkflow, + "ignored", + ) + s.NoError(err) + s.Eventually(func() bool { + resp, err := s.Client.ListWorkflow(s.Context, &workflowservice.ListWorkflowExecutionsRequest{ + Query: "CustomKeywordField = '" + searchAttr + "'", + }) + s.NoError(err) + return len(resp.Executions) == 1 + }, 3*time.Second, 100*time.Millisecond) + + res := s.Execute( + "workflow", "terminate", + "--address", s.Address(), + "--query", "CustomKeywordField = '"+searchAttr+"'", + "--reason", "skip-count-test", + "--yes", + ) + s.NoError(res.Err) + + // Prompt text should show the query, not the count + s.NotContains(res.Stdout.String(), "approximately") + s.Contains(res.Stdout.String(), "matching query") + + // Confirm workflow was terminated + s.Eventually(func() bool { + err := run.Get(s.Context, nil) + return err != nil && strings.Contains(err.Error(), "terminated") + }, 5*time.Second, 100*time.Millisecond) +} + func (s *SharedServerSuite) testTerminateBatchWorkflow( total int, rps float32, @@ -674,6 +720,59 @@ func (s *SharedServerSuite) TestWorkflow_Batch_Update_Options_Versioning_Overrid }, 10*time.Second, 100*time.Millisecond) } +func (s *SharedServerSuite) TestWorkflow_Batch_Update_Options_OneTimeOverride() { + query := "WorkflowType = 'YourWorkflowType'" + deploymentName := "deployment-" + uuid.NewString() + buildID := "build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var startBatchRequest *workflowservice.StartBatchOperationRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.StartBatchOperationRequest); ok { + lastRequestLock.Lock() + startBatchRequest = r + lastRequestLock.Unlock() + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "update-options", + "--address", s.Address(), + "--query", query, + "--yes", + "--versioning-override-behavior", "one_time", + "--versioning-override-deployment-name", deploymentName, + "--versioning-override-build-id", buildID, + ) + s.NoError(res.Err) + + lastRequestLock.Lock() + req := startBatchRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + s.Equal(s.Namespace(), req.GetNamespace()) + s.Equal(query, req.GetVisibilityQuery()) + updateOptionsOperation := req.GetUpdateWorkflowOptionsOperation() + require.NotNil(s.T(), updateOptionsOperation) + s.Equal([]string{"versioning_override"}, updateOptionsOperation.GetUpdateMask().GetPaths()) + override := updateOptionsOperation.GetWorkflowExecutionOptions().GetVersioningOverride() + require.NotNil(s.T(), override) + oneTime := override.GetOneTime() + require.NotNil(s.T(), oneTime) + s.Equal(deploymentName, oneTime.GetTargetDeploymentVersion().GetDeploymentName()) + s.Equal(buildID, oneTime.GetTargetDeploymentVersion().GetBuildId()) +} + func (s *SharedServerSuite) TestWorkflow_Update_Options_Versioning_Override() { buildId1 := "id1-" + uuid.NewString() buildId2 := "id2-" + uuid.NewString() @@ -843,6 +942,56 @@ func (s *SharedServerSuite) TestWorkflow_Update_Options_Versioning_Override() { s.Nil(versioningInfo.VersioningOverride) } +func (s *SharedServerSuite) TestWorkflow_Update_Options_OneTimeOverride() { + workflowID := "workflow-" + uuid.NewString() + deploymentName := "deployment-" + uuid.NewString() + buildID := "build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var updateOptionsRequest *workflowservice.UpdateWorkflowExecutionOptionsRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.UpdateWorkflowExecutionOptionsRequest); ok { + lastRequestLock.Lock() + updateOptionsRequest = r + lastRequestLock.Unlock() + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "update-options", + "--address", s.Address(), + "--workflow-id", workflowID, + "--versioning-override-behavior", "one_time", + "--versioning-override-deployment-name", deploymentName, + "--versioning-override-build-id", buildID, + ) + s.NoError(res.Err) + + lastRequestLock.Lock() + req := updateOptionsRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + s.Equal(s.Namespace(), req.GetNamespace()) + s.Equal(workflowID, req.GetWorkflowExecution().GetWorkflowId()) + s.Equal([]string{"versioning_override"}, req.GetUpdateMask().GetPaths()) + override := req.GetWorkflowExecutionOptions().GetVersioningOverride() + require.NotNil(s.T(), override) + oneTime := override.GetOneTime() + require.NotNil(s.T(), oneTime) + s.Equal(deploymentName, oneTime.GetTargetDeploymentVersion().GetDeploymentName()) + s.Equal(buildID, oneTime.GetTargetDeploymentVersion().GetBuildId()) +} + func (s *SharedServerSuite) TestWorkflow_Update_Execute() { workflowUpdateTest{ s: s, diff --git a/internal/temporalcli/commands.workflow_view.go b/internal/temporalcli/commands.workflow_view.go index 9d142c3c8..7becc5af3 100644 --- a/internal/temporalcli/commands.workflow_view.go +++ b/internal/temporalcli/commands.workflow_view.go @@ -210,14 +210,20 @@ func (c *TemporalWorkflowDescribeCommand) run(cctx *CommandContext, args []strin overrideBehavior := "" overridePinnedVersionDeploymentName := "" overridePinnedVersionBuildId := "" + overrideTargetVersionDeploymentName := "" + overrideTargetVersionBuildId := "" if vInfo.VersioningOverride != nil { switch vInfo.VersioningOverride.GetOverride().(type) { case *workflow.VersioningOverride_Pinned: - overridePinnedVersionDeploymentName = vInfo.GetVersioningOverride().GetPinned().Version.DeploymentName - overridePinnedVersionBuildId = vInfo.GetVersioningOverride().GetPinned().Version.BuildId + overridePinnedVersionDeploymentName = vInfo.GetVersioningOverride().GetPinned().GetVersion().GetDeploymentName() + overridePinnedVersionBuildId = vInfo.GetVersioningOverride().GetPinned().GetVersion().GetBuildId() overrideBehavior = enums.VERSIONING_BEHAVIOR_PINNED.String() case *workflow.VersioningOverride_AutoUpgrade: overrideBehavior = enums.VERSIONING_BEHAVIOR_AUTO_UPGRADE.String() + case *workflow.VersioningOverride_OneTime: + overrideTargetVersionDeploymentName = vInfo.GetVersioningOverride().GetOneTime().GetTargetDeploymentVersion().GetDeploymentName() + overrideTargetVersionBuildId = vInfo.GetVersioningOverride().GetOneTime().GetTargetDeploymentVersion().GetBuildId() + overrideBehavior = "OneTime" } } _ = cctx.Printer.PrintStructured(struct { @@ -227,6 +233,8 @@ func (c *TemporalWorkflowDescribeCommand) run(cctx *CommandContext, args []strin OverrideBehavior string `cli:",cardOmitEmpty"` OverridePinnedVersionDeploymentName string `cli:",cardOmitEmpty"` OverridePinnedVersionBuildId string `cli:",cardOmitEmpty"` + OverrideTargetVersionDeploymentName string `cli:",cardOmitEmpty"` + OverrideTargetVersionBuildId string `cli:",cardOmitEmpty"` TransitionVersionDeploymentName string `cli:",cardOmitEmpty"` TransitionVersionBuildId string `cli:",cardOmitEmpty"` }{ @@ -236,6 +244,8 @@ func (c *TemporalWorkflowDescribeCommand) run(cctx *CommandContext, args []strin OverrideBehavior: overrideBehavior, OverridePinnedVersionDeploymentName: overridePinnedVersionDeploymentName, OverridePinnedVersionBuildId: overridePinnedVersionBuildId, + OverrideTargetVersionDeploymentName: overrideTargetVersionDeploymentName, + OverrideTargetVersionBuildId: overrideTargetVersionBuildId, TransitionVersionDeploymentName: vInfo.VersionTransition.GetDeploymentVersion().GetDeploymentName(), TransitionVersionBuildId: vInfo.VersionTransition.GetDeploymentVersion().GetBuildId(), }, printer.StructuredOptions{}) @@ -324,8 +334,14 @@ func (c *TemporalWorkflowDescribeCommand) run(cctx *CommandContext, args []strin _ = cctx.Printer.PrintStructured(resp.PendingChildren, printer.StructuredOptions{}) } - cctx.Printer.Println(color.MagentaString("Pending Nexus Operations: %v", len(resp.PendingNexusOperations))) - if len(resp.PendingNexusOperations) > 0 { + var pendingNexusOps []*workflow.PendingNexusOperationInfo + for _, op := range resp.PendingNexusOperations { + if op.GetEndpoint() != temporalSystemNexusEndpoint { + pendingNexusOps = append(pendingNexusOps, op) + } + } + cctx.Printer.Println(color.MagentaString("Pending Nexus Operations: %v", len(pendingNexusOps))) + if len(pendingNexusOps) > 0 { cctx.Printer.Println() ops := make([]struct { Endpoint string @@ -348,8 +364,8 @@ func (c *TemporalWorkflowDescribeCommand) run(cctx *CommandContext, args []strin CancelationLastAttemptCompleteTime time.Time `cli:",cardOmitEmpty"` CancelationLastAttemptFailure *failure.Failure `cli:",cardOmitEmpty"` CancelationBlockedReason string `cli:",cardOmitEmpty"` - }, len(resp.PendingNexusOperations)) - for i, op := range resp.PendingNexusOperations { + }, len(pendingNexusOps)) + for i, op := range pendingNexusOps { ops[i].Endpoint = op.GetEndpoint() ops[i].Service = op.GetService() ops[i].Operation = op.GetOperation() @@ -558,7 +574,7 @@ func (c *TemporalWorkflowShowCommand) run(cctx *CommandContext, _ []string) erro } // Call describe - cl, err := dialClient(cctx, &c.Parent.ClientOptions) + cl, codec, err := dialClientWithCodec(cctx, &c.Parent.ClientOptions) if err != nil { return err } @@ -574,6 +590,7 @@ func (c *TemporalWorkflowShowCommand) run(cctx *CommandContext, _ []string) erro includeDetails: c.Detailed, follow: c.Follow, reverse: c.Reverse, + codec: codec, } if !cctx.JSONOutput { cctx.Printer.Println(color.MagentaString("Progress:")) diff --git a/internal/temporalcli/commands.workflow_view_test.go b/internal/temporalcli/commands.workflow_view_test.go index 31cf22b33..0c3929305 100644 --- a/internal/temporalcli/commands.workflow_view_test.go +++ b/internal/temporalcli/commands.workflow_view_test.go @@ -7,22 +7,32 @@ import ( "fmt" "strconv" "strings" + "sync" "time" "github.com/google/uuid" "github.com/nexus-rpc/sdk-go/nexus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/temporalio/cli/internal/temporalcli" + commandpb "go.temporal.io/api/command/v1" "go.temporal.io/api/common/v1" + deploymentpb "go.temporal.io/api/deployment/v1" "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" nexuspb "go.temporal.io/api/nexus/v1" "go.temporal.io/api/operatorservice/v1" + taskqueuepb "go.temporal.io/api/taskqueue/v1" + workflowpb "go.temporal.io/api/workflow/v1" "go.temporal.io/api/workflowservice/v1" + "go.temporal.io/api/workflowservice/v1/workflowservicenexus" "go.temporal.io/sdk/client" "go.temporal.io/sdk/temporal" "go.temporal.io/sdk/temporalnexus" "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" + "go.temporal.io/server/common/payloads" + "google.golang.org/grpc" ) func (s *SharedServerSuite) TestWorkflow_Describe_ActivityFailing() { @@ -731,6 +741,108 @@ func (s *SharedServerSuite) TestWorkflow_Describe_Deployment() { s.Nil(versioningInfo.VersioningOverride) } +func (s *SharedServerSuite) TestWorkflow_Describe_OneTimeOverride() { + workflowID := "workflow-" + uuid.NewString() + runID := "run-" + uuid.NewString() + taskQueue := "task-queue-" + uuid.NewString() + baseDeploymentName := "base-deployment-" + uuid.NewString() + baseBuildID := "base-build-" + uuid.NewString() + targetDeploymentName := "target-deployment-" + uuid.NewString() + targetBuildID := "target-build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var describeRequest *workflowservice.DescribeWorkflowExecutionRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.DescribeWorkflowExecutionRequest); ok { + lastRequestLock.Lock() + describeRequest = r + lastRequestLock.Unlock() + + resp := reply.(*workflowservice.DescribeWorkflowExecutionResponse) + resp.WorkflowExecutionInfo = &workflowpb.WorkflowExecutionInfo{ + Execution: &common.WorkflowExecution{ + WorkflowId: workflowID, + RunId: runID, + }, + Type: &common.WorkflowType{Name: "DevWorkflow"}, + Status: enums.WORKFLOW_EXECUTION_STATUS_RUNNING, + TaskQueue: taskQueue, + VersioningInfo: &workflowpb.WorkflowExecutionVersioningInfo{ + Behavior: enums.VERSIONING_BEHAVIOR_AUTO_UPGRADE, + DeploymentVersion: &deploymentpb.WorkerDeploymentVersion{ + DeploymentName: baseDeploymentName, + BuildId: baseBuildID, + }, + VersioningOverride: &workflowpb.VersioningOverride{ + Override: &workflowpb.VersioningOverride_OneTime{ + OneTime: &workflowpb.VersioningOverride_OneTimeOverride{ + TargetDeploymentVersion: &deploymentpb.WorkerDeploymentVersion{ + DeploymentName: targetDeploymentName, + BuildId: targetBuildID, + }, + }, + }, + }, + }, + } + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "describe", + "--address", s.Address(), + "-w", workflowID, + "-r", runID, + ) + require.NoError(s.T(), res.Err) + + lastRequestLock.Lock() + req := describeRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + s.Equal(workflowID, req.GetExecution().GetWorkflowId()) + s.Equal(runID, req.GetExecution().GetRunId()) + + out := res.Stdout.String() + s.ContainsOnSameLine(out, "Behavior", "AutoUpgrade") + s.ContainsOnSameLine(out, "DeploymentName", baseDeploymentName) + s.ContainsOnSameLine(out, "BuildId", baseBuildID) + s.ContainsOnSameLine(out, "OverrideBehavior", "OneTime") + s.ContainsOnSameLine(out, "OverrideTargetVersionDeploymentName", targetDeploymentName) + s.ContainsOnSameLine(out, "OverrideTargetVersionBuildId", targetBuildID) + + res = s.Execute( + "workflow", "describe", + "--address", s.Address(), + "-w", workflowID, + "-r", runID, + "--output", "json", + ) + require.NoError(s.T(), res.Err) + + var jsonResp workflowservice.DescribeWorkflowExecutionResponse + require.NoError(s.T(), temporalcli.UnmarshalProtoJSONWithOptions(res.Stdout.Bytes(), &jsonResp, true)) + versioningInfo := jsonResp.GetWorkflowExecutionInfo().GetVersioningInfo() + require.NotNil(s.T(), versioningInfo) + s.Equal(enums.VERSIONING_BEHAVIOR_AUTO_UPGRADE, versioningInfo.GetBehavior()) + s.Equal(baseDeploymentName, versioningInfo.GetDeploymentVersion().GetDeploymentName()) + s.Equal(baseBuildID, versioningInfo.GetDeploymentVersion().GetBuildId()) + oneTime := versioningInfo.GetVersioningOverride().GetOneTime() + require.NotNil(s.T(), oneTime) + s.Equal(targetDeploymentName, oneTime.GetTargetDeploymentVersion().GetDeploymentName()) + s.Equal(targetBuildID, oneTime.GetTargetDeploymentVersion().GetBuildId()) +} + func (s *SharedServerSuite) TestWorkflow_Describe_NexusOperationAndCallback() { handlerWorkflowID := uuid.NewString() endpointName := validEndpointName(s.T()) @@ -1148,3 +1260,262 @@ func (s *SharedServerSuite) TestWorkflow_Describe_RootWorkflow() { s.Equal(run.GetID(), jsonOut.WorkflowExecutionInfo.RootExecution.GetWorkflowId()) s.Equal(run.GetRunID(), jsonOut.WorkflowExecutionInfo.RootExecution.GetRunId()) } + +// temporalSystemNexusEndpointName is the reserved endpoint name for Temporal system +// Nexus operations (mirrors the unexported temporalSystemNexusEndpoint constant in the +// production code; reproduced here as a literal because the constant is not exported). +const temporalSystemNexusEndpointName = "__temporal_system" + +// runSystemNexusSWSWorkflow drives a SignalWithStartWorkflowExecution Nexus operation +// against the __temporal_system endpoint from a raw-polled caller workflow, waits for it +// to complete, then completes the caller. Returns the caller workflow ID. The target +// workflow is registered for cleanup via s.T().Cleanup. The SDK cannot be used here +// because it refuses endpoints with the reserved "__temporal_" prefix. +func (s *SharedServerSuite) runSystemNexusSWSWorkflow(ctx context.Context) string { + callerTaskQueue := "cli-sys-nexus-caller-" + uuid.NewString() + targetTaskQueue := "cli-sys-nexus-target-" + uuid.NewString() + targetWorkflowID := "cli-sys-nexus-target-" + uuid.NewString() + callerWorkflowID := "cli-sys-nexus-caller-" + uuid.NewString() + + startResp, err := s.Client.WorkflowService().StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{ + Namespace: s.Namespace(), + WorkflowId: callerWorkflowID, + WorkflowType: &common.WorkflowType{Name: "caller-workflow"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: callerTaskQueue, Kind: enums.TASK_QUEUE_KIND_NORMAL}, + RequestId: uuid.NewString(), + }) + s.NoError(err) + + pollResp, err := s.Client.WorkflowService().PollWorkflowTaskQueue(ctx, &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: s.Namespace(), + TaskQueue: &taskqueuepb.TaskQueue{Name: callerTaskQueue, Kind: enums.TASK_QUEUE_KIND_NORMAL}, + Identity: "cli-test", + }) + s.NoError(err) + s.Equal(callerWorkflowID, pollResp.WorkflowExecution.WorkflowId) + s.Equal(startResp.RunId, pollResp.WorkflowExecution.RunId) + + _, err = s.Client.WorkflowService().RespondWorkflowTaskCompleted(ctx, &workflowservice.RespondWorkflowTaskCompletedRequest{ + Identity: "cli-test", + TaskToken: pollResp.TaskToken, + Commands: []*commandpb.Command{ + { + CommandType: enums.COMMAND_TYPE_SCHEDULE_NEXUS_OPERATION, + Attributes: &commandpb.Command_ScheduleNexusOperationCommandAttributes{ + ScheduleNexusOperationCommandAttributes: &commandpb.ScheduleNexusOperationCommandAttributes{ + Endpoint: temporalSystemNexusEndpointName, + Service: workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.ServiceName, + Operation: workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.SignalWithStartWorkflowExecution.Name(), + Input: payloads.MustEncodeSingle(&workflowservice.SignalWithStartWorkflowExecutionRequest{ + WorkflowId: targetWorkflowID, + SignalName: "cli-test-signal", + WorkflowType: &common.WorkflowType{Name: "target-workflow"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: targetTaskQueue}, + }), + }, + }, + }, + }, + }) + s.NoError(err) + s.T().Cleanup(func() { + _ = s.Client.TerminateWorkflow(context.Background(), targetWorkflowID, "", "test cleanup") + }) + + pollResp, err = s.Client.WorkflowService().PollWorkflowTaskQueue(ctx, &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: s.Namespace(), + TaskQueue: &taskqueuepb.TaskQueue{Name: callerTaskQueue, Kind: enums.TASK_QUEUE_KIND_NORMAL}, + Identity: "cli-test", + }) + s.NoError(err) + + var sawCompleted bool + for _, e := range pollResp.History.Events { + if e.GetNexusOperationCompletedEventAttributes() != nil { + sawCompleted = true + break + } + if attrs := e.GetNexusOperationFailedEventAttributes(); attrs != nil { + s.FailNow("SignalWithStartWorkflowExecution Nexus operation failed", "failure: %v", attrs.Failure) + } + } + s.True(sawCompleted, "expected a NexusOperationCompleted event in caller history before completing the caller workflow") + + _, err = s.Client.WorkflowService().RespondWorkflowTaskCompleted(ctx, &workflowservice.RespondWorkflowTaskCompletedRequest{ + Identity: "cli-test", + TaskToken: pollResp.TaskToken, + Commands: []*commandpb.Command{{ + CommandType: enums.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION, + Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{ + CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{}, + }, + }}, + }) + s.NoError(err) + return callerWorkflowID +} + +// TestWorkflow_Show_SystemNexusOperationTransformsTypeNames drives a SignalWithStart +// Nexus operation against the __temporal_system endpoint from inside a workflow, then +// verifies that `workflow show` (default table mode) renders the resulting history +// events using the operation-prefixed names (e.g. SignalWithStartWorkflowExecutionScheduled) +// instead of the generic NexusOperation* names. +func (s *SharedServerSuite) TestWorkflow_Show_SystemNexusOperationTransformsTypeNames() { + ctx, cancel := context.WithTimeout(s.Context, 60*time.Second) + defer cancel() + + callerWorkflowID := s.runSystemNexusSWSWorkflow(ctx) + + res := s.Execute( + "workflow", "show", + "--address", s.Address(), + "-w", callerWorkflowID, + ) + s.NoError(res.Err) + out := res.Stdout.String() + s.Contains(out, "SignalWithStartWorkflowExecutionScheduled", "expected transformed Scheduled name in show output") + s.Contains(out, "SignalWithStartWorkflowExecutionCompleted", "expected transformed Completed name in show output") + s.NotContains(out, "NexusOperationScheduled", "raw event type name should be replaced by the unwrapped form") + s.NotContains(out, "NexusOperationCompleted", "raw event type name should be replaced by the unwrapped form") +} + +// TestWorkflow_Show_JSONOutputDoesNotUnwrapSystemNexus pins down that `workflow show -o json` +// emits the raw history.History proto for system Nexus operations — no event-type name +// rewriting and no "unwrappedInput"/"unwrappedResult" injection. JSON output must stay +// compatible with SDK replayers, which need the exact serialized history the server stored. +func (s *SharedServerSuite) TestWorkflow_Show_JSONOutputDoesNotUnwrapSystemNexus() { + ctx, cancel := context.WithTimeout(s.Context, 60*time.Second) + defer cancel() + + callerWorkflowID := s.runSystemNexusSWSWorkflow(ctx) + + res := s.Execute( + "workflow", "show", + "--address", s.Address(), + "-w", callerWorkflowID, + "-o", "json", + ) + s.NoError(res.Err) + raw := res.Stdout.String() + + // Negative assertions: none of the text-mode transformations may bleed into JSON. + s.NotContains(raw, "SignalWithStartWorkflowExecutionScheduled", + "JSON output must not rewrite NEXUS_OPERATION_SCHEDULED to the operation-prefixed name") + s.NotContains(raw, "SignalWithStartWorkflowExecutionCompleted", + "JSON output must not rewrite NEXUS_OPERATION_COMPLETED to the operation-prefixed name") + s.NotContains(raw, "unwrappedInput", + "JSON output must not inject the unwrappedInput field used by text-mode rendering") + s.NotContains(raw, "unwrappedResult", + "JSON output must not inject the unwrappedResult field used by text-mode rendering") + + // Positive assertion: the raw event types still appear, and the JSON parses cleanly as + // a history.History proto with the original Scheduled+Completed events for the system + // Nexus operation. + var hist historypb.History + s.NoError(temporalcli.UnmarshalProtoJSONWithOptions([]byte(raw), &hist, false)) + var schedAttrs *historypb.NexusOperationScheduledEventAttributes + var sawCompleted bool + for _, ev := range hist.Events { + if a := ev.GetNexusOperationScheduledEventAttributes(); a != nil { + schedAttrs = a + } + if ev.GetNexusOperationCompletedEventAttributes() != nil { + sawCompleted = true + } + } + if schedAttrs == nil { + s.FailNow("expected a NexusOperationScheduled event in JSON output") + } + s.True(sawCompleted, "expected a NexusOperationCompleted event in JSON output") + // The raw scheduled attributes must still carry the system endpoint and operation name — + // that's what replayers need to reconstruct the original command. + s.Equal(temporalSystemNexusEndpointName, schedAttrs.Endpoint) + s.Equal(workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.SignalWithStartWorkflowExecution.Name(), schedAttrs.Operation) + s.Equal(workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.ServiceName, schedAttrs.Service) + s.NotNil(schedAttrs.Input, "scheduled Input payload must survive untouched in JSON output") +} + +// TestWorkflow_Show_UserEndpointNexusOperationDoesNotTransformTypeNames verifies the +// negative case: when a Nexus operation runs against a non-system, user-defined endpoint, +// `workflow show` must leave the event type names untransformed because the +// (endpoint, operation) pair is not registered in the global systemNexusOps map. +func (s *SharedServerSuite) TestWorkflow_Show_UserEndpointNexusOperationDoesNotTransformTypeNames() { + handlerWorkflowID := uuid.NewString() + endpointName := validEndpointName(s.T()) + + // Workflow that waits to be canceled. Reused from the existing Nexus describe test. + handlerWorkflow := func(ctx workflow.Context, input nexus.NoValue) (nexus.NoValue, error) { + ctx.Done().Receive(ctx, nil) + return nil, ctx.Err() + } + + op := temporalnexus.NewWorkflowRunOperation("test-op", handlerWorkflow, func(ctx context.Context, _ nexus.NoValue, opts nexus.StartOperationOptions) (client.StartWorkflowOptions, error) { + return client.StartWorkflowOptions{ID: handlerWorkflowID}, nil + }) + service := nexus.NewService("test-service") + s.NoError(service.Register(op)) + + callerWorkflow := func(ctx workflow.Context) error { + nc := workflow.NewNexusClient(endpointName, service.Name) + fut := nc.ExecuteOperation(ctx, op, nil, workflow.NexusOperationOptions{}) + var exec workflow.NexusOperationExecution + if err := fut.GetNexusOperationExecution().Get(ctx, &exec); err != nil { + return err + } + // Block forever; the test only needs the Scheduled event to be present. + ctx.Done().Receive(ctx, nil) + return ctx.Err() + } + + w := s.DevServer.StartDevWorker(s.Suite.T(), DevWorkerOptions{ + Workflows: []any{handlerWorkflow, callerWorkflow}, + NexusServices: []*nexus.Service{service}, + }) + defer w.Stop() + + _, err := s.Client.OperatorService().CreateNexusEndpoint(s.Context, &operatorservice.CreateNexusEndpointRequest{ + Spec: &nexuspb.EndpointSpec{ + Name: endpointName, + Target: &nexuspb.EndpointTarget{ + Variant: &nexuspb.EndpointTarget_Worker_{ + Worker: &nexuspb.EndpointTarget_Worker{ + Namespace: s.Namespace(), + TaskQueue: w.Options.TaskQueue, + }, + }, + }, + }, + }) + s.NoError(err) + + run, err := s.Client.ExecuteWorkflow( + s.Context, + client.StartWorkflowOptions{TaskQueue: w.Options.TaskQueue}, + callerWorkflow, + ) + s.NoError(err) + s.T().Cleanup(func() { + _ = s.Client.TerminateWorkflow(context.Background(), run.GetID(), run.GetRunID(), "test cleanup") + _ = s.Client.TerminateWorkflow(context.Background(), handlerWorkflowID, "", "test cleanup") + }) + + // Wait until the operation has been scheduled — we don't need it to complete. + s.Eventually(func() bool { + resp, derr := s.Client.DescribeWorkflowExecution(s.Context, run.GetID(), run.GetRunID()) + if derr != nil { + return false + } + return len(resp.PendingNexusOperations) > 0 + }, 30*time.Second, 100*time.Millisecond) + + // `workflow show` over a non-system Nexus operation must NOT transform names. + res := s.Execute( + "workflow", "show", + "--address", s.Address(), + "-w", run.GetID(), + ) + s.NoError(res.Err) + out := res.Stdout.String() + s.Contains(out, "NexusOperationScheduled", "user-endpoint event type name should be preserved") + s.NotContains(out, "SignalWithStartWorkflowExecutionScheduled", "user-endpoint event must not pick up a system-op unwrapped name") +} diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 78bdf3513..9127c5f92 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. @@ -979,6 +983,7 @@ commands: - worker deployment set-ramping-version - worker deployment delete-version - worker deployment update-version-metadata + - worker deployment update-version-compute-config - worker deployment manager-identity - name: temporal worker deployment create @@ -1091,6 +1096,8 @@ commands: If a Worker Deployment Version with the supplied BuildID already exists, this command will return an error. + Returns an error if all compute configuration fields are empty. + Note: This is an experimental feature and may change in the future. option-sets: - deployment-version @@ -1319,6 +1326,72 @@ commands: Keys of entries to be deleted from metadata. Can be passed multiple times. + - name: temporal worker deployment update-version-compute-config + summary: Update compute configuration for a Version + description: | + Update compute configuration associated with a Worker Deployment + Version. + + For example, to update the AWS Lambda function ARN associated with an + existing Worker Deployment Version: + + ``` + temporal worker deployment update-version-compute-config \ + --deployment-name YourDeploymentName --build-id YourBuildID \ + --aws-lambda-function-arn UpdatedLambdaFunctionARN + ``` + + To update the AWS IAM role ARN that is assumed by the serverless worker + manager associated with an existing Worker Deployment Version: + + ``` + temporal worker deployment update-version-compute-config \ + --deployment-name YourDeploymentName --build-id YourBuildID \ + --aws-lambda-assume-role-arn UpdatedRoleARN + ``` + + If --remove is specified, the compute configuration for the Worker + Deployment Version will be removed: + + ``` + temporal worker deployment update-version-compute-config \ + --deployment-name YourDeploymentName --build-id YourBuildID \ + --remove + ``` + + If a Worker Deployment Version with the supplied BuildID does not exist, + this command will return an error. + + Note: This is an experimental feature and may change in the future. + option-sets: + - deployment-version + options: + - name: aws-lambda-function-arn + type: string + description: | + Qualified (contains version suffix) or unqualified AWS Lambda + function ARN to invoke when there are no active pollers for task + queue targets in the Worker Deployment. + - name: aws-lambda-assume-role-arn + type: string + description: | + AWS IAM role ARN that the Temporal server will assume when invoking + the Lambda function that spawns a new Worker in this Worker + Deployment Version. Required when --aws-lambda-function-arn is + specified. + - name: aws-lambda-assume-role-external-id + type: string + description: | + Temporal server will enforce that the AWS IAM trust policy associated + with the AWS IAM role specified in --aws-lambda-assume-role-arn has + an aws:ExternalId condition that matches the supplied value. Required + when --aws-lambda-function-arn is specified. + - name: remove + type: bool + description: | + Removes any compute configuration associated with this Worker + Deployment Version. + - name: temporal worker deployment manager-identity summary: Manager Identity commands change the `ManagerIdentity` of a Worker Deployment description: | @@ -1619,6 +1692,210 @@ commands: description: Property value (required). # required: true + - name: temporal nexus + summary: Start, list, and operate on Nexus Operations + description: | + Nexus Operation commands perform operations on Nexus + Operation Executions: + + ``` + temporal nexus [command] [options] + ``` + + For example: + + ``` + temporal nexus operation list + ``` + option-sets: + - client + docs: + description-header: >- + Learn how to use Temporal Nexus commands for starting, listing, + and managing Nexus Operation Executions. + keywords: + - nexus + - nexus operation + - nexus operation start + - nexus operation execute + - nexus operation describe + - nexus operation cancel + - nexus operation terminate + - nexus operation list + - nexus operation count + - cli reference + - cli-feature + - command-line-interface-cli + - temporal cli + tags: + - Nexus + - Temporal CLI + + - name: temporal nexus operation + summary: Commands for managing Nexus Operations + description: | + These commands manage Nexus Operation Executions. + + Nexus Operation commands follow this syntax: + + ``` + temporal nexus operation [command] [options] + ``` + + - name: temporal nexus operation start + summary: Start a new Nexus Operation (Experimental) + description: | + Start a new Nexus Operation. Outputs the + Operation ID and Run ID. + + ``` + temporal nexus operation start \ + --endpoint YourEndpoint \ + --service YourService \ + --operation YourOperation \ + --operation-id YourOperationId \ + --input '{"some-key": "some-value"}' + ``` + option-sets: + - nexus-operation-start + - payload-input + + - name: temporal nexus operation execute + summary: Start a new Nexus Operation and wait for its result (Experimental) + description: | + Start a new Nexus Operation Execution and block until + it completes. The result is output to stdout. + + ``` + temporal nexus operation execute \ + --endpoint YourEndpoint \ + --service YourService \ + --operation YourOperation \ + --operation-id YourOperationId \ + --input '{"some-key": "some-value"}' + ``` + option-sets: + - nexus-operation-start + - payload-input + + - name: temporal nexus operation describe + summary: Show detailed info for a Nexus Operation (Experimental) + description: | + Display detailed information about a specific Nexus + Operation Execution. + + ``` + temporal nexus operation describe \ + --operation-id YourOperationId + ``` + option-sets: + - nexus-operation-reference + options: + - name: raw + type: bool + description: Print properties without changing their format. + + - name: temporal nexus operation cancel + summary: Request cancellation of a Nexus Operation (Experimental) + description: | + Request cancellation of a Nexus Operation. + + ``` + temporal nexus operation cancel \ + --operation-id YourOperationId + ``` + + The Operation handler determines how to handle the + cancellation request. + option-sets: + - nexus-operation-reference + options: + - name: reason + type: string + description: Reason for cancellation. + + - name: temporal nexus operation terminate + summary: Forcefully end a Nexus Operation (Experimental) + description: | + Terminate a Nexus Operation. + + ``` + temporal nexus operation terminate \ + --operation-id YourOperationId \ + --reason YourReason + ``` + + Operation handlers cannot see or respond to terminations. + option-sets: + - nexus-operation-reference + options: + - name: reason + type: string + description: | + Reason for termination. + Defaults to a message with the current user's name. + + - name: temporal nexus operation list + summary: List Nexus Operations matching a query (Experimental) + description: | + List Nexus Operations. Use `--query` to filter results. + + ``` + temporal nexus operation list \ + --query 'Endpoint="YourEndpoint"' + ``` + + Visit https://docs.temporal.io/visibility to read more about + Search Attributes and queries. + options: + - name: query + short: q + type: string + description: | + Query to filter the Nexus Operation Executions to list. + - name: limit + type: int + description: | + Maximum number of Nexus Operation Executions to display. + - name: page-size + type: int + description: | + Maximum number of Nexus Operation Executions to fetch + at a time from the server. + + - name: temporal nexus operation count + summary: Count Nexus Operations matching a query (Experimental) + description: | + Return a count of Nexus Operations. Use `--query` + to filter the operations to be counted. + + ``` + temporal nexus operation count \ + --query 'Endpoint="YourEndpoint"' + ``` + + Visit https://docs.temporal.io/visibility to read more about + Search Attributes and queries. + options: + - name: query + short: q + type: string + description: | + Query to filter Nexus Operation Executions to count. + + - name: temporal nexus operation result + summary: Wait for and output the result of a Nexus Operation (Experimental) + description: | + Wait for a Nexus Operation to complete and output + the result. + + ``` + temporal nexus operation result \ + --operation-id YourOperationId + ``` + option-sets: + - nexus-operation-reference + - name: temporal operator summary: Manage Temporal deployments description: | @@ -2320,6 +2597,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: | @@ -2642,6 +2950,11 @@ commands: - name: ui-codec-endpoint type: string description: UI remote codec HTTP endpoint. + - name: ui-disable-news-fetch + type: bool + description: | + Disable the Web UI newsfeed. When set, the UI will not request the + newsfeed and the button to open the newsfeed panel is hidden. - name: sqlite-pragma type: string[] description: SQLite pragma statements in "PRAGMA=VALUE" format. @@ -2839,14 +3152,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 +3194,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 +3238,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 +3285,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 +3317,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 +3348,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 +3380,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 +3410,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 +3423,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 +3438,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 +3462,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 +3476,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 +3492,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 +3509,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 +3520,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 +3531,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 +3553,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 +3570,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 +3594,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 +3623,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 +3649,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 +3662,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 +3740,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 +3755,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 @@ -3846,6 +4094,17 @@ commands: --versioning-override-build-id YourDeploymentBuildId ``` + or to move the workflow execution to a Worker Deployment Version until + the next Workflow Task completes there, set behavior to `one_time`: + + ``` + temporal workflow update-options \ + --workflow-id YourWorkflowId \ + --versioning-override-behavior one_time \ + --versioning-override-deployment-name YourDeploymentName \ + --versioning-override-build-id YourDeploymentBuildId + ``` + To remove any previous overrides, set the behavior to `unspecified`: @@ -3868,17 +4127,18 @@ commands: enum-values: - unspecified - pinned + - one_time - auto_upgrade - name: versioning-override-deployment-name type: string description: - When overriding to a `pinned` behavior, specifies the Deployment Name of the - version to target. + When overriding to a `pinned` or `one_time` behavior, specifies the + Deployment Name of the version to target. - name: versioning-override-build-id type: string description: - When overriding to a `pinned` behavior, specifies the Build ID of the - version to target. + When overriding to a `pinned` or `one_time` behavior, specifies the + Build ID of the version to target. - name: temporal workflow query summary: Retrieve Workflow Execution state description: | @@ -5025,6 +5285,80 @@ option-sets: May be used as an alternative to `--target-namespace` and `--target-task-queue`. + - name: nexus-operation-reference + options: + - name: operation-id + type: string + description: Nexus Operation ID. + required: true + - name: run-id + type: string + short: r + description: Run ID of the Nexus Operation. + + - name: nexus-operation-start + options: + - name: endpoint + type: string + description: Nexus Endpoint name. + required: true + - name: service + type: string + description: Nexus Service name. + required: true + - name: operation + type: string + description: Nexus Operation name. + required: true + - name: operation-id + type: string + description: Nexus Operation ID. + required: true + - name: schedule-to-close-timeout + type: duration + description: | + Total time the operation is allowed to run. + - name: schedule-to-start-timeout + type: duration + description: | + Maximum time to wait for an operation to be started (or completed + synchronously) by a handler. + - name: start-to-close-timeout + type: duration + description: | + Maximum time to wait for an asynchronous operation to complete + after it has been started. + - name: id-conflict-policy + type: string-enum + description: | + Policy for handling an Operation ID conflict with a + running operation. + enum-values: + - Fail + - UseExisting + - TerminateExisting + - name: id-reuse-policy + type: string-enum + description: | + Policy for re-using an Operation ID from a previously + closed operation. + enum-values: + - AllowDuplicate + - RejectDuplicate + - name: search-attribute + type: string[] + description: | + Search Attribute in `KEY=VALUE` format. + Keys must be identifiers, and values must be JSON values. + For example: 'YourKey={"your": "value"}'. + Can be passed multiple times. + - name: static-summary + type: string + experimental: true + description: | + Static summary for the Nexus Operation for human consumption in UIs. + Uses Temporal Markdown formatting, should be a single line. + - name: query-modifiers options: - name: reject-condition @@ -5051,17 +5385,18 @@ option-sets: required: true enum-values: - pinned + - one_time - auto_upgrade - name: versioning-override-deployment-name type: string description: - When overriding to a `pinned` behavior, specifies the Deployment Name of the - version to target. + When overriding to a `pinned` or `one_time` behavior, specifies the + Deployment Name of the version to target. - name: versioning-override-build-id type: string description: - When overriding to a `pinned` behavior, specifies the Build ID of the - version to target. + When overriding to a `pinned` or `one_time` behavior, specifies the + Build ID of the version to target. - name: activity-reference options: diff --git a/internal/temporalcli/commands_test.go b/internal/temporalcli/commands_test.go index b77732bcb..1b1a18e51 100644 --- a/internal/temporalcli/commands_test.go +++ b/internal/temporalcli/commands_test.go @@ -234,8 +234,14 @@ func (s *SharedServerSuite) SetupSuite() { // Disable DescribeTaskQueue cache. "frontend.activityAPIsEnabled": true, "history.enableChasm": true, - "activity.enableStandalone": true, - "activity.longPollTimeout": 2 * time.Second, + // Required by TestWorkflow_Show_SystemNexusOperationTransformsTypeNames + // to schedule a SignalWithStartWorkflowExecution Nexus operation against + // the __temporal_system endpoint from inside a workflow. + "history.enableSignalWithStartFromWorkflow": true, + "activity.enableStandalone": true, + "activity.longPollTimeout": 2 * time.Second, + "nexusoperation.enableStandalone": true, + "history.enableChasmCallbacks": true, // this is overridden since we don't want caching to be enabled // while testing DescribeTaskQueue behaviour related to versioning "matching.TaskQueueInfoByBuildIdTTL": 0 * time.Second,