Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package forgejo-runner for openSUSE:Factory checked in at 2026-01-05 14:54:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/forgejo-runner (Old) and /work/SRC/openSUSE:Factory/.forgejo-runner.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "forgejo-runner" Mon Jan 5 14:54:02 2026 rev:38 rq:1325318 version:12.4.0 Changes: -------- --- /work/SRC/openSUSE:Factory/forgejo-runner/forgejo-runner.changes 2025-12-25 19:58:01.112151935 +0100 +++ /work/SRC/openSUSE:Factory/.forgejo-runner.new.1928/forgejo-runner.changes 2026-01-05 14:56:53.347581470 +0100 @@ -1,0 +2,11 @@ +Sun Jan 4 20:24:48 UTC 2026 - Richard Rahl <[email protected]> + +- Update to version 12.4.0: + * feat: include all integration tests in make integration-test + * feat: request up to capacity jobs from Forgejo in one API call + * fix: remove cmd and ports from container schema + * fix: evaluate jobs.<job_id>.container.volumes + * fix: re-parsing incomplete jobs must use on.workflow_call.inputs, not + global inputs + +------------------------------------------------------------------- Old: ---- forgejo-runner-12.3.1.obscpio New: ---- forgejo-runner-12.4.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ forgejo-runner.spec ++++++ --- /var/tmp/diff_new_pack.9rCpS4/_old 2026-01-05 14:56:56.299704132 +0100 +++ /var/tmp/diff_new_pack.9rCpS4/_new 2026-01-05 14:56:56.303704299 +0100 @@ -1,7 +1,7 @@ # # spec file for package forgejo-runner # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %define services %{name}.service Name: forgejo-runner -Version: 12.3.1 +Version: 12.4.0 Release: 0 Summary: Daemon that connects to a Forgejo instance and runs CI jobs License: GPL-3.0-or-later @@ -28,7 +28,7 @@ Source2: %{name}.service BuildRequires: fish BuildRequires: zsh -BuildRequires: golang(API) >= 1.23 +BuildRequires: golang(API) >= 1.25 BuildRequires: pkgconfig(bash-completion) BuildRequires: pkgconfig(systemd) Requires: git-core ++++++ _service ++++++ --- /var/tmp/diff_new_pack.9rCpS4/_old 2026-01-05 14:56:56.339705795 +0100 +++ /var/tmp/diff_new_pack.9rCpS4/_new 2026-01-05 14:56:56.343705961 +0100 @@ -2,7 +2,7 @@ <service name="obs_scm" mode="manual"> <param name="url">https://code.forgejo.org/forgejo/runner</param> <param name="scm">git</param> - <param name="revision">refs/tags/v12.3.1</param> + <param name="revision">refs/tags/v12.4.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">disable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ forgejo-runner-12.3.1.obscpio -> forgejo-runner-12.4.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/.forgejo/workflows/test.yml new/forgejo-runner-12.4.0/.forgejo/workflows/test.yml --- old/forgejo-runner-12.3.1/.forgejo/workflows/test.yml 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/.forgejo/workflows/test.yml 2025-12-31 15:18:43.000000000 +0100 @@ -153,12 +153,11 @@ # gcc -- for `go test -race` apt-get install -qq -y \ qemu-user-static binfmt-support \ - gcc + gcc make - name: integration test run: | - go test -race ./act/container - go test -race -timeout 30m ./act/runner/... + make act-integration-test env: TEST_MUST_SUPPORT_MIXED_ARCH: true @@ -205,7 +204,7 @@ - run: apt-get install -y -qq gcc # required for `-race` - - run: make integration-test + - run: make runner-integration-test validate-mocks: name: validate mocks diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/Makefile new/forgejo-runner-12.4.0/Makefile --- old/forgejo-runner-12.3.1/Makefile 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/Makefile 2025-12-31 15:18:43.000000000 +0100 @@ -110,9 +110,18 @@ $(GO) test -race -short ./act/container $(GO) test -race ./act/artifactcache/... ./act/workflowpattern/... ./act/filecollector/... ./act/common/... ./act/jobparser ./act/model ./act/exprparser ./act/schema -integration-test: +.PHONY: integration-test +integration-test: runner-integration-test act-integration-test + +.PHONY: runner-integration-test +runner-integration-test: @$(GO) test -race -v ./internal/app/run/... +.PHONY: act-integration-test +act-integration-test: + @$(GO) test -race ./act/container + @$(GO) test -race -v -timeout 30m ./act/runner/... + .PHONY: vet vet: @echo "Running go vet..." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/container/docker_run.go new/forgejo-runner-12.4.0/act/container/docker_run.go --- old/forgejo-runner-12.3.1/act/container/docker_run.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/container/docker_run.go 2025-12-31 15:18:43.000000000 +0100 @@ -13,6 +13,7 @@ "path/filepath" "regexp" "runtime" + "slices" "strconv" "strings" "time" @@ -579,6 +580,18 @@ config.Hostname = jobConfig.Config.Hostname } + if len(jobConfig.Config.User) > 0 { + logger.Debugf("--user %v", jobConfig.Config.User) + config.User = jobConfig.Config.User + } + + for _, group := range jobConfig.HostConfig.GroupAdd { + if !slices.Contains(hostConfig.GroupAdd, group) { + logger.Debugf("--group-add %v", group) + hostConfig.GroupAdd = append(hostConfig.GroupAdd, group) + } + } + return config, hostConfig, nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/container/docker_run_test.go new/forgejo-runner-12.4.0/act/container/docker_run_test.go --- old/forgejo-runner-12.3.1/act/container/docker_run_test.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/container/docker_run_test.go 2025-12-31 15:18:43.000000000 +0100 @@ -375,6 +375,16 @@ config: &container.Config{}, hostConfig: &container.HostConfig{}, }, + { + name: "MergeUserAndGroupAdd", + options: "--user asdf --user root --group-add group1 --group-add wheel --group-add system --group-add wheel --group-add group1", + config: &container.Config{ + User: "root", + }, + hostConfig: &container.HostConfig{ + GroupAdd: []string{"group1", "wheel", "system"}, + }, + }, } { t.Run(testCase.name, func(t *testing.T) { cr := &containerReference{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/jobparser/jobparser.go new/forgejo-runner-12.4.0/act/jobparser/jobparser.go --- old/forgejo-runner-12.3.1/act/jobparser/jobparser.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/jobparser/jobparser.go 2025-12-31 15:18:43.000000000 +0100 @@ -63,6 +63,17 @@ pc.parentUniqueID = workflow.Metadata.WorkflowCallID } + // `pc.inputs` are the inputs for the job that should be used whenever `${{ inputs... }}` appears in the job + // definition. However, if `content` represents a reusable workflow job, then any reference to `${{ inputs... }}` + // should actually come from `on.workflow_call.inputs`. Normally this is handled in `expandReusableWorkflow` by + // replacing the inputs in the `ParseOption` array, but, we could also be re-parsing a reusable workflow that was + // incomplete in which case `pc.inputs` will be the global inputs and incorrect for evaluation. That case needs to + // be detected and overridden: + if workflow.Metadata.WorkflowCallParent != "" { + // This workflow has parent metadata, so that means it is a child workflow job; replace `pc.inputs`. + pc.inputs = getWorkflowCallInputDefaults(origin) + } + results := map[string]*JobResult{} for id, job := range origin.Jobs { results[id] = &JobResult{ @@ -903,3 +914,25 @@ return hex.EncodeToString(h.Sum(nil)) } + +func getWorkflowCallInputDefaults(job *model.Workflow) map[string]any { + workflowCallConfig := job.WorkflowCallConfig() + if workflowCallConfig == nil { + return nil + } + + overrideInputs := map[string]any{} + for k, input := range workflowCallConfig.Inputs { + overrideInputs[k] = input.Default + var value any + if value == nil { + _ = input.Default.Decode(&value) + } + if input.Type == "boolean" { + overrideInputs[k] = value == "true" || value == true + } else { + overrideInputs[k] = value + } + } + return overrideInputs +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/jobparser/jobparser_test.go new/forgejo-runner-12.4.0/act/jobparser/jobparser_test.go --- old/forgejo-runner-12.3.1/act/jobparser/jobparser_test.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/jobparser/jobparser_test.go 2025-12-31 15:18:43.000000000 +0100 @@ -477,6 +477,37 @@ }), }, }, + // `expand_reusable_incomplete6` tests expanding an incomplete reusable workflow within a parent reusable + // workflow, and being able to reference the inputs that were defined from the parent workflow and stored in + // `on.workflow_call.inputs` as default values. In particular, the `inputs` provided to the jobparser shouldn't + // be used since those will always be Forgejo's perspective of the outer-most workflow's inputs. + { + name: "expand_reusable_incomplete6", + reparsingSingleWorkflow: true, + expectingInvalidWorkflowOutput: true, + options: []ParseOption{ + WithWorkflowNeeds([]string{"define-runs-on"}), + WithJobOutputs(map[string]map[string]string{ + "define-runs-on": { + "runners": "nixos-25.11", + }, + }), + WithInputs(map[string]any{ + // These inputs should all be overridden but are provided to ensure they don't appear in the output + "example-boolean-required": false, + "example-number-required": 456, + "example-string-required": "no thanks", + }), + SupportIncompleteRunsOn(), + ExpandLocalReusableWorkflows(func(job *Job, path string) ([]byte, error) { + if path == "./.forgejo/workflows/expand_reusable_incomplete6_reusable.yml" { + content := ReadTestdata(t, "expand_reusable_incomplete6_reusable.yaml", true) + return content, nil + } + return nil, fmt.Errorf("unexpected local path: %q", path) + }), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6.in.yaml new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6.in.yaml --- old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6.in.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6.in.yaml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,30 @@ +"on": + workflow_call: + inputs: + example-boolean-required: + default: true + type: boolean + example-number-required: + default: 123 + type: number + example-string-required: + default: reusable-1 + type: string +name: test +jobs: + reusable: + name: reusable (incomplete matrix) + needs: define-runs-on + runs-on: [] + uses: ./.forgejo/workflows/expand_reusable_incomplete6_reusable.yml + with: + input1: ${{ inputs.example-boolean-required }} + input2: ${{ inputs.example-number-required }} + input3: ${{ inputs.example-string-required }} + input4: ${{ needs.define-runs-on.outputs.runners }} +incomplete_with: true +incomplete_with_needs: + job: define-runs-on + output: runners +__metadata: + workflow_call_parent: 93e6933d7bffb610a7aeecf39163ff8c6ec563c2f4b84ea80d9144de72de713b diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6.out.yaml new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6.out.yaml --- old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6.out.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6.out.yaml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,56 @@ +name: test +"on": + workflow_call: + inputs: + example-boolean-required: + default: true + type: boolean + example-number-required: + default: 123 + type: number + example-string-required: + default: reusable-1 + type: string +jobs: + reusable: + name: reusable + needs: + - define-runs-on + - reusable.job1 + runs-on: [] + if: false +__metadata: + workflow_call_inputs: + input1: true + input2: 123 + input3: reusable-1 + input4: nixos-25.11 + workflow_call_id: 93e6933d7beeb610a7aeecf39163ff8c6ec563c2f4b84ea80d9144de72de713b + workflow_call_parent: 93e6933d7bffb610a7aeecf39163ff8c6ec563c2f4b84ea80d9144de72de713b +--- +name: test +"on": + workflow_call: + inputs: + input1: + default: true + type: boolean + input2: + default: 123 + type: number + input3: + default: reusable-1 + type: string + input4: + default: nixos-25.11 + type: string +jobs: + reusable.job1: + name: job1 + needs: + - define-runs-on + runs-on: nixos-25.11 + steps: + - run: echo "Woohoo!" +__metadata: + workflow_call_parent: 93e6933d7beeb610a7aeecf39163ff8c6ec563c2f4b84ea80d9144de72de713b diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6_reusable.yaml new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6_reusable.yaml --- old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6_reusable.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6_reusable.yaml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,17 @@ +on: + workflow_call: + inputs: + input1: + type: boolean + input2: + type: number + input3: + type: string + input4: + type: string + +jobs: + job1: + runs-on: ${{ inputs.input4 }} + steps: + - run: echo "Woohoo!" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/run_context.go new/forgejo-runner-12.4.0/act/runner/run_context.go --- old/forgejo-runner-12.3.1/act/runner/run_context.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/run_context.go 2025-12-31 15:18:43.000000000 +0100 @@ -154,7 +154,12 @@ if job := rc.Run.Job(); job != nil { if container := job.Container(); container != nil { - for _, v := range container.Volumes { + interpolatedVolumes := make([]string, 0, len(container.Volumes)) + for _, volume := range container.Volumes { + interpolatedVolumes = append(interpolatedVolumes, rc.ExprEval.Interpolate(ctx, volume)) + } + + for _, v := range interpolatedVolumes { if !strings.Contains(v, ":") || filepath.IsAbs(v) { // Bind anonymous volume or host file. binds = append(binds, v) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/run_context_test.go new/forgejo-runner-12.4.0/act/runner/run_context_test.go --- old/forgejo-runner-12.3.1/act/runner/run_context_test.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/run_context_test.go 2025-12-31 15:18:43.000000000 +0100 @@ -268,14 +268,19 @@ rc := &RunContext{ Name: "TestRCName", Run: &model.Run{ + JobID: "test", Workflow: &model.Workflow{ Name: "TestWorkflowName", + Jobs: map[string]*model.Job{ + "test": {}, + }, }, }, Config: &Config{ BindWorkdir: false, }, } + rc.ExprEval = rc.NewExpressionEvaluator(t.Context()) rc.Run.JobID = "job1" rc.Run.Workflow.Jobs = map[string]*model.Job{"job1": job} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/runner_test.go new/forgejo-runner-12.4.0/act/runner/runner_test.go --- old/forgejo-runner-12.3.1/act/runner/runner_test.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/runner_test.go 2025-12-31 15:18:43.000000000 +0100 @@ -6,6 +6,7 @@ "fmt" "io" "os" + "path" "path/filepath" "runtime" "strings" @@ -199,6 +200,7 @@ Matrix: cfg.Matrix, JobLoggerLevel: cfg.JobLoggerLevel, ContainerDaemonSocket: os.Getenv("DOCKER_HOST"), + ValidVolumes: cfg.ValidVolumes, } if cfg.GitHubInstance != "" { runnerConfig.GitHubInstance = cfg.GitHubInstance @@ -289,11 +291,6 @@ {workdir, "fail", "push", "Job 'build' failed", platforms, secrets}, {workdir, "runs-on", "push", "", platforms, secrets}, {workdir, "checkout", "push", "", platforms, secrets}, - {workdir, "job-container", "push", "", platforms, secrets}, - {workdir, "job-container-non-root", "push", "", platforms, secrets}, - {workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms, secrets}, - {workdir, "job-container-env-reference", "push", "", platforms, map[string]string{"ALPINE_TAG": "3.22"}}, - {workdir, "container-hostname", "push", "", platforms, secrets}, {workdir, "remote-action-docker", "push", "", platforms, secrets}, {workdir, "remote-action-js", "push", "", platforms, secrets}, // {workdir, "remote-action-js-node-user", "push", "", platforms, secrets}, // Test if this works with non root container @@ -342,12 +339,24 @@ {workdir, "stepsummary", "push", "", platforms, secrets}, {workdir, "tool-cache", "push", "", platforms, secrets}, + // job container + {workdir, "job-container", "push", "", platforms, secrets}, + {workdir, "job-container-env", "push", "", platforms, secrets}, + {workdir, "job-container-image", "push", "", platforms, map[string]string{"ALPINE_TAG": "3.22"}}, + {workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms, secrets}, + {workdir, "job-container-non-root", "push", "", platforms, secrets}, + {workdir, "job-container-options", "push", "", platforms, secrets}, + {workdir, "job-container-options-group-add", "push", "", platforms, secrets}, + {workdir, "job-container-options-user", "push", "", platforms, secrets}, + // services {workdir, "services", "push", "", platforms, secrets}, {workdir, "services-with-container", "push", "", platforms, secrets}, {workdir, "mysql-service-container-with-health-check", "push", "", platforms, secrets}, {workdir, "mysql-service-container-premature-terminate", "push", "service [maindb]", platforms, secrets}, {workdir, "services-empty-image", "push", "", platforms, secrets}, + {workdir, "services-options-group-add", "push", "", platforms, secrets}, + {workdir, "services-options-user", "push", "", platforms, secrets}, {workdir, "services-context-expression", "push", "", platforms, secrets}, } @@ -813,3 +822,26 @@ }) } } + +func TestRunner_JobContainerVolumes(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + skip.If(t, runtime.GOOS != "linux") // Windows and macOS cannot run linux docker container natively + + tempDir := t.TempDir() + volumePath := path.Join(tempDir, "file.txt") + err := os.WriteFile(volumePath, []byte("hello"), 0o644) + require.NoError(t, err) + + jobFile := TestJobFileInfo{ + workdir: workdir, + workflowPath: "job-container-volumes", + eventName: "push", + errorMessage: "", + platforms: platforms, + } + + config := &Config{Env: map[string]string{"volume_path": volumePath}, ValidVolumes: []string{volumePath}} + jobFile.runTest(t.Context(), t, config) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/container-hostname/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/container-hostname/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/container-hostname/push.yml 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/container-hostname/push.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,26 +0,0 @@ -name: container-hostname -on: push - -defaults: - run: - shell: bash - -jobs: - with-hostname: - runs-on: ubuntu-latest - container: - image: code.forgejo.org/oci/node:22-bookworm - options: "--hostname my.host.local" - steps: - - run: | - echo "HOST: $(uname -n)" - [[ "$(uname -n)" == "my.host.local" ]] - - default-hostname: - runs-on: ubuntu-latest - container: - image: code.forgejo.org/oci/node:22-bookworm - steps: - - run: | - echo "HOST: $(uname -n)" - [[ "$(uname -n)" != "my.host.local" ]] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/job-container-env/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/job-container-env/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-env/push.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-env/push.yml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,15 @@ +on: + push: +env: + name: "Alice" +jobs: + test: + runs-on: ubuntu-latest + container: + image: code.forgejo.org/oci/node:22-bookworm + env: + greeting: "Hello ${{ env.name }}" + steps: + - run: | + echo "env.greeting=${{ env.greeting }}" + [[ "${{ env.greeting }}" = "Hello Alice" ]] || exit 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/job-container-env-reference/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/job-container-env-reference/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-env-reference/push.yml 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-env-reference/push.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,21 +0,0 @@ -name: job-container -on: push - -env: - ATdirect: 3.22 - ATvar: ${{ secrets.ALPINE_TAG }} - -jobs: - test: - runs-on: ubuntu-latest - container: - image: code.forgejo.org/oci/alpine:${{ env.ATdirect }} - steps: - - run: cat /etc/os-release - - run: echo ${{ env.ATdirect }} - - run: echo ${{ env.ATvar }} - test2: - runs-on: ubuntu-latest - container: code.forgejo.org/oci/alpine:${{ env.ATvar }} - steps: - - run: cat /etc/os-release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/job-container-image/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/job-container-image/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-image/push.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-image/push.yml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,21 @@ +name: job-container +on: push + +env: + ATdirect: 3.22 + ATvar: ${{ secrets.ALPINE_TAG }} + +jobs: + test: + runs-on: ubuntu-latest + container: + image: code.forgejo.org/oci/alpine:${{ env.ATdirect }} + steps: + - run: cat /etc/os-release + - run: echo ${{ env.ATdirect }} + - run: echo ${{ env.ATvar }} + test2: + runs-on: ubuntu-latest + container: code.forgejo.org/oci/alpine:${{ env.ATvar }} + steps: + - run: cat /etc/os-release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options/push.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options/push.yml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,28 @@ +name: container-hostname +on: push + +defaults: + run: + shell: bash +env: + hostname: "my.host.local" + +jobs: + with-hostname: + runs-on: ubuntu-latest + container: + image: code.forgejo.org/oci/node:22-bookworm + options: "--hostname ${{ env.hostname }}" + steps: + - run: | + echo "HOST: $(uname -n)" + [[ "$(uname -n)" == "my.host.local" ]] + + default-hostname: + runs-on: ubuntu-latest + container: + image: code.forgejo.org/oci/node:22-bookworm + steps: + - run: | + echo "HOST: $(uname -n)" + [[ "$(uname -n)" != "my.host.local" ]] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options-group-add/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options-group-add/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options-group-add/push.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options-group-add/push.yml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,17 @@ +name: job-container-options-group-add +on: push + +jobs: + # test job groups + job-with-default-groups: + runs-on: ubuntu-latest + steps: + - name: Check default groups + run: groups | grep "^root$" || exit 1 + job-with-additional-groups: + runs-on: ubuntu-latest + container: + options: --group-add users --group-add daemon + steps: + - name: Check added groups + run: groups | grep "^root daemon users$" || exit 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options-user/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options-user/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options-user/push.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options-user/push.yml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,17 @@ +name: job-container-options-user +on: push + +jobs: + # test job user + job-as-default-user: + runs-on: ubuntu-latest + steps: + - name: Check default user is root + run: whoami | grep "^root$" || exit 1 + job-as-nobody: + runs-on: ubuntu-latest + container: + options: --user nobody + steps: + - name: Check user is now nobody + run: whoami | grep "^nobody$" || exit 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/job-container-volumes/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/job-container-volumes/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-volumes/push.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-volumes/push.yml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,13 @@ +on: + push: +jobs: + test: + runs-on: ubuntu-latest + container: + image: code.forgejo.org/oci/node:22-bookworm + volumes: + - "${{ env.volume_path }}:/mnt/file.txt" + steps: + - run: | + [[ -f "/mnt/file.txt" ]] || exit 1 + [[ "$(cat /mnt/file.txt)" = "hello" ]] || exit 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/services-options-group-add/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/services-options-group-add/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/services-options-group-add/push.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/services-options-group-add/push.yml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,26 @@ +name: services-options-group-add +on: push + +jobs: + # test service groups + service-with-default-groups: + runs-on: ubuntu-latest + services: + nginx: + image: 'nginx:latest' + # this runs the webserver iff the condition is ok. We then look for the webserver in the job steps + cmd: ["sh", "-c", "(groups | grep \"^root$\") && nginx -g 'daemon off;'"] + steps: + - name: Test whether webserver is reachable + run: curl -sfI http://nginx:80 + service-with-additional-groups: + runs-on: ubuntu-latest + services: + nginx: + options: --group-add users --group-add daemon + image: 'nginx:latest' + # this runs the webserver iff the condition is ok. We then look for the webserver in the job steps + cmd: ["sh", "-c", "(groups | grep \"^root daemon users$\") && nginx -g 'daemon off;'"] + steps: + - name: Test whether webserver is reachable + run: curl -sfI http://nginx:80 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/testdata/services-options-user/push.yml new/forgejo-runner-12.4.0/act/runner/testdata/services-options-user/push.yml --- old/forgejo-runner-12.3.1/act/runner/testdata/services-options-user/push.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/runner/testdata/services-options-user/push.yml 2025-12-31 15:18:43.000000000 +0100 @@ -0,0 +1,25 @@ +name: services-options-user +on: push + +jobs: + # test service user + service-as-default-user: + runs-on: ubuntu-latest + services: + nginx: + image: 'nginx:latest' + # this runs the webserver iff the condition is ok. We then look for the webserver in the job steps + cmd: ["sh", "-c", "(whoami | grep \"^root$\") && nginx -g 'daemon off;'"] + steps: + - name: Test whether webserver is reachable + run: curl -sfI http://nginx:80 + service-as-nobody: + runs-on: ubuntu-latest + services: + nginx: + options: --user nobody + image: 'nginx:latest' + # Here we try to run the webserver but expect a failure since nobody cannot bind to port 80. + steps: + - name: Test whether webserver is reachable + run: bash -c '! curl -sfI http://nginx:80' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/schema/schema_test.go new/forgejo-runner-12.4.0/act/schema/schema_test.go --- old/forgejo-runner-12.3.1/act/schema/schema_test.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/schema/schema_test.go 2025-12-31 15:18:43.000000000 +0100 @@ -359,6 +359,70 @@ assert.NoError(t, err) } +func TestSchemaContainer(t *testing.T) { + var node yaml.Node + err := yaml.Unmarshal([]byte(` +on: + push: +jobs: + container-schema: + runs-on: ubuntu-latest + container: + image: alpine:3.20 + credentials: + username: 'root' + password: 'admin1234' + env: + SOME_VARIABLE: contents + options: "--hostname alpine" + volumes: + - /srv/example:/srv/example + steps: + - run: echo "OK" +`), &node) + require.NoError(t, err) + n := &Node{ + Definition: "workflow-root", + Schema: GetWorkflowSchema(), + } + require.NoError(t, n.UnmarshalYAML(&node)) +} + +func TestSchemaServices(t *testing.T) { + var node yaml.Node + err := yaml.Unmarshal([]byte(` +on: + push: +jobs: + service-schema: + runs-on: ubuntu-latest + services: + pgsql: + image: code.forgejo.org/oci/postgres:15 + credentials: + username: 'root' + password: 'admin1234' + env: + POSTGRES_DB: test + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: "--hostname pgsql" + cmd: + - /some/command + volumes: + - /srv/example:/srv/example + steps: + - run: echo "OK" +`), &node) + require.NoError(t, err) + n := &Node{ + Definition: "workflow-root", + Schema: GetWorkflowSchema(), + } + require.NoError(t, n.UnmarshalYAML(&node)) +} + func TestSchemaServicesContextExpressions(t *testing.T) { var node yaml.Node err := yaml.Unmarshal([]byte(` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/act/schema/workflow_schema.json new/forgejo-runner-12.4.0/act/schema/workflow_schema.json --- old/forgejo-runner-12.3.1/act/schema/workflow_schema.json 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/act/schema/workflow_schema.json 2025-12-31 15:18:43.000000000 +0100 @@ -2024,15 +2024,7 @@ "type": "non-empty-string", "description": "Use `jobs.<job_id>.container.options` to configure additional Docker container resource options." }, - "cmd": { - "type": "sequence-of-non-empty-string", - "description": "Use `jobs.<job_id>.container.cmd` to specify the command to run instead of the default." - }, "env": "container-env", - "ports": { - "type": "sequence-of-non-empty-string", - "description": "Use `jobs.<job_id>.container.ports` to set an array of ports to expose on the container." - }, "volumes": { "type": "sequence-of-non-empty-string", "description": "Use `jobs.<job_id>.container.volumes` to set an array of volumes for the container to use. You can use volumes to share data between services or other steps in a job. You can specify named Docker volumes, anonymous Docker volumes, or bind mounts on the host." @@ -2041,35 +2033,80 @@ } } }, + "container-registry-credentials": { + "description": "If the image's container registry requires authentication to pull the image, you can use `jobs.<job_id>.container.credentials` to set a map of the username and password. The credentials are the same values that you would provide to the `docker login` command.", + "context": ["forge", "forgejo", "github", "inputs", "vars", "secrets", "env"], + "mapping": { + "properties": { + "username": "non-empty-string", + "password": "non-empty-string" + } + } + }, + "container-env": { + "description": "Use `jobs.<job_id>.container.env` to set a map of variables in the container.", + "mapping": { + "loose-key-type": "non-empty-string", + "loose-value-type": "string-runner-context" + } + }, "services": { - "description": "Additional containers to host services for a job in a workflow. These are useful for creating databases or cache services like redis. The runner on the virtual machine will automatically create a network and manage the life cycle of the service containers. When you use a service container for a job or your step uses container actions, you don't need to set port information to access the service. Docker automatically exposes all ports between containers on the same network. When both the job and the action run in a container, you can directly reference the container by its hostname. The hostname is automatically mapped to the service name. When a step does not use a container action, you must access the service using localhost and bind the ports.", + "description": "Additional containers to host services for a job in a workflow. These are useful for creating databases or cache services like Redis. The runner on the virtual machine will automatically create a network and manage the life cycle of the service containers. When you use a service container for a job or your step uses container actions, you don't need to set port information to access the service. Docker automatically exposes all ports between containers on the same network. When both the job and the action run in a container, you can directly reference the container by its hostname. The hostname is automatically mapped to the service name. When a step does not use a container action, you must access the service using localhost and bind the ports.", "context": ["forge", "forgejo", "github", "inputs", "vars", "needs", "strategy", "matrix", "env"], "mapping": { "loose-key-type": "non-empty-string", - "loose-value-type": "services-container" + "loose-value-type": "service" } }, - "services-container": { + "service": { "context": ["forge", "forgejo", "github", "inputs", "vars", "needs", "strategy", "matrix", "env"], - "one-of": ["non-empty-string", "container-mapping"] + "one-of": ["non-empty-string", "service-mapping"] }, - "container-registry-credentials": { - "description": "If the image's container registry requires authentication to pull the image, you can use `jobs.<job_id>.container.credentials` to set a map of the username and password. The credentials are the same values that you would provide to the `docker login` command.", - "context": ["forge", "forgejo", "github", "inputs", "vars", "secrets", "env"], + "service-mapping": { "mapping": { "properties": { - "username": "non-empty-string", - "password": "non-empty-string" + "image": { + "type": "non-empty-string", + "description": "Name of the container image that contains the service to run. The value can be a short name like `fedora` or a fully qualified image name like `registry.fedoraproject.org/fedora`." + }, + "options": { + "type": "non-empty-string", + "description": "Additional options to pass to the container runtime." + }, + "cmd": { + "type": "sequence-of-non-empty-string", + "description": "List of commands to run instead of the default command." + }, + "env": "service-env", + "ports": { + "type": "sequence-of-non-empty-string", + "description": "List of service container ports to expose in the format `HOST_PORT:CONTAINER_PORT`" + }, + "volumes": { + "type": "sequence-of-non-empty-string", + "description": "List of volumes to mount into the service container. You can specify named volumes, anonymous volumes, or bind mounts." + }, + "credentials": "service-container-registry-credentials" } } }, - "container-env": { - "description": "Use `jobs.<job_id>.container.env` to set a map of variables in the container.", + "service-env": { + "description": "A map of environment variables that should be injected into the service container during startup.", "mapping": { "loose-key-type": "non-empty-string", "loose-value-type": "string-runner-context" } }, + "service-container-registry-credentials": { + "description": "If the image's container registry requires authentication to pull the image, you can specify the username and password to use when pulling the image.", + "context": ["forge", "forgejo", "github", "inputs", "vars", "secrets", "env"], + "mapping": { + "properties": { + "username": "non-empty-string", + "password": "non-empty-string" + } + } + }, "non-empty-string": { "string": { "require-non-empty": true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/examples/docker-compose/compose-forgejo-and-runner.yml new/forgejo-runner-12.4.0/examples/docker-compose/compose-forgejo-and-runner.yml --- old/forgejo-runner-12.3.1/examples/docker-compose/compose-forgejo-and-runner.yml 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/examples/docker-compose/compose-forgejo-and-runner.yml 2025-12-31 15:18:43.000000000 +0100 @@ -51,7 +51,7 @@ - 8080:3000 runner-register: - image: code.forgejo.org/forgejo/runner:12.0.1 + image: code.forgejo.org/forgejo/runner:12.3.1 links: - docker-in-docker - forgejo @@ -77,7 +77,7 @@ ' runner-daemon: - image: code.forgejo.org/forgejo/runner:12.0.1 + image: code.forgejo.org/forgejo/runner:12.3.1 links: - docker-in-docker - forgejo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/examples/lxc-systemd/forgejo-runner-service.sh new/forgejo-runner-12.4.0/examples/lxc-systemd/forgejo-runner-service.sh --- old/forgejo-runner-12.3.1/examples/lxc-systemd/forgejo-runner-service.sh 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/examples/lxc-systemd/forgejo-runner-service.sh 2025-12-31 15:18:43.000000000 +0100 @@ -22,7 +22,7 @@ : ${INPUTS_LIFETIME:=7d} DEFAULT_LXC_HELPERS_VERSION=1.1.3 # renovate: datasource=forgejo-tags depName=forgejo/lxc-helpers : ${INPUTS_LXC_HELPERS_VERSION:=$DEFAULT_LXC_HELPERS_VERSION} -DEFAULT_RUNNER_VERSION=12.0.1 # renovate: datasource=forgejo-releases depName=forgejo/runner +DEFAULT_RUNNER_VERSION=12.3.1 # renovate: datasource=forgejo-releases depName=forgejo/runner : ${INPUTS_RUNNER_VERSION:=$DEFAULT_RUNNER_VERSION} : ${KILL_AFTER:=21600} # 6h == 21600 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/go.mod new/forgejo-runner-12.4.0/go.mod --- old/forgejo-runner-12.3.1/go.mod 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/go.mod 2025-12-31 15:18:43.000000000 +0100 @@ -1,11 +1,11 @@ module code.forgejo.org/forgejo/runner/v12 -go 1.24.0 +go 1.25.0 -toolchain go1.24.10 +toolchain go1.25.5 require ( - code.forgejo.org/forgejo/actions-proto v0.5.3 + code.forgejo.org/forgejo/actions-proto v0.6.0 connectrpc.com/connect v1.19.1 dario.cat/mergo v1.0.2 github.com/Masterminds/semver v1.5.0 @@ -37,9 +37,10 @@ github.com/timshannon/bolthold v0.0.0-20240314194003-30aac6950928 go.etcd.io/bbolt v1.4.3 go.yaml.in/yaml/v3 v3.0.4 + golang.org/x/sys v0.38.0 golang.org/x/term v0.37.0 golang.org/x/time v0.14.0 - google.golang.org/protobuf v1.36.10 + google.golang.org/protobuf v1.36.11 gotest.tools/v3 v3.5.2 ) @@ -103,7 +104,6 @@ golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.38.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/go.sum new/forgejo-runner-12.4.0/go.sum --- old/forgejo-runner-12.3.1/go.sum 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/go.sum 2025-12-31 15:18:43.000000000 +0100 @@ -1,5 +1,5 @@ -code.forgejo.org/forgejo/actions-proto v0.5.3 h1:dDProRNB4CDvEl9gfo8jkiVfGdiW7fXAt5TM9Irka28= -code.forgejo.org/forgejo/actions-proto v0.5.3/go.mod h1:33iTdur/jVa/wAQP+BuciRTK9WZcVaxy0BNEnSWWFDM= +code.forgejo.org/forgejo/actions-proto v0.6.0 h1:dw1Dogk9A4V/yrLVkhe9dSZPsqNAIkI1kCXPSqG3tZA= +code.forgejo.org/forgejo/actions-proto v0.6.0/go.mod h1:+444hHBs9/qDh5X/AedaTv0Egj3vd/EXP93vg9zFV2E= connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8= @@ -264,8 +264,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -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/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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/internal/app/poll/poller.go new/forgejo-runner-12.4.0/internal/app/poll/poller.go --- old/forgejo-runner-12.3.1/internal/app/poll/poller.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/internal/app/poll/poller.go 2025-12-31 15:18:43.000000000 +0100 @@ -70,12 +70,39 @@ func (p *poller) Poll() { limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1) + + capacity := int64(p.cfg.Runner.Capacity) + inProgressTasks := atomic.Int64{} wg := &sync.WaitGroup{} - for i := 0; i < p.cfg.Runner.Capacity; i++ { - wg.Add(1) - go p.poll(i, wg, limiter) + + log.Infof("[poller] launched") + for { + if err := limiter.Wait(p.pollingCtx); err != nil { + log.Infof("[poller] shutdown begin, %d tasks currently running", inProgressTasks.Load()) + break + } + + availableCapacity := capacity - inProgressTasks.Load() + if availableCapacity > 0 { + log.Tracef("[poller] fetching at most %d tasks", availableCapacity) + tasks, ok := p.fetchTasks(p.pollingCtx, availableCapacity) + if !ok { + continue + } + + log.Tracef("[poller] successfully fetched %d tasks", len(tasks)) + for _, task := range tasks { + inProgressTasks.Add(1) + wg.Go(func() { + p.runTaskWithRecover(p.jobsCtx, task) + inProgressTasks.Add(-1) + }) + } + } } + wg.Wait() + log.Trace("[poller] shutdown complete, all tasks complete") // signal the poller is finished close(p.done) @@ -98,22 +125,6 @@ } } -func (p *poller) poll(id int, wg *sync.WaitGroup, limiter *rate.Limiter) { - log.Infof("[poller %d] launched", id) - defer wg.Done() - for { - if err := limiter.Wait(p.pollingCtx); err != nil { - log.Infof("[poller %d] shutdown", id) - return - } - task, ok := p.fetchTask(p.pollingCtx) - if !ok { - continue - } - p.runTaskWithRecover(p.jobsCtx, task) - } -} - func (p *poller) runTaskWithRecover(ctx context.Context, task *runnerv1.Task) { defer func() { if r := recover(); r != nil { @@ -127,7 +138,11 @@ } } -func (p *poller) fetchTask(ctx context.Context) (*runnerv1.Task, bool) { +func (p *poller) fetchTasks(ctx context.Context, availableCapacity int64) ([]*runnerv1.Task, bool) { + if availableCapacity == 0 { + return nil, false + } + reqCtx, cancel := context.WithTimeout(ctx, p.cfg.Runner.FetchTimeout) defer cancel() @@ -135,6 +150,7 @@ v := p.tasksVersion.Load() resp, err := p.client.FetchTask(reqCtx, connect.NewRequest(&runnerv1.FetchTaskRequest{ TasksVersion: v, + TaskCapacity: &availableCapacity, })) if errors.Is(err, context.DeadlineExceeded) { log.Trace("deadline exceeded") @@ -161,8 +177,11 @@ return nil, false } - // got a task, set `tasksVersion` to zero to focre query db in next request. + // got a task, set `tasksVersion` to zero to force query db in next request. p.tasksVersion.CompareAndSwap(resp.Msg.GetTasksVersion(), 0) - return resp.Msg.GetTask(), true + taskSlice := []*runnerv1.Task{resp.Msg.GetTask()} + taskSlice = append(taskSlice, resp.Msg.GetAdditionalTasks()...) + + return taskSlice, true } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-runner-12.3.1/internal/app/poll/poller_test.go new/forgejo-runner-12.4.0/internal/app/poll/poller_test.go --- old/forgejo-runner-12.3.1/internal/app/poll/poller_test.go 2025-12-24 17:06:29.000000000 +0100 +++ new/forgejo-runner-12.4.0/internal/app/poll/poller_test.go 2025-12-31 15:18:43.000000000 +0100 @@ -7,6 +7,7 @@ "context" "fmt" "testing" + "testing/synctest" "time" "connectrpc.com/connect" @@ -14,10 +15,14 @@ "code.forgejo.org/forgejo/actions-proto/ping/v1/pingv1connect" runnerv1 "code.forgejo.org/forgejo/actions-proto/runner/v1" "code.forgejo.org/forgejo/actions-proto/runner/v1/runnerv1connect" + mock_runner "code.forgejo.org/forgejo/runner/v12/internal/app/run/mocks" + "code.forgejo.org/forgejo/runner/v12/internal/pkg/client/mocks" "code.forgejo.org/forgejo/runner/v12/internal/pkg/config" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" ) type mockPoller struct { @@ -32,10 +37,11 @@ pingv1connect.PingServiceClient runnerv1connect.RunnerServiceClient - sleep time.Duration - cancel bool - err error - noTask bool + sleep time.Duration + cancel bool + err error + noTask bool + addtTasks bool } func (o mockClient) Address() string { @@ -69,9 +75,15 @@ o.noTask = false } + var addt []*runnerv1.Task + if o.addtTasks { + addt = append(addt, &runnerv1.Task{}) + } + return connect.NewResponse(&runnerv1.FetchTaskResponse{ - Task: task, - TasksVersion: int64(1), + Task: task, + TasksVersion: int64(1), + AdditionalTasks: addt, }), nil } @@ -206,16 +218,19 @@ func TestPoller_Fetch(t *testing.T) { setTrace(t) for _, testCase := range []struct { - name string - noTask bool - sleep time.Duration - err error - cancel bool - success bool + name string + noTask bool + sleep time.Duration + err error + cancel bool + success bool + addtTasks bool + taskCount int }{ { - name: "Success", - success: true, + name: "Success", + success: true, + taskCount: 1, }, { name: "Timeout", @@ -230,6 +245,12 @@ noTask: true, }, { + name: "AdditionalTasks", + success: true, + addtTasks: true, + taskCount: 2, + }, + { name: "Error", err: fmt.Errorf("random error"), }, @@ -245,17 +266,19 @@ Runner: configRunner, }, &mockClient{ - sleep: testCase.sleep, - cancel: testCase.cancel, - noTask: testCase.noTask, - err: testCase.err, + sleep: testCase.sleep, + cancel: testCase.cancel, + noTask: testCase.noTask, + err: testCase.err, + addtTasks: testCase.addtTasks, }, &mockRunner{}, ) - task, ok := p.fetchTask(context.Background()) + task, ok := p.fetchTasks(context.Background(), 100) if testCase.success { assert.True(t, ok) assert.NotNil(t, task) + assert.Len(t, task, testCase.taskCount) } else { assert.False(t, ok) assert.Nil(t, task) @@ -263,3 +286,191 @@ }) } } + +func TestPollerPoll(t *testing.T) { + setup := func(t *testing.T, pollingCtx context.Context) (*mocks.Client, *mock_runner.RunnerInterface, Poller) { + mockClient := mocks.NewClient(t) + mockRunner := mock_runner.NewRunnerInterface(t) + poller := New(pollingCtx, &config.Config{ + Runner: config.Runner{ + Capacity: 3, + FetchInterval: 1 * time.Second, + }, + }, mockClient, mockRunner) + return mockClient, mockRunner, poller + } + teardown := func(t *testing.T, mockClient *mocks.Client, mockRunner *mock_runner.RunnerInterface) { + mockClient.AssertExpectations(t) + mockRunner.AssertExpectations(t) + } + emptyResponse := connect.NewResponse(&runnerv1.FetchTaskResponse{ + Task: nil, + TasksVersion: int64(1), + AdditionalTasks: nil, + }) + task1 := &runnerv1.Task{} + task2 := &runnerv1.Task{} + task3 := &runnerv1.Task{} + twoTaskResponse := connect.NewResponse(&runnerv1.FetchTaskResponse{ + Task: task1, + TasksVersion: int64(1), + AdditionalTasks: []*runnerv1.Task{task2}, + }) + threeTaskResponse := connect.NewResponse(&runnerv1.FetchTaskResponse{ + Task: task1, + TasksVersion: int64(1), + AdditionalTasks: []*runnerv1.Task{task2, task3}, + }) + + // invocations of `fetchTasks` are rate limited per configuration + t.Run("fetchTasks rate limited", func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + pollingCtx, cancel := context.WithCancel(t.Context()) + defer cancel() + + mockClient, mockRunner, poller := setup(t, pollingCtx) + mockClient.On("FetchTask", mock.Anything, mock.Anything).Return(emptyResponse, nil) + + go poller.Poll() + + time.Sleep(1 * time.Millisecond) // should immediately FetchTask + mockClient.AssertNumberOfCalls(t, "FetchTask", 1) + + time.Sleep(998 * time.Millisecond) // not again until all of FetchInterval (1s) passes + mockClient.AssertNumberOfCalls(t, "FetchTask", 1) + + time.Sleep(3 * time.Millisecond) // but then it does FetchTask again + mockClient.AssertNumberOfCalls(t, "FetchTask", 2) + + require.NoError(t, poller.Shutdown(t.Context())) + + teardown(t, mockClient, mockRunner) + }) + }) + + t.Run("available capacity is passed to fetchTask", func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + pollingCtx, cancel := context.WithCancel(t.Context()) + defer cancel() + + mockClient, mockRunner, poller := setup(t, pollingCtx) + mockClient.On("FetchTask", mock.Anything, mock.Anything). + Once(). + Run(func(args mock.Arguments) { + req := args.Get(1).(*connect.Request[runnerv1.FetchTaskRequest]) + assert.EqualValues(t, 0, req.Msg.GetTasksVersion(), "GetTasksVersion") + assert.EqualValues(t, 3, req.Msg.GetTaskCapacity(), "GetTaskCapacity") + }). + Return(emptyResponse, nil) + + go poller.Poll() + time.Sleep(1 * time.Millisecond) + require.NoError(t, poller.Shutdown(t.Context())) + + teardown(t, mockClient, mockRunner) + }) + }) + + t.Run("available capacity is varied as tasks start and finish", func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + pollingCtx, cancel := context.WithCancel(t.Context()) + defer cancel() + + mockClient, mockRunner, poller := setup(t, pollingCtx) + // First fetch -- assert 3 capacity requested, return two tasks + mockClient.On("FetchTask", mock.Anything, mock.Anything). + Once(). + Run(func(args mock.Arguments) { + req := args.Get(1).(*connect.Request[runnerv1.FetchTaskRequest]) + assert.EqualValues(t, 0, req.Msg.GetTasksVersion(), "GetTasksVersion Call 1") + assert.EqualValues(t, 3, req.Msg.GetTaskCapacity(), "GetTaskCapacity Call 1") + }). + Return(twoTaskResponse, nil) + // Second fetch -- assert 1 capacity, return no tasks + mockClient.On("FetchTask", mock.Anything, mock.Anything). + Once(). + Run(func(args mock.Arguments) { + req := args.Get(1).(*connect.Request[runnerv1.FetchTaskRequest]) + assert.EqualValues(t, 0, req.Msg.GetTasksVersion(), "GetTasksVersion Call 2") + assert.EqualValues(t, 1, req.Msg.GetTaskCapacity(), "GetTaskCapacity Call 2") + }). + Return(emptyResponse, nil) + // Third fetch -- assert 3 capacity, return no tasks + mockClient.On("FetchTask", mock.Anything, mock.Anything). + Once(). + Run(func(args mock.Arguments) { + req := args.Get(1).(*connect.Request[runnerv1.FetchTaskRequest]) + assert.EqualValues(t, 1, req.Msg.GetTasksVersion(), "GetTasksVersion Call 3") + assert.EqualValues(t, 3, req.Msg.GetTaskCapacity(), "GetTaskCapacity Call 3") + }). + Return(emptyResponse, nil) + mockRunner.On("Run", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + // Take some time to execute so that the 2nd FetchTask still has these tasks considered in-progress. + time.Sleep(1500 * time.Millisecond) + }). + Return(nil) + + go poller.Poll() + time.Sleep(1 * time.Millisecond) + mockClient.AssertNumberOfCalls(t, "FetchTask", 1) + time.Sleep(1 * time.Second) + mockClient.AssertNumberOfCalls(t, "FetchTask", 2) + time.Sleep(1 * time.Second) + mockClient.AssertNumberOfCalls(t, "FetchTask", 3) + require.NoError(t, poller.Shutdown(t.Context())) + + teardown(t, mockClient, mockRunner) + }) + }) + + t.Run("no FetchTask when available capacity is zero", func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + pollingCtx, cancel := context.WithCancel(t.Context()) + defer cancel() + + mockClient, mockRunner, poller := setup(t, pollingCtx) + mockClient.On("FetchTask", mock.Anything, mock.Anything). + Return(threeTaskResponse, nil) + mockRunner.On("Run", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + time.Sleep(1 * time.Hour) + }). + Return(nil) + + go poller.Poll() + time.Sleep(1 * time.Millisecond) + mockClient.AssertNumberOfCalls(t, "FetchTask", 1) + time.Sleep(30 * time.Minute) // a long time later, but jobs are using up all the capacity... + mockClient.AssertNumberOfCalls(t, "FetchTask", 1) + require.NoError(t, poller.Shutdown(t.Context())) + + teardown(t, mockClient, mockRunner) + }) + }) + + t.Run("poll shutdown waits for task completion", func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + pollingCtx, cancel := context.WithCancel(t.Context()) + defer cancel() + + mockClient, mockRunner, poller := setup(t, pollingCtx) + mockClient.On("FetchTask", mock.Anything, mock.Anything). + Return(twoTaskResponse, nil) + mockRunner.On("Run", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + time.Sleep(1 * time.Hour) + }). + Return(nil) + + go poller.Poll() + time.Sleep(1 * time.Millisecond) // let poll get started, fetch tasks, start them + shutdownStart := time.Now() + require.NoError(t, poller.Shutdown(t.Context())) + shutdownEnd := time.Now() + assert.EqualValues(t, 3599999000, shutdownEnd.Sub(shutdownStart).Microseconds()) + + teardown(t, mockClient, mockRunner) + }) + }) +} ++++++ forgejo-runner.obsinfo ++++++ --- /var/tmp/diff_new_pack.9rCpS4/_old 2026-01-05 14:56:58.279786406 +0100 +++ /var/tmp/diff_new_pack.9rCpS4/_new 2026-01-05 14:56:58.295787072 +0100 @@ -1,5 +1,5 @@ name: forgejo-runner -version: 12.3.1 -mtime: 1766592389 -commit: 4e1da369ef8c5e6ce963b0121393af6f78d0df0b +version: 12.4.0 +mtime: 1767190723 +commit: a4f4474fed53d9a687e4363116b07b33c1f6f66f ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/code.forgejo.org/forgejo/actions-proto/runner/v1/messages.pb.go new/vendor/code.forgejo.org/forgejo/actions-proto/runner/v1/messages.pb.go --- old/vendor/code.forgejo.org/forgejo/actions-proto/runner/v1/messages.pb.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/code.forgejo.org/forgejo/actions-proto/runner/v1/messages.pb.go 2025-12-31 15:18:43.000000000 +0100 @@ -384,7 +384,10 @@ sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TasksVersion int64 `protobuf:"varint,1,opt,name=tasks_version,json=tasksVersion,proto3" json:"tasks_version,omitempty"` // Runner use `tasks_version` to compare with Gitea and detemine whether new tasks may exist. + // Runner use `tasks_version` to compare with Gitea and detemine whether new tasks may exist. + TasksVersion int64 `protobuf:"varint,1,opt,name=tasks_version,json=tasksVersion,proto3" json:"tasks_version,omitempty"` + // If provided and `>1`, then multiple tasks may be returned from FetchTask in `additional_tasks`. + TaskCapacity *int64 `protobuf:"varint,2,opt,name=task_capacity,json=taskCapacity,proto3,oneof" json:"task_capacity,omitempty"` } func (x *FetchTaskRequest) Reset() { @@ -426,13 +429,25 @@ return 0 } +func (x *FetchTaskRequest) GetTaskCapacity() int64 { + if x != nil && x.TaskCapacity != nil { + return *x.TaskCapacity + } + return 0 +} + type FetchTaskResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Task *Task `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` - TasksVersion int64 `protobuf:"varint,2,opt,name=tasks_version,json=tasksVersion,proto3" json:"tasks_version,omitempty"` // Gitea informs the Runner of the latest version of tasks through `tasks_version`. + Task *Task `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` + // Gitea informs the Runner of the latest version of tasks through `tasks_version`. + TasksVersion int64 `protobuf:"varint,2,opt,name=tasks_version,json=tasksVersion,proto3" json:"tasks_version,omitempty"` + // If `task_capacity` was provided in `FetchTaskRequest`, then additional_tasks may indicate multiple assigned tasks + // to this runner which are returned in `additional_tasks`. `task` would always be populated for the first task, and + // the first task is omitted from this collection. + AdditionalTasks []*Task `protobuf:"bytes,3,rep,name=additional_tasks,json=additionalTasks,proto3" json:"additional_tasks,omitempty"` } func (x *FetchTaskResponse) Reset() { @@ -481,6 +496,13 @@ return 0 } +func (x *FetchTaskResponse) GetAdditionalTasks() []*Task { + if x != nil { + return x.AdditionalTasks + } + return nil +} + type UpdateTaskRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1246,171 +1268,178 @@ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x06, 0x72, 0x75, 0x6e, - 0x6e, 0x65, 0x72, 0x22, 0x37, 0x0a, 0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 0x54, 0x61, 0x73, 0x6b, + 0x6e, 0x65, 0x72, 0x22, 0x73, 0x0a, 0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, - 0x74, 0x61, 0x73, 0x6b, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x11, - 0x46, 0x65, 0x74, 0x63, 0x68, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x23, 0x0a, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, - 0x52, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, - 0x61, 0x73, 0x6b, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc0, 0x01, 0x0a, 0x11, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, - 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, - 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, - 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x63, - 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x6f, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, - 0x64, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x25, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x17, - 0x0a, 0x07, 0x6e, 0x6f, 0x5f, 0x6d, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x06, 0x6e, 0x6f, 0x4d, 0x6f, 0x72, 0x65, 0x22, 0x30, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, - 0x61, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xa7, 0x02, 0x0a, 0x06, 0x52, 0x75, - 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0d, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, - 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, - 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, - 0x72, 0x61, 0x6c, 0x22, 0x9a, 0x04, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x10, - 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x07, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x01, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x07, - 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, - 0x01, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x6e, 0x65, - 0x65, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x75, 0x6e, 0x6e, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x2e, 0x4e, 0x65, 0x65, 0x64, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x04, - 0x76, 0x61, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x75, 0x6e, - 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x2e, 0x56, 0x61, 0x72, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x74, 0x61, 0x73, 0x6b, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0d, + 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x61, 0x73, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x22, 0x99, 0x01, 0x0a, 0x11, 0x46, 0x65, 0x74, + 0x63, 0x68, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, + 0x0a, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, + 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x04, 0x74, + 0x61, 0x73, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x5f, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x61, 0x73, 0x6b, + 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x10, 0x61, 0x64, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x61, 0x73, 0x6b, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x54, + 0x61, 0x73, 0x6b, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, + 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x75, 0x6e, 0x6e, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x4f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4d, 0x0a, 0x0a, 0x4e, 0x65, 0x65, 0x64, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x65, 0x65, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, - 0x13, 0x0a, 0x11, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x22, 0xad, 0x01, 0x0a, 0x08, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x65, 0x65, 0x64, 0x12, 0x3a, 0x0a, - 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, - 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4e, - 0x65, 0x65, 0x64, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x3a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x63, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, + 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x6e, + 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0b, 0x73, 0x65, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x81, 0x01, 0x0a, + 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x25, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x6f, + 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x5f, 0x6d, 0x6f, + 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6e, 0x6f, 0x4d, 0x6f, 0x72, 0x65, + 0x22, 0x30, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x22, 0xa7, 0x02, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x75, + 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x0c, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x4c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x22, 0x9a, 0x04, 0x0a, + 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, + 0x00, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x50, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, + 0x01, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, + 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x2e, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x61, 0x73, 0x6b, 0x2e, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, + 0x6e, 0x65, 0x65, 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x04, 0x76, 0x61, 0x72, 0x73, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x61, 0x73, 0x6b, 0x2e, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, + 0x76, 0x61, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0xe8, 0x01, 0x0a, 0x09, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, - 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, - 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x22, 0xf8, 0x01, 0x0a, 0x09, - 0x53, 0x74, 0x65, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, - 0x67, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, - 0x6f, 0x67, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x6f, 0x67, - 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x52, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x77, - 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2a, 0x7a, 0x0a, 0x0c, 0x52, 0x75, - 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x55, - 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x55, 0x4e, - 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, - 0x01, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x55, 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, - 0x55, 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4f, 0x46, 0x46, - 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x03, 0x2a, 0x72, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x53, 0x55, - 0x4c, 0x54, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, - 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x02, - 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, - 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, - 0x5f, 0x53, 0x4b, 0x49, 0x50, 0x50, 0x45, 0x44, 0x10, 0x04, 0x42, 0x9e, 0x01, 0x0a, 0x0d, 0x63, - 0x6f, 0x6d, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x63, - 0x6f, 0x64, 0x65, 0x2e, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x6a, 0x6f, 0x2e, 0x6f, 0x72, 0x67, 0x2f, - 0x66, 0x6f, 0x72, 0x67, 0x65, 0x6a, 0x6f, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, - 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x52, 0x58, 0x58, 0xaa, 0x02, - 0x09, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x09, 0x52, 0x75, 0x6e, - 0x6e, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x15, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5c, - 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x0a, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x1a, 0x4d, 0x0a, 0x0a, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, + 0x4e, 0x65, 0x65, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x37, 0x0a, 0x09, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x77, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xad, 0x01, 0x0a, 0x08, 0x54, 0x61, + 0x73, 0x6b, 0x4e, 0x65, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x65, 0x65, 0x64, 0x2e, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x3a, 0x0a, + 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe8, 0x01, 0x0a, 0x09, 0x54, 0x61, + 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, + 0x0a, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, + 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, + 0x74, 0x65, 0x70, 0x73, 0x22, 0xf8, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x65, 0x70, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x39, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x6f, 0x70, + 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x52, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x77, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2a, 0x7a, 0x0a, 0x0c, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x55, 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x55, 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x55, + 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x55, 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4f, 0x46, 0x46, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x03, 0x2a, + 0x72, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, + 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x53, 0x55, 0x43, 0x43, + 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, + 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x53, + 0x55, 0x4c, 0x54, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, + 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x53, 0x4b, 0x49, 0x50, 0x50, 0x45, + 0x44, 0x10, 0x04, 0x42, 0x9e, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x75, 0x6e, 0x6e, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x66, 0x6f, 0x72, + 0x67, 0x65, 0x6a, 0x6f, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x6a, 0x6f, + 0x2f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, + 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x76, + 0x31, 0xa2, 0x02, 0x03, 0x52, 0x58, 0x58, 0xaa, 0x02, 0x09, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x2e, 0x56, 0x31, 0xca, 0x02, 0x09, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, + 0x02, 0x15, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0a, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1458,31 +1487,32 @@ 12, // 0: runner.v1.RegisterResponse.runner:type_name -> runner.v1.Runner 12, // 1: runner.v1.DeclareResponse.runner:type_name -> runner.v1.Runner 13, // 2: runner.v1.FetchTaskResponse.task:type_name -> runner.v1.Task - 15, // 3: runner.v1.UpdateTaskRequest.state:type_name -> runner.v1.TaskState - 18, // 4: runner.v1.UpdateTaskRequest.outputs:type_name -> runner.v1.UpdateTaskRequest.OutputsEntry - 15, // 5: runner.v1.UpdateTaskResponse.state:type_name -> runner.v1.TaskState - 17, // 6: runner.v1.UpdateLogRequest.rows:type_name -> runner.v1.LogRow - 0, // 7: runner.v1.Runner.status:type_name -> runner.v1.RunnerStatus - 23, // 8: runner.v1.Task.context:type_name -> google.protobuf.Struct - 19, // 9: runner.v1.Task.secrets:type_name -> runner.v1.Task.SecretsEntry - 20, // 10: runner.v1.Task.needs:type_name -> runner.v1.Task.NeedsEntry - 21, // 11: runner.v1.Task.vars:type_name -> runner.v1.Task.VarsEntry - 22, // 12: runner.v1.TaskNeed.outputs:type_name -> runner.v1.TaskNeed.OutputsEntry - 1, // 13: runner.v1.TaskNeed.result:type_name -> runner.v1.Result - 1, // 14: runner.v1.TaskState.result:type_name -> runner.v1.Result - 24, // 15: runner.v1.TaskState.started_at:type_name -> google.protobuf.Timestamp - 24, // 16: runner.v1.TaskState.stopped_at:type_name -> google.protobuf.Timestamp - 16, // 17: runner.v1.TaskState.steps:type_name -> runner.v1.StepState - 1, // 18: runner.v1.StepState.result:type_name -> runner.v1.Result - 24, // 19: runner.v1.StepState.started_at:type_name -> google.protobuf.Timestamp - 24, // 20: runner.v1.StepState.stopped_at:type_name -> google.protobuf.Timestamp - 24, // 21: runner.v1.LogRow.time:type_name -> google.protobuf.Timestamp - 14, // 22: runner.v1.Task.NeedsEntry.value:type_name -> runner.v1.TaskNeed - 23, // [23:23] is the sub-list for method output_type - 23, // [23:23] is the sub-list for method input_type - 23, // [23:23] is the sub-list for extension type_name - 23, // [23:23] is the sub-list for extension extendee - 0, // [0:23] is the sub-list for field type_name + 13, // 3: runner.v1.FetchTaskResponse.additional_tasks:type_name -> runner.v1.Task + 15, // 4: runner.v1.UpdateTaskRequest.state:type_name -> runner.v1.TaskState + 18, // 5: runner.v1.UpdateTaskRequest.outputs:type_name -> runner.v1.UpdateTaskRequest.OutputsEntry + 15, // 6: runner.v1.UpdateTaskResponse.state:type_name -> runner.v1.TaskState + 17, // 7: runner.v1.UpdateLogRequest.rows:type_name -> runner.v1.LogRow + 0, // 8: runner.v1.Runner.status:type_name -> runner.v1.RunnerStatus + 23, // 9: runner.v1.Task.context:type_name -> google.protobuf.Struct + 19, // 10: runner.v1.Task.secrets:type_name -> runner.v1.Task.SecretsEntry + 20, // 11: runner.v1.Task.needs:type_name -> runner.v1.Task.NeedsEntry + 21, // 12: runner.v1.Task.vars:type_name -> runner.v1.Task.VarsEntry + 22, // 13: runner.v1.TaskNeed.outputs:type_name -> runner.v1.TaskNeed.OutputsEntry + 1, // 14: runner.v1.TaskNeed.result:type_name -> runner.v1.Result + 1, // 15: runner.v1.TaskState.result:type_name -> runner.v1.Result + 24, // 16: runner.v1.TaskState.started_at:type_name -> google.protobuf.Timestamp + 24, // 17: runner.v1.TaskState.stopped_at:type_name -> google.protobuf.Timestamp + 16, // 18: runner.v1.TaskState.steps:type_name -> runner.v1.StepState + 1, // 19: runner.v1.StepState.result:type_name -> runner.v1.Result + 24, // 20: runner.v1.StepState.started_at:type_name -> google.protobuf.Timestamp + 24, // 21: runner.v1.StepState.stopped_at:type_name -> google.protobuf.Timestamp + 24, // 22: runner.v1.LogRow.time:type_name -> google.protobuf.Timestamp + 14, // 23: runner.v1.Task.NeedsEntry.value:type_name -> runner.v1.TaskNeed + 24, // [24:24] is the sub-list for method output_type + 24, // [24:24] is the sub-list for method input_type + 24, // [24:24] is the sub-list for extension type_name + 24, // [24:24] is the sub-list for extension extendee + 0, // [0:24] is the sub-list for field type_name } func init() { file_runner_v1_messages_proto_init() } @@ -1684,6 +1714,7 @@ } } } + file_runner_v1_messages_proto_msgTypes[4].OneofWrappers = []interface{}{} file_runner_v1_messages_proto_msgTypes[11].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go new/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go --- old/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go 2025-12-31 15:18:43.000000000 +0100 @@ -32,7 +32,7 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor { f := new(filedesc.Field) f.L0.ParentFile = filedesc.SurrogateProto2 - f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures + packed := false for len(tag) > 0 { i := strings.IndexByte(tag, ',') if i < 0 { @@ -108,7 +108,7 @@ f.L1.StringName.InitJSON(jsonName) } case s == "packed": - f.L1.EditionFeatures.IsPacked = true + packed = true case strings.HasPrefix(s, "def="): // The default tag is special in that everything afterwards is the // default regardless of the presence of commas. @@ -121,6 +121,13 @@ tag = strings.TrimPrefix(tag[i:], ",") } + // Update EditionFeatures after the loop and after we know whether this is + // a proto2 or proto3 field. + f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures + if packed { + f.L1.EditionFeatures.IsPacked = true + } + // The generator uses the group message name instead of the field name. // We obtain the real field name by lowercasing the group name. if f.L1.Kind == protoreflect.GroupKind { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go new/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go --- old/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go 2025-12-31 15:18:43.000000000 +0100 @@ -424,27 +424,34 @@ return Token{}, d.newSyntaxError("invalid field name: %s", errId(d.in)) } -// parseTypeName parses Any type URL or extension field name. The name is -// enclosed in [ and ] characters. The C++ parser does not handle many legal URL -// strings. This implementation is more liberal and allows for the pattern -// ^[-_a-zA-Z0-9]+([./][-_a-zA-Z0-9]+)*`). Whitespaces and comments are allowed -// in between [ ], '.', '/' and the sub names. +// parseTypeName parses an Any type URL or an extension field name. The name is +// enclosed in [ and ] characters. We allow almost arbitrary type URL prefixes, +// closely following the text-format spec [1,2]. We implement "ExtensionName | +// AnyName" as follows (with some exceptions for backwards compatibility): +// +// char = [-_a-zA-Z0-9] +// url_char = char | [.~!$&'()*+,;=] | "%", hex, hex +// +// Ident = char, { char } +// TypeName = Ident, { ".", Ident } ; +// UrlPrefix = url_char, { url_char | "/" } ; +// ExtensionName = "[", TypeName, "]" ; +// AnyName = "[", UrlPrefix, "/", TypeName, "]" ; +// +// Additionally, we allow arbitrary whitespace and comments between [ and ]. +// +// [1] https://protobuf.dev/reference/protobuf/textformat-spec/#characters +// [2] https://protobuf.dev/reference/protobuf/textformat-spec/#field-names func (d *Decoder) parseTypeName() (Token, error) { - startPos := len(d.orig) - len(d.in) // Use alias s to advance first in order to use d.in for error handling. - // Caller already checks for [ as first character. + // Caller already checks for [ as first character (d.in[0] == '['). s := consume(d.in[1:], 0) if len(s) == 0 { return Token{}, ErrUnexpectedEOF } + // Collect everything between [ and ] in name. var name []byte - for len(s) > 0 && isTypeNameChar(s[0]) { - name = append(name, s[0]) - s = s[1:] - } - s = consume(s, 0) - var closed bool for len(s) > 0 && !closed { switch { @@ -452,23 +459,20 @@ s = s[1:] closed = true - case s[0] == '/', s[0] == '.': - if len(name) > 0 && (name[len(name)-1] == '/' || name[len(name)-1] == '.') { - return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s", - d.orig[startPos:len(d.orig)-len(s)+1]) - } + case s[0] == '/' || isTypeNameChar(s[0]) || isUrlExtraChar(s[0]): name = append(name, s[0]) - s = s[1:] - s = consume(s, 0) - for len(s) > 0 && isTypeNameChar(s[0]) { - name = append(name, s[0]) - s = s[1:] + s = consume(s[1:], 0) + + // URL percent-encoded chars + case s[0] == '%': + if len(s) < 3 || !isHexChar(s[1]) || !isHexChar(s[2]) { + return Token{}, d.parseTypeNameError(s, 3) } - s = consume(s, 0) + name = append(name, s[0], s[1], s[2]) + s = consume(s[3:], 0) default: - return Token{}, d.newSyntaxError( - "invalid type URL/extension field name: %s", d.orig[startPos:len(d.orig)-len(s)+1]) + return Token{}, d.parseTypeNameError(s, 1) } } @@ -476,15 +480,38 @@ return Token{}, ErrUnexpectedEOF } - // First character cannot be '.'. Last character cannot be '.' or '/'. - size := len(name) - if size == 0 || name[0] == '.' || name[size-1] == '.' || name[size-1] == '/' { - return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s", - d.orig[startPos:len(d.orig)-len(s)]) + // Split collected name on last '/' into urlPrefix and typeName (if '/' is + // present). + typeName := name + if i := bytes.LastIndexByte(name, '/'); i != -1 { + urlPrefix := name[:i] + typeName = name[i+1:] + + // urlPrefix may be empty (for backwards compatibility). + // If non-empty, it must not start with '/'. + if len(urlPrefix) > 0 && urlPrefix[0] == '/' { + return Token{}, d.parseTypeNameError(s, 0) + } + } + + // typeName must not be empty (note: "" splits to [""]) and all identifier + // parts must not be empty. + for _, ident := range bytes.Split(typeName, []byte{'.'}) { + if len(ident) == 0 { + return Token{}, d.parseTypeNameError(s, 0) + } } + // typeName must not contain any percent-encoded or special URL chars. + for _, b := range typeName { + if b == '%' || (b != '.' && isUrlExtraChar(b)) { + return Token{}, d.parseTypeNameError(s, 0) + } + } + + startPos := len(d.orig) - len(d.in) + endPos := len(d.orig) - len(s) d.in = s - endPos := len(d.orig) - len(d.in) d.consume(0) return Token{ @@ -496,16 +523,32 @@ }, nil } +func (d *Decoder) parseTypeNameError(s []byte, numUnconsumedChars int) error { + return d.newSyntaxError( + "invalid type URL/extension field name: %s", + d.in[:len(d.in)-len(s)+min(numUnconsumedChars, len(s))], + ) +} + +func isHexChar(b byte) bool { + return ('0' <= b && b <= '9') || + ('a' <= b && b <= 'f') || + ('A' <= b && b <= 'F') +} + func isTypeNameChar(b byte) bool { - return (b == '-' || b == '_' || + return b == '-' || b == '_' || ('0' <= b && b <= '9') || ('a' <= b && b <= 'z') || - ('A' <= b && b <= 'Z')) + ('A' <= b && b <= 'Z') } -func isWhiteSpace(b byte) bool { +// isUrlExtraChar complements isTypeNameChar with extra characters that we allow +// in URLs but not in type names. Note that '/' is not included so that it can +// be treated specially. +func isUrlExtraChar(b byte) bool { switch b { - case ' ', '\n', '\r', '\t': + case '.', '~', '!', '$', '&', '(', ')', '*', '+', ',', ';', '=': return true default: return false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/filedesc/desc.go new/vendor/google.golang.org/protobuf/internal/filedesc/desc.go --- old/vendor/google.golang.org/protobuf/internal/filedesc/desc.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/filedesc/desc.go 2025-12-31 15:18:43.000000000 +0100 @@ -32,6 +32,7 @@ EditionProto3 Edition = 999 Edition2023 Edition = 1000 Edition2024 Edition = 1001 + EditionUnstable Edition = 9999 EditionUnsupported Edition = 100000 ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go new/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go --- old/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go 2025-12-31 15:18:43.000000000 +0100 @@ -330,7 +330,6 @@ md.L1.Extensions.List[extensionIdx].unmarshalFull(v, sb) extensionIdx++ case genid.DescriptorProto_Options_field_number: - md.unmarshalOptions(v) rawOptions = appendOptions(rawOptions, v) } default: @@ -356,27 +355,6 @@ md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Message, rawOptions) } -func (md *Message) unmarshalOptions(b []byte) { - for len(b) > 0 { - num, typ, n := protowire.ConsumeTag(b) - b = b[n:] - switch typ { - case protowire.VarintType: - v, m := protowire.ConsumeVarint(b) - b = b[m:] - switch num { - case genid.MessageOptions_MapEntry_field_number: - md.L1.IsMapEntry = protowire.DecodeBool(v) - case genid.MessageOptions_MessageSetWireFormat_field_number: - md.L1.IsMessageSet = protowire.DecodeBool(v) - } - default: - m := protowire.ConsumeFieldValue(num, typ, b) - b = b[m:] - } - } -} - func unmarshalMessageReservedRange(b []byte) (r [2]protoreflect.FieldNumber) { for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go new/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go --- old/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go 2025-12-31 15:18:43.000000000 +0100 @@ -26,6 +26,7 @@ Edition_EDITION_PROTO3_enum_value = 999 Edition_EDITION_2023_enum_value = 1000 Edition_EDITION_2024_enum_value = 1001 + Edition_EDITION_UNSTABLE_enum_value = 9999 Edition_EDITION_1_TEST_ONLY_enum_value = 1 Edition_EDITION_2_TEST_ONLY_enum_value = 2 Edition_EDITION_99997_TEST_ONLY_enum_value = 99997 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/impl/codec_map.go new/vendor/google.golang.org/protobuf/internal/impl/codec_map.go --- old/vendor/google.golang.org/protobuf/internal/impl/codec_map.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/impl/codec_map.go 2025-12-31 15:18:43.000000000 +0100 @@ -113,6 +113,9 @@ } func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { + if opts.depth--; opts.depth < 0 { + return out, errRecursionDepth + } if wtyp != protowire.BytesType { return out, errUnknown } @@ -170,6 +173,9 @@ } func consumeMapOfMessage(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { + if opts.depth--; opts.depth < 0 { + return out, errRecursionDepth + } if wtyp != protowire.BytesType { return out, errUnknown } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/impl/decode.go new/vendor/google.golang.org/protobuf/internal/impl/decode.go --- old/vendor/google.golang.org/protobuf/internal/impl/decode.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/impl/decode.go 2025-12-31 15:18:43.000000000 +0100 @@ -102,8 +102,7 @@ func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) { mi.init() - opts.depth-- - if opts.depth < 0 { + if opts.depth--; opts.depth < 0 { return out, errRecursionDepth } if flags.ProtoLegacy && mi.isMessageSet { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/impl/validate.go new/vendor/google.golang.org/protobuf/internal/impl/validate.go --- old/vendor/google.golang.org/protobuf/internal/impl/validate.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/impl/validate.go 2025-12-31 15:18:43.000000000 +0100 @@ -68,9 +68,13 @@ if in.Resolver == nil { in.Resolver = protoregistry.GlobalTypes } + if in.Depth == 0 { + in.Depth = protowire.DefaultRecursionLimit + } o, st := mi.validate(in.Buf, 0, unmarshalOptions{ flags: in.Flags, resolver: in.Resolver, + depth: in.Depth, }) if o.initialized { out.Flags |= protoiface.UnmarshalInitialized @@ -257,6 +261,9 @@ states[0].typ = validationTypeGroup states[0].endGroup = groupTag } + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } initialized := true start := len(b) State: @@ -451,6 +458,13 @@ mi: vi.mi, tail: b, }) + if vi.typ == validationTypeMessage || + vi.typ == validationTypeGroup || + vi.typ == validationTypeMap { + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } + } b = v continue State case validationTypeRepeatedVarint: @@ -499,6 +513,9 @@ mi: vi.mi, endGroup: num, }) + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } continue State case flags.ProtoLegacy && vi.typ == validationTypeMessageSetItem: typeid, v, n, err := messageset.ConsumeFieldValue(b, false) @@ -521,6 +538,13 @@ mi: xvi.mi, tail: b[n:], }) + if xvi.typ == validationTypeMessage || + xvi.typ == validationTypeGroup || + xvi.typ == validationTypeMap { + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } + } b = v continue State } @@ -547,12 +571,14 @@ switch st.typ { case validationTypeMessage, validationTypeGroup: numRequiredFields = int(st.mi.numRequiredFields) + opts.depth++ case validationTypeMap: // If this is a map field with a message value that contains // required fields, require that the value be present. if st.mi != nil && st.mi.numRequiredFields > 0 { numRequiredFields = 1 } + opts.depth++ } // If there are more than 64 required fields, this check will // always fail and we will report that the message is potentially diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/version/version.go new/vendor/google.golang.org/protobuf/internal/version/version.go --- old/vendor/google.golang.org/protobuf/internal/version/version.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/version/version.go 2025-12-31 15:18:43.000000000 +0100 @@ -52,7 +52,7 @@ const ( Major = 1 Minor = 36 - Patch = 10 + Patch = 11 PreRelease = "" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/proto/decode.go new/vendor/google.golang.org/protobuf/proto/decode.go --- old/vendor/google.golang.org/protobuf/proto/decode.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/proto/decode.go 2025-12-31 15:18:43.000000000 +0100 @@ -121,9 +121,8 @@ out, err = methods.Unmarshal(in) } else { - o.RecursionLimit-- - if o.RecursionLimit < 0 { - return out, errors.New("exceeded max recursion depth") + if o.RecursionLimit--; o.RecursionLimit < 0 { + return out, errRecursionDepth } err = o.unmarshalMessageSlow(b, m) } @@ -220,6 +219,9 @@ } func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv protoreflect.Map, fd protoreflect.FieldDescriptor) (n int, err error) { + if o.RecursionLimit--; o.RecursionLimit < 0 { + return 0, errRecursionDepth + } if wtyp != protowire.BytesType { return 0, errUnknown } @@ -305,3 +307,5 @@ var errUnknown = errors.New("BUG: internal error (unknown)") var errDecode = errors.New("cannot parse invalid wire-format data") + +var errRecursionDepth = errors.New("exceeded maximum recursion depth") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go new/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go --- old/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go 2025-12-31 15:18:43.000000000 +0100 @@ -172,13 +172,14 @@ // ) to obtain a formatter capable of generating timestamps in this format. type Timestamp struct { state protoimpl.MessageState `protogen:"open.v1"` - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. + // Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must + // be between -315576000000 and 315576000000 inclusive (which corresponds to + // 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z). Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 + // Non-negative fractions of a second at nanosecond resolution. This field is + // the nanosecond portion of the duration, not an alternative to seconds. + // Negative second values with fractions must still have non-negative nanos + // values that count forward in time. Must be between 0 and 999,999,999 // inclusive. Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` unknownFields protoimpl.UnknownFields diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2025-12-24 17:06:29.000000000 +0100 +++ new/vendor/modules.txt 2025-12-31 15:18:43.000000000 +0100 @@ -1,4 +1,4 @@ -# code.forgejo.org/forgejo/actions-proto v0.5.3 +# code.forgejo.org/forgejo/actions-proto v0.6.0 ## explicit; go 1.24.0 code.forgejo.org/forgejo/actions-proto/ping/v1 code.forgejo.org/forgejo/actions-proto/ping/v1/pingv1connect @@ -504,7 +504,7 @@ golang.org/x/time/rate # google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f ## explicit; go 1.19 -# google.golang.org/protobuf v1.36.10 +# google.golang.org/protobuf v1.36.11 ## explicit; go 1.23 google.golang.org/protobuf/encoding/protojson google.golang.org/protobuf/encoding/prototext
