feat(proto): list, disable and enable workflows per repository
- commit
- d244a03090e3dae702b8b02360979fc00b28aa45
- author
- brice · · 2026-10-18 19:12 UTC
- parent
- 4269eaf
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email - refs
- feat/workflow-disabletag: v0.7.0
5 files · +822 -116
M docs/services.md +1 -1
| @@ -10,7 +10,7 @@ | |||
| 10 | 10 | | `EventService` | `Watch` (streamed, resumable) | | |
| 11 | 11 | | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) | | |
| 12 | 12 | | `TrustedKeyService` | `ListTrustedKeys`, `AddTrustedKey` and `RemoveTrustedKey` (owner only). `Commit.verification` carries the result of the check (feature `commit_signatures`) | | |
| 13 | - | | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` | | |
| 13 | + | | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListWorkflows`, `DisableWorkflow`, `EnableWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` | | |
| 14 | 14 | | `RunnerService` | `RegisterRunner`, `FetchJob` (long poll), `UpdateJob`, `AppendLog` (offset-idempotent), `Heartbeat`, `WatchJob` (cancel requests, streamed), `CompleteJob` (idempotent), `ResolveActions`, `UploadSummary` | | |
| 15 | 15 | | `SecretService` | `SetSecret`, `ListSecrets` (names only), `DeleteSecret`. Scopes: instance, repository, environment | | |
| 16 | 16 | | `VariableService` | `SetVariable`, `GetVariable`, `ListVariables`, `DeleteVariable`. Same scopes | | |
| @@ -10,7 +10,7 @@ | |||||
| 10 | | `EventService` | `Watch` (streamed, resumable) | | 10 | | `EventService` | `Watch` (streamed, resumable) | | ||
| 11 | | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) | | 11 | | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) | | ||
| 12 | | `TrustedKeyService` | `ListTrustedKeys`, `AddTrustedKey` and `RemoveTrustedKey` (owner only). `Commit.verification` carries the result of the check (feature `commit_signatures`) | | 12 | | `TrustedKeyService` | `ListTrustedKeys`, `AddTrustedKey` and `RemoveTrustedKey` (owner only). `Commit.verification` carries the result of the check (feature `commit_signatures`) | | ||
| 13 | - | | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` | | 13 | + | | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListWorkflows`, `DisableWorkflow`, `EnableWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` | |
| 14 | | `RunnerService` | `RegisterRunner`, `FetchJob` (long poll), `UpdateJob`, `AppendLog` (offset-idempotent), `Heartbeat`, `WatchJob` (cancel requests, streamed), `CompleteJob` (idempotent), `ResolveActions`, `UploadSummary` | | 14 | | `RunnerService` | `RegisterRunner`, `FetchJob` (long poll), `UpdateJob`, `AppendLog` (offset-idempotent), `Heartbeat`, `WatchJob` (cancel requests, streamed), `CompleteJob` (idempotent), `ResolveActions`, `UploadSummary` | | ||
| 15 | | `SecretService` | `SetSecret`, `ListSecrets` (names only), `DeleteSecret`. Scopes: instance, repository, environment | | 15 | | `SecretService` | `SetSecret`, `ListSecrets` (names only), `DeleteSecret`. Scopes: instance, repository, environment | | ||
| 16 | | `VariableService` | `SetVariable`, `GetVariable`, `ListVariables`, `DeleteVariable`. Same scopes | | 16 | | `VariableService` | `SetVariable`, `GetVariable`, `ListVariables`, `DeleteVariable`. Same scopes | | ||
M gen/ermite/ermiteconnect/run.connect.go +141 -0
| @@ -56,6 +56,15 @@ const ( | |||
| 56 | 56 | // RunServiceDispatchWorkflowProcedure is the fully-qualified name of the RunService's | |
| 57 | 57 | // DispatchWorkflow RPC. | |
| 58 | 58 | RunServiceDispatchWorkflowProcedure = "/ermite.RunService/DispatchWorkflow" | |
| 59 | + | // RunServiceListWorkflowsProcedure is the fully-qualified name of the RunService's ListWorkflows | |
| 60 | + | // RPC. | |
| 61 | + | RunServiceListWorkflowsProcedure = "/ermite.RunService/ListWorkflows" | |
| 62 | + | // RunServiceDisableWorkflowProcedure is the fully-qualified name of the RunService's | |
| 63 | + | // DisableWorkflow RPC. | |
| 64 | + | RunServiceDisableWorkflowProcedure = "/ermite.RunService/DisableWorkflow" | |
| 65 | + | // RunServiceEnableWorkflowProcedure is the fully-qualified name of the RunService's EnableWorkflow | |
| 66 | + | // RPC. | |
| 67 | + | RunServiceEnableWorkflowProcedure = "/ermite.RunService/EnableWorkflow" | |
| 59 | 68 | // RunServiceListChecksProcedure is the fully-qualified name of the RunService's ListChecks RPC. | |
| 60 | 69 | RunServiceListChecksProcedure = "/ermite.RunService/ListChecks" | |
| 61 | 70 | // RunServiceListRunnersProcedure is the fully-qualified name of the RunService's ListRunners RPC. | |
| @@ -145,6 +154,36 @@ type RunServiceClient interface { | |||
| 145 | 154 | // missing while required without default, or has a value of the wrong type | |
| 146 | 155 | // or outside its choices, or if the workflow file is not valid. | |
| 147 | 156 | DispatchWorkflow(context.Context, *connect.Request[ermite.DispatchWorkflowRequest]) (*connect.Response[ermite.DispatchWorkflowResponse], error) | |
| 157 | + | // ListWorkflows lists the workflow files of a repository with their state, | |
| 158 | + | // like `gh workflow list --all`, ordered by path: the files of | |
| 159 | + | // .github/workflows at the tip of the default branch, and the files that are | |
| 160 | + | // disabled even if the default branch has no such file (a repository that | |
| 161 | + | // is empty, or a file not pushed yet). Needs the same access as reading the | |
| 162 | + | // repository. A repository without a default branch lists only its disabled | |
| 163 | + | // files. Only servers with the feature "ci.workflows" implement it | |
| 164 | + | // (unimplemented otherwise). Fails with not_found if the repository does not | |
| 165 | + | // exist or cannot be read. | |
| 166 | + | ListWorkflows(context.Context, *connect.Request[ermite.ListWorkflowsRequest]) (*connect.Response[ermite.ListWorkflowsResponse], error) | |
| 167 | + | // DisableWorkflow disables a workflow file of a repository, like `gh | |
| 168 | + | // workflow disable`: no event starts a run of it any more (push, | |
| 169 | + | // pull_request, pull_request_target, schedule, workflow_run, create, | |
| 170 | + | // delete, and workflow_dispatch). Runs already started are not affected. | |
| 171 | + | // The state belongs to the repository and to the file path, not to a | |
| 172 | + | // commit or a branch, and the file does not have to exist: it can be set on | |
| 173 | + | // an empty repository, before the first push. Disabling a disabled workflow | |
| 174 | + | // succeeds and changes nothing. Needs the repo:admin scope on the | |
| 175 | + | // repository, and a token that is not limited to branch prefixes. Only | |
| 176 | + | // servers with the feature "ci.workflows" implement it (unimplemented | |
| 177 | + | // otherwise). Fails with not_found if the repository does not exist or the | |
| 178 | + | // caller cannot read it, permission_denied without the scope, and | |
| 179 | + | // invalid_argument if the workflow is not a file name or a path of | |
| 180 | + | // .github/workflows ending in .yml or .yaml. | |
| 181 | + | DisableWorkflow(context.Context, *connect.Request[ermite.DisableWorkflowRequest]) (*connect.Response[ermite.DisableWorkflowResponse], error) | |
| 182 | + | // EnableWorkflow undoes DisableWorkflow, like `gh workflow enable`. Enabling | |
| 183 | + | // a workflow that is not disabled succeeds and changes nothing. Same access | |
| 184 | + | // and errors as DisableWorkflow. Events that happened while the workflow was | |
| 185 | + | // disabled are not replayed. | |
| 186 | + | EnableWorkflow(context.Context, *connect.Request[ermite.EnableWorkflowRequest]) (*connect.Response[ermite.EnableWorkflowResponse], error) | |
| 148 | 187 | // ListChecks lists the checks of a commit: for each job of each run on that | |
| 149 | 188 | // sha, the one of the latest attempt, ordered by creation time then name. | |
| 150 | 189 | // A commit without a run has no check: the list is empty, which is not an | |
| @@ -227,6 +266,24 @@ func NewRunServiceClient(httpClient connect.HTTPClient, baseURL string, opts ... | |||
| 227 | 266 | connect.WithSchema(runServiceMethods.ByName("DispatchWorkflow")), | |
| 228 | 267 | connect.WithClientOptions(opts...), | |
| 229 | 268 | ), | |
| 269 | + | listWorkflows: connect.NewClient[ermite.ListWorkflowsRequest, ermite.ListWorkflowsResponse]( | |
| 270 | + | httpClient, | |
| 271 | + | baseURL+RunServiceListWorkflowsProcedure, | |
| 272 | + | connect.WithSchema(runServiceMethods.ByName("ListWorkflows")), | |
| 273 | + | connect.WithClientOptions(opts...), | |
| 274 | + | ), | |
| 275 | + | disableWorkflow: connect.NewClient[ermite.DisableWorkflowRequest, ermite.DisableWorkflowResponse]( | |
| 276 | + | httpClient, | |
| 277 | + | baseURL+RunServiceDisableWorkflowProcedure, | |
| 278 | + | connect.WithSchema(runServiceMethods.ByName("DisableWorkflow")), | |
| 279 | + | connect.WithClientOptions(opts...), | |
| 280 | + | ), | |
| 281 | + | enableWorkflow: connect.NewClient[ermite.EnableWorkflowRequest, ermite.EnableWorkflowResponse]( | |
| 282 | + | httpClient, | |
| 283 | + | baseURL+RunServiceEnableWorkflowProcedure, | |
| 284 | + | connect.WithSchema(runServiceMethods.ByName("EnableWorkflow")), | |
| 285 | + | connect.WithClientOptions(opts...), | |
| 286 | + | ), | |
| 230 | 287 | listChecks: connect.NewClient[ermite.ListChecksRequest, ermite.ListChecksResponse]( | |
| 231 | 288 | httpClient, | |
| 232 | 289 | baseURL+RunServiceListChecksProcedure, | |
| @@ -259,6 +316,9 @@ type runServiceClient struct { | |||
| 259 | 316 | cancelRun *connect.Client[ermite.CancelRunRequest, ermite.CancelRunResponse] | |
| 260 | 317 | rerunRun *connect.Client[ermite.RerunRunRequest, ermite.RerunRunResponse] | |
| 261 | 318 | dispatchWorkflow *connect.Client[ermite.DispatchWorkflowRequest, ermite.DispatchWorkflowResponse] | |
| 319 | + | listWorkflows *connect.Client[ermite.ListWorkflowsRequest, ermite.ListWorkflowsResponse] | |
| 320 | + | disableWorkflow *connect.Client[ermite.DisableWorkflowRequest, ermite.DisableWorkflowResponse] | |
| 321 | + | enableWorkflow *connect.Client[ermite.EnableWorkflowRequest, ermite.EnableWorkflowResponse] | |
| 262 | 322 | listChecks *connect.Client[ermite.ListChecksRequest, ermite.ListChecksResponse] | |
| 263 | 323 | listRunners *connect.Client[ermite.ListRunnersRequest, ermite.ListRunnersResponse] | |
| 264 | 324 | removeRunner *connect.Client[ermite.RemoveRunnerRequest, ermite.RemoveRunnerResponse] | |
| @@ -309,6 +369,21 @@ func (c *runServiceClient) DispatchWorkflow(ctx context.Context, req *connect.Re | |||
| 309 | 369 | return c.dispatchWorkflow.CallUnary(ctx, req) | |
| 310 | 370 | } | |
| 311 | 371 | ||
| 372 | + | // ListWorkflows calls ermite.RunService.ListWorkflows. | |
| 373 | + | func (c *runServiceClient) ListWorkflows(ctx context.Context, req *connect.Request[ermite.ListWorkflowsRequest]) (*connect.Response[ermite.ListWorkflowsResponse], error) { | |
| 374 | + | return c.listWorkflows.CallUnary(ctx, req) | |
| 375 | + | } | |
| 376 | + | ||
| 377 | + | // DisableWorkflow calls ermite.RunService.DisableWorkflow. | |
| 378 | + | func (c *runServiceClient) DisableWorkflow(ctx context.Context, req *connect.Request[ermite.DisableWorkflowRequest]) (*connect.Response[ermite.DisableWorkflowResponse], error) { | |
| 379 | + | return c.disableWorkflow.CallUnary(ctx, req) | |
| 380 | + | } | |
| 381 | + | ||
| 382 | + | // EnableWorkflow calls ermite.RunService.EnableWorkflow. | |
| 383 | + | func (c *runServiceClient) EnableWorkflow(ctx context.Context, req *connect.Request[ermite.EnableWorkflowRequest]) (*connect.Response[ermite.EnableWorkflowResponse], error) { | |
| 384 | + | return c.enableWorkflow.CallUnary(ctx, req) | |
| 385 | + | } | |
| 386 | + | ||
| 312 | 387 | // ListChecks calls ermite.RunService.ListChecks. | |
| 313 | 388 | func (c *runServiceClient) ListChecks(ctx context.Context, req *connect.Request[ermite.ListChecksRequest]) (*connect.Response[ermite.ListChecksResponse], error) { | |
| 314 | 389 | return c.listChecks.CallUnary(ctx, req) | |
| @@ -405,6 +480,36 @@ type RunServiceHandler interface { | |||
| 405 | 480 | // missing while required without default, or has a value of the wrong type | |
| 406 | 481 | // or outside its choices, or if the workflow file is not valid. | |
| 407 | 482 | DispatchWorkflow(context.Context, *connect.Request[ermite.DispatchWorkflowRequest]) (*connect.Response[ermite.DispatchWorkflowResponse], error) | |
| 483 | + | // ListWorkflows lists the workflow files of a repository with their state, | |
| 484 | + | // like `gh workflow list --all`, ordered by path: the files of | |
| 485 | + | // .github/workflows at the tip of the default branch, and the files that are | |
| 486 | + | // disabled even if the default branch has no such file (a repository that | |
| 487 | + | // is empty, or a file not pushed yet). Needs the same access as reading the | |
| 488 | + | // repository. A repository without a default branch lists only its disabled | |
| 489 | + | // files. Only servers with the feature "ci.workflows" implement it | |
| 490 | + | // (unimplemented otherwise). Fails with not_found if the repository does not | |
| 491 | + | // exist or cannot be read. | |
| 492 | + | ListWorkflows(context.Context, *connect.Request[ermite.ListWorkflowsRequest]) (*connect.Response[ermite.ListWorkflowsResponse], error) | |
| 493 | + | // DisableWorkflow disables a workflow file of a repository, like `gh | |
| 494 | + | // workflow disable`: no event starts a run of it any more (push, | |
| 495 | + | // pull_request, pull_request_target, schedule, workflow_run, create, | |
| 496 | + | // delete, and workflow_dispatch). Runs already started are not affected. | |
| 497 | + | // The state belongs to the repository and to the file path, not to a | |
| 498 | + | // commit or a branch, and the file does not have to exist: it can be set on | |
| 499 | + | // an empty repository, before the first push. Disabling a disabled workflow | |
| 500 | + | // succeeds and changes nothing. Needs the repo:admin scope on the | |
| 501 | + | // repository, and a token that is not limited to branch prefixes. Only | |
| 502 | + | // servers with the feature "ci.workflows" implement it (unimplemented | |
| 503 | + | // otherwise). Fails with not_found if the repository does not exist or the | |
| 504 | + | // caller cannot read it, permission_denied without the scope, and | |
| 505 | + | // invalid_argument if the workflow is not a file name or a path of | |
| 506 | + | // .github/workflows ending in .yml or .yaml. | |
| 507 | + | DisableWorkflow(context.Context, *connect.Request[ermite.DisableWorkflowRequest]) (*connect.Response[ermite.DisableWorkflowResponse], error) | |
| 508 | + | // EnableWorkflow undoes DisableWorkflow, like `gh workflow enable`. Enabling | |
| 509 | + | // a workflow that is not disabled succeeds and changes nothing. Same access | |
| 510 | + | // and errors as DisableWorkflow. Events that happened while the workflow was | |
| 511 | + | // disabled are not replayed. | |
| 512 | + | EnableWorkflow(context.Context, *connect.Request[ermite.EnableWorkflowRequest]) (*connect.Response[ermite.EnableWorkflowResponse], error) | |
| 408 | 513 | // ListChecks lists the checks of a commit: for each job of each run on that | |
| 409 | 514 | // sha, the one of the latest attempt, ordered by creation time then name. | |
| 410 | 515 | // A commit without a run has no check: the list is empty, which is not an | |
| @@ -483,6 +588,24 @@ func NewRunServiceHandler(svc RunServiceHandler, opts ...connect.HandlerOption) | |||
| 483 | 588 | connect.WithSchema(runServiceMethods.ByName("DispatchWorkflow")), | |
| 484 | 589 | connect.WithHandlerOptions(opts...), | |
| 485 | 590 | ) | |
| 591 | + | runServiceListWorkflowsHandler := connect.NewUnaryHandler( | |
| 592 | + | RunServiceListWorkflowsProcedure, | |
| 593 | + | svc.ListWorkflows, | |
| 594 | + | connect.WithSchema(runServiceMethods.ByName("ListWorkflows")), | |
| 595 | + | connect.WithHandlerOptions(opts...), | |
| 596 | + | ) | |
| 597 | + | runServiceDisableWorkflowHandler := connect.NewUnaryHandler( | |
| 598 | + | RunServiceDisableWorkflowProcedure, | |
| 599 | + | svc.DisableWorkflow, | |
| 600 | + | connect.WithSchema(runServiceMethods.ByName("DisableWorkflow")), | |
| 601 | + | connect.WithHandlerOptions(opts...), | |
| 602 | + | ) | |
| 603 | + | runServiceEnableWorkflowHandler := connect.NewUnaryHandler( | |
| 604 | + | RunServiceEnableWorkflowProcedure, | |
| 605 | + | svc.EnableWorkflow, | |
| 606 | + | connect.WithSchema(runServiceMethods.ByName("EnableWorkflow")), | |
| 607 | + | connect.WithHandlerOptions(opts...), | |
| 608 | + | ) | |
| 486 | 609 | runServiceListChecksHandler := connect.NewUnaryHandler( | |
| 487 | 610 | RunServiceListChecksProcedure, | |
| 488 | 611 | svc.ListChecks, | |
| @@ -521,6 +644,12 @@ func NewRunServiceHandler(svc RunServiceHandler, opts ...connect.HandlerOption) | |||
| 521 | 644 | runServiceRerunRunHandler.ServeHTTP(w, r) | |
| 522 | 645 | case RunServiceDispatchWorkflowProcedure: | |
| 523 | 646 | runServiceDispatchWorkflowHandler.ServeHTTP(w, r) | |
| 647 | + | case RunServiceListWorkflowsProcedure: | |
| 648 | + | runServiceListWorkflowsHandler.ServeHTTP(w, r) | |
| 649 | + | case RunServiceDisableWorkflowProcedure: | |
| 650 | + | runServiceDisableWorkflowHandler.ServeHTTP(w, r) | |
| 651 | + | case RunServiceEnableWorkflowProcedure: | |
| 652 | + | runServiceEnableWorkflowHandler.ServeHTTP(w, r) | |
| 524 | 653 | case RunServiceListChecksProcedure: | |
| 525 | 654 | runServiceListChecksHandler.ServeHTTP(w, r) | |
| 526 | 655 | case RunServiceListRunnersProcedure: | |
| @@ -572,6 +701,18 @@ func (UnimplementedRunServiceHandler) DispatchWorkflow(context.Context, *connect | |||
| 572 | 701 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.DispatchWorkflow is not implemented")) | |
| 573 | 702 | } | |
| 574 | 703 | ||
| 704 | + | func (UnimplementedRunServiceHandler) ListWorkflows(context.Context, *connect.Request[ermite.ListWorkflowsRequest]) (*connect.Response[ermite.ListWorkflowsResponse], error) { | |
| 705 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.ListWorkflows is not implemented")) | |
| 706 | + | } | |
| 707 | + | ||
| 708 | + | func (UnimplementedRunServiceHandler) DisableWorkflow(context.Context, *connect.Request[ermite.DisableWorkflowRequest]) (*connect.Response[ermite.DisableWorkflowResponse], error) { | |
| 709 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.DisableWorkflow is not implemented")) | |
| 710 | + | } | |
| 711 | + | ||
| 712 | + | func (UnimplementedRunServiceHandler) EnableWorkflow(context.Context, *connect.Request[ermite.EnableWorkflowRequest]) (*connect.Response[ermite.EnableWorkflowResponse], error) { | |
| 713 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.EnableWorkflow is not implemented")) | |
| 714 | + | } | |
| 715 | + | ||
| 575 | 716 | func (UnimplementedRunServiceHandler) ListChecks(context.Context, *connect.Request[ermite.ListChecksRequest]) (*connect.Response[ermite.ListChecksResponse], error) { | |
| 576 | 717 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.ListChecks is not implemented")) | |
| 577 | 718 | } | |
| @@ -56,6 +56,15 @@ const ( | |||||
| 56 | // RunServiceDispatchWorkflowProcedure is the fully-qualified name of the RunService's | 56 | // RunServiceDispatchWorkflowProcedure is the fully-qualified name of the RunService's | ||
| 57 | // DispatchWorkflow RPC. | 57 | // DispatchWorkflow RPC. | ||
| 58 | RunServiceDispatchWorkflowProcedure = "/ermite.RunService/DispatchWorkflow" | 58 | RunServiceDispatchWorkflowProcedure = "/ermite.RunService/DispatchWorkflow" | ||
| 59 | + | // RunServiceListWorkflowsProcedure is the fully-qualified name of the RunService's ListWorkflows | |||
| 60 | + | // RPC. | |||
| 61 | + | RunServiceListWorkflowsProcedure = "/ermite.RunService/ListWorkflows" | |||
| 62 | + | // RunServiceDisableWorkflowProcedure is the fully-qualified name of the RunService's | |||
| 63 | + | // DisableWorkflow RPC. | |||
| 64 | + | RunServiceDisableWorkflowProcedure = "/ermite.RunService/DisableWorkflow" | |||
| 65 | + | // RunServiceEnableWorkflowProcedure is the fully-qualified name of the RunService's EnableWorkflow | |||
| 66 | + | // RPC. | |||
| 67 | + | RunServiceEnableWorkflowProcedure = "/ermite.RunService/EnableWorkflow" | |||
| 59 | // RunServiceListChecksProcedure is the fully-qualified name of the RunService's ListChecks RPC. | 68 | // RunServiceListChecksProcedure is the fully-qualified name of the RunService's ListChecks RPC. | ||
| 60 | RunServiceListChecksProcedure = "/ermite.RunService/ListChecks" | 69 | RunServiceListChecksProcedure = "/ermite.RunService/ListChecks" | ||
| 61 | // RunServiceListRunnersProcedure is the fully-qualified name of the RunService's ListRunners RPC. | 70 | // RunServiceListRunnersProcedure is the fully-qualified name of the RunService's ListRunners RPC. | ||
| @@ -145,6 +154,36 @@ type RunServiceClient interface { | |||||
| 145 | // missing while required without default, or has a value of the wrong type | 154 | // missing while required without default, or has a value of the wrong type | ||
| 146 | // or outside its choices, or if the workflow file is not valid. | 155 | // or outside its choices, or if the workflow file is not valid. | ||
| 147 | DispatchWorkflow(context.Context, *connect.Request[ermite.DispatchWorkflowRequest]) (*connect.Response[ermite.DispatchWorkflowResponse], error) | 156 | DispatchWorkflow(context.Context, *connect.Request[ermite.DispatchWorkflowRequest]) (*connect.Response[ermite.DispatchWorkflowResponse], error) | ||
| 157 | + | // ListWorkflows lists the workflow files of a repository with their state, | |||
| 158 | + | // like `gh workflow list --all`, ordered by path: the files of | |||
| 159 | + | // .github/workflows at the tip of the default branch, and the files that are | |||
| 160 | + | // disabled even if the default branch has no such file (a repository that | |||
| 161 | + | // is empty, or a file not pushed yet). Needs the same access as reading the | |||
| 162 | + | // repository. A repository without a default branch lists only its disabled | |||
| 163 | + | // files. Only servers with the feature "ci.workflows" implement it | |||
| 164 | + | // (unimplemented otherwise). Fails with not_found if the repository does not | |||
| 165 | + | // exist or cannot be read. | |||
| 166 | + | ListWorkflows(context.Context, *connect.Request[ermite.ListWorkflowsRequest]) (*connect.Response[ermite.ListWorkflowsResponse], error) | |||
| 167 | + | // DisableWorkflow disables a workflow file of a repository, like `gh | |||
| 168 | + | // workflow disable`: no event starts a run of it any more (push, | |||
| 169 | + | // pull_request, pull_request_target, schedule, workflow_run, create, | |||
| 170 | + | // delete, and workflow_dispatch). Runs already started are not affected. | |||
| 171 | + | // The state belongs to the repository and to the file path, not to a | |||
| 172 | + | // commit or a branch, and the file does not have to exist: it can be set on | |||
| 173 | + | // an empty repository, before the first push. Disabling a disabled workflow | |||
| 174 | + | // succeeds and changes nothing. Needs the repo:admin scope on the | |||
| 175 | + | // repository, and a token that is not limited to branch prefixes. Only | |||
| 176 | + | // servers with the feature "ci.workflows" implement it (unimplemented | |||
| 177 | + | // otherwise). Fails with not_found if the repository does not exist or the | |||
| 178 | + | // caller cannot read it, permission_denied without the scope, and | |||
| 179 | + | // invalid_argument if the workflow is not a file name or a path of | |||
| 180 | + | // .github/workflows ending in .yml or .yaml. | |||
| 181 | + | DisableWorkflow(context.Context, *connect.Request[ermite.DisableWorkflowRequest]) (*connect.Response[ermite.DisableWorkflowResponse], error) | |||
| 182 | + | // EnableWorkflow undoes DisableWorkflow, like `gh workflow enable`. Enabling | |||
| 183 | + | // a workflow that is not disabled succeeds and changes nothing. Same access | |||
| 184 | + | // and errors as DisableWorkflow. Events that happened while the workflow was | |||
| 185 | + | // disabled are not replayed. | |||
| 186 | + | EnableWorkflow(context.Context, *connect.Request[ermite.EnableWorkflowRequest]) (*connect.Response[ermite.EnableWorkflowResponse], error) | |||
| 148 | // ListChecks lists the checks of a commit: for each job of each run on that | 187 | // ListChecks lists the checks of a commit: for each job of each run on that | ||
| 149 | // sha, the one of the latest attempt, ordered by creation time then name. | 188 | // sha, the one of the latest attempt, ordered by creation time then name. | ||
| 150 | // A commit without a run has no check: the list is empty, which is not an | 189 | // A commit without a run has no check: the list is empty, which is not an | ||
| @@ -227,6 +266,24 @@ func NewRunServiceClient(httpClient connect.HTTPClient, baseURL string, opts ... | |||||
| 227 | connect.WithSchema(runServiceMethods.ByName("DispatchWorkflow")), | 266 | connect.WithSchema(runServiceMethods.ByName("DispatchWorkflow")), | ||
| 228 | connect.WithClientOptions(opts...), | 267 | connect.WithClientOptions(opts...), | ||
| 229 | ), | 268 | ), | ||
| 269 | + | listWorkflows: connect.NewClient[ermite.ListWorkflowsRequest, ermite.ListWorkflowsResponse]( | |||
| 270 | + | httpClient, | |||
| 271 | + | baseURL+RunServiceListWorkflowsProcedure, | |||
| 272 | + | connect.WithSchema(runServiceMethods.ByName("ListWorkflows")), | |||
| 273 | + | connect.WithClientOptions(opts...), | |||
| 274 | + | ), | |||
| 275 | + | disableWorkflow: connect.NewClient[ermite.DisableWorkflowRequest, ermite.DisableWorkflowResponse]( | |||
| 276 | + | httpClient, | |||
| 277 | + | baseURL+RunServiceDisableWorkflowProcedure, | |||
| 278 | + | connect.WithSchema(runServiceMethods.ByName("DisableWorkflow")), | |||
| 279 | + | connect.WithClientOptions(opts...), | |||
| 280 | + | ), | |||
| 281 | + | enableWorkflow: connect.NewClient[ermite.EnableWorkflowRequest, ermite.EnableWorkflowResponse]( | |||
| 282 | + | httpClient, | |||
| 283 | + | baseURL+RunServiceEnableWorkflowProcedure, | |||
| 284 | + | connect.WithSchema(runServiceMethods.ByName("EnableWorkflow")), | |||
| 285 | + | connect.WithClientOptions(opts...), | |||
| 286 | + | ), | |||
| 230 | listChecks: connect.NewClient[ermite.ListChecksRequest, ermite.ListChecksResponse]( | 287 | listChecks: connect.NewClient[ermite.ListChecksRequest, ermite.ListChecksResponse]( | ||
| 231 | httpClient, | 288 | httpClient, | ||
| 232 | baseURL+RunServiceListChecksProcedure, | 289 | baseURL+RunServiceListChecksProcedure, | ||
| @@ -259,6 +316,9 @@ type runServiceClient struct { | |||||
| 259 | cancelRun *connect.Client[ermite.CancelRunRequest, ermite.CancelRunResponse] | 316 | cancelRun *connect.Client[ermite.CancelRunRequest, ermite.CancelRunResponse] | ||
| 260 | rerunRun *connect.Client[ermite.RerunRunRequest, ermite.RerunRunResponse] | 317 | rerunRun *connect.Client[ermite.RerunRunRequest, ermite.RerunRunResponse] | ||
| 261 | dispatchWorkflow *connect.Client[ermite.DispatchWorkflowRequest, ermite.DispatchWorkflowResponse] | 318 | dispatchWorkflow *connect.Client[ermite.DispatchWorkflowRequest, ermite.DispatchWorkflowResponse] | ||
| 319 | + | listWorkflows *connect.Client[ermite.ListWorkflowsRequest, ermite.ListWorkflowsResponse] | |||
| 320 | + | disableWorkflow *connect.Client[ermite.DisableWorkflowRequest, ermite.DisableWorkflowResponse] | |||
| 321 | + | enableWorkflow *connect.Client[ermite.EnableWorkflowRequest, ermite.EnableWorkflowResponse] | |||
| 262 | listChecks *connect.Client[ermite.ListChecksRequest, ermite.ListChecksResponse] | 322 | listChecks *connect.Client[ermite.ListChecksRequest, ermite.ListChecksResponse] | ||
| 263 | listRunners *connect.Client[ermite.ListRunnersRequest, ermite.ListRunnersResponse] | 323 | listRunners *connect.Client[ermite.ListRunnersRequest, ermite.ListRunnersResponse] | ||
| 264 | removeRunner *connect.Client[ermite.RemoveRunnerRequest, ermite.RemoveRunnerResponse] | 324 | removeRunner *connect.Client[ermite.RemoveRunnerRequest, ermite.RemoveRunnerResponse] | ||
| @@ -309,6 +369,21 @@ func (c *runServiceClient) DispatchWorkflow(ctx context.Context, req *connect.Re | |||||
| 309 | return c.dispatchWorkflow.CallUnary(ctx, req) | 369 | return c.dispatchWorkflow.CallUnary(ctx, req) | ||
| 310 | } | 370 | } | ||
| 311 | 371 | ||||
| 372 | + | // ListWorkflows calls ermite.RunService.ListWorkflows. | |||
| 373 | + | func (c *runServiceClient) ListWorkflows(ctx context.Context, req *connect.Request[ermite.ListWorkflowsRequest]) (*connect.Response[ermite.ListWorkflowsResponse], error) { | |||
| 374 | + | return c.listWorkflows.CallUnary(ctx, req) | |||
| 375 | + | } | |||
| 376 | + | ||||
| 377 | + | // DisableWorkflow calls ermite.RunService.DisableWorkflow. | |||
| 378 | + | func (c *runServiceClient) DisableWorkflow(ctx context.Context, req *connect.Request[ermite.DisableWorkflowRequest]) (*connect.Response[ermite.DisableWorkflowResponse], error) { | |||
| 379 | + | return c.disableWorkflow.CallUnary(ctx, req) | |||
| 380 | + | } | |||
| 381 | + | ||||
| 382 | + | // EnableWorkflow calls ermite.RunService.EnableWorkflow. | |||
| 383 | + | func (c *runServiceClient) EnableWorkflow(ctx context.Context, req *connect.Request[ermite.EnableWorkflowRequest]) (*connect.Response[ermite.EnableWorkflowResponse], error) { | |||
| 384 | + | return c.enableWorkflow.CallUnary(ctx, req) | |||
| 385 | + | } | |||
| 386 | + | ||||
| 312 | // ListChecks calls ermite.RunService.ListChecks. | 387 | // ListChecks calls ermite.RunService.ListChecks. | ||
| 313 | func (c *runServiceClient) ListChecks(ctx context.Context, req *connect.Request[ermite.ListChecksRequest]) (*connect.Response[ermite.ListChecksResponse], error) { | 388 | func (c *runServiceClient) ListChecks(ctx context.Context, req *connect.Request[ermite.ListChecksRequest]) (*connect.Response[ermite.ListChecksResponse], error) { | ||
| 314 | return c.listChecks.CallUnary(ctx, req) | 389 | return c.listChecks.CallUnary(ctx, req) | ||
| @@ -405,6 +480,36 @@ type RunServiceHandler interface { | |||||
| 405 | // missing while required without default, or has a value of the wrong type | 480 | // missing while required without default, or has a value of the wrong type | ||
| 406 | // or outside its choices, or if the workflow file is not valid. | 481 | // or outside its choices, or if the workflow file is not valid. | ||
| 407 | DispatchWorkflow(context.Context, *connect.Request[ermite.DispatchWorkflowRequest]) (*connect.Response[ermite.DispatchWorkflowResponse], error) | 482 | DispatchWorkflow(context.Context, *connect.Request[ermite.DispatchWorkflowRequest]) (*connect.Response[ermite.DispatchWorkflowResponse], error) | ||
| 483 | + | // ListWorkflows lists the workflow files of a repository with their state, | |||
| 484 | + | // like `gh workflow list --all`, ordered by path: the files of | |||
| 485 | + | // .github/workflows at the tip of the default branch, and the files that are | |||
| 486 | + | // disabled even if the default branch has no such file (a repository that | |||
| 487 | + | // is empty, or a file not pushed yet). Needs the same access as reading the | |||
| 488 | + | // repository. A repository without a default branch lists only its disabled | |||
| 489 | + | // files. Only servers with the feature "ci.workflows" implement it | |||
| 490 | + | // (unimplemented otherwise). Fails with not_found if the repository does not | |||
| 491 | + | // exist or cannot be read. | |||
| 492 | + | ListWorkflows(context.Context, *connect.Request[ermite.ListWorkflowsRequest]) (*connect.Response[ermite.ListWorkflowsResponse], error) | |||
| 493 | + | // DisableWorkflow disables a workflow file of a repository, like `gh | |||
| 494 | + | // workflow disable`: no event starts a run of it any more (push, | |||
| 495 | + | // pull_request, pull_request_target, schedule, workflow_run, create, | |||
| 496 | + | // delete, and workflow_dispatch). Runs already started are not affected. | |||
| 497 | + | // The state belongs to the repository and to the file path, not to a | |||
| 498 | + | // commit or a branch, and the file does not have to exist: it can be set on | |||
| 499 | + | // an empty repository, before the first push. Disabling a disabled workflow | |||
| 500 | + | // succeeds and changes nothing. Needs the repo:admin scope on the | |||
| 501 | + | // repository, and a token that is not limited to branch prefixes. Only | |||
| 502 | + | // servers with the feature "ci.workflows" implement it (unimplemented | |||
| 503 | + | // otherwise). Fails with not_found if the repository does not exist or the | |||
| 504 | + | // caller cannot read it, permission_denied without the scope, and | |||
| 505 | + | // invalid_argument if the workflow is not a file name or a path of | |||
| 506 | + | // .github/workflows ending in .yml or .yaml. | |||
| 507 | + | DisableWorkflow(context.Context, *connect.Request[ermite.DisableWorkflowRequest]) (*connect.Response[ermite.DisableWorkflowResponse], error) | |||
| 508 | + | // EnableWorkflow undoes DisableWorkflow, like `gh workflow enable`. Enabling | |||
| 509 | + | // a workflow that is not disabled succeeds and changes nothing. Same access | |||
| 510 | + | // and errors as DisableWorkflow. Events that happened while the workflow was | |||
| 511 | + | // disabled are not replayed. | |||
| 512 | + | EnableWorkflow(context.Context, *connect.Request[ermite.EnableWorkflowRequest]) (*connect.Response[ermite.EnableWorkflowResponse], error) | |||
| 408 | // ListChecks lists the checks of a commit: for each job of each run on that | 513 | // ListChecks lists the checks of a commit: for each job of each run on that | ||
| 409 | // sha, the one of the latest attempt, ordered by creation time then name. | 514 | // sha, the one of the latest attempt, ordered by creation time then name. | ||
| 410 | // A commit without a run has no check: the list is empty, which is not an | 515 | // A commit without a run has no check: the list is empty, which is not an | ||
| @@ -483,6 +588,24 @@ func NewRunServiceHandler(svc RunServiceHandler, opts ...connect.HandlerOption) | |||||
| 483 | connect.WithSchema(runServiceMethods.ByName("DispatchWorkflow")), | 588 | connect.WithSchema(runServiceMethods.ByName("DispatchWorkflow")), | ||
| 484 | connect.WithHandlerOptions(opts...), | 589 | connect.WithHandlerOptions(opts...), | ||
| 485 | ) | 590 | ) | ||
| 591 | + | runServiceListWorkflowsHandler := connect.NewUnaryHandler( | |||
| 592 | + | RunServiceListWorkflowsProcedure, | |||
| 593 | + | svc.ListWorkflows, | |||
| 594 | + | connect.WithSchema(runServiceMethods.ByName("ListWorkflows")), | |||
| 595 | + | connect.WithHandlerOptions(opts...), | |||
| 596 | + | ) | |||
| 597 | + | runServiceDisableWorkflowHandler := connect.NewUnaryHandler( | |||
| 598 | + | RunServiceDisableWorkflowProcedure, | |||
| 599 | + | svc.DisableWorkflow, | |||
| 600 | + | connect.WithSchema(runServiceMethods.ByName("DisableWorkflow")), | |||
| 601 | + | connect.WithHandlerOptions(opts...), | |||
| 602 | + | ) | |||
| 603 | + | runServiceEnableWorkflowHandler := connect.NewUnaryHandler( | |||
| 604 | + | RunServiceEnableWorkflowProcedure, | |||
| 605 | + | svc.EnableWorkflow, | |||
| 606 | + | connect.WithSchema(runServiceMethods.ByName("EnableWorkflow")), | |||
| 607 | + | connect.WithHandlerOptions(opts...), | |||
| 608 | + | ) | |||
| 486 | runServiceListChecksHandler := connect.NewUnaryHandler( | 609 | runServiceListChecksHandler := connect.NewUnaryHandler( | ||
| 487 | RunServiceListChecksProcedure, | 610 | RunServiceListChecksProcedure, | ||
| 488 | svc.ListChecks, | 611 | svc.ListChecks, | ||
| @@ -521,6 +644,12 @@ func NewRunServiceHandler(svc RunServiceHandler, opts ...connect.HandlerOption) | |||||
| 521 | runServiceRerunRunHandler.ServeHTTP(w, r) | 644 | runServiceRerunRunHandler.ServeHTTP(w, r) | ||
| 522 | case RunServiceDispatchWorkflowProcedure: | 645 | case RunServiceDispatchWorkflowProcedure: | ||
| 523 | runServiceDispatchWorkflowHandler.ServeHTTP(w, r) | 646 | runServiceDispatchWorkflowHandler.ServeHTTP(w, r) | ||
| 647 | + | case RunServiceListWorkflowsProcedure: | |||
| 648 | + | runServiceListWorkflowsHandler.ServeHTTP(w, r) | |||
| 649 | + | case RunServiceDisableWorkflowProcedure: | |||
| 650 | + | runServiceDisableWorkflowHandler.ServeHTTP(w, r) | |||
| 651 | + | case RunServiceEnableWorkflowProcedure: | |||
| 652 | + | runServiceEnableWorkflowHandler.ServeHTTP(w, r) | |||
| 524 | case RunServiceListChecksProcedure: | 653 | case RunServiceListChecksProcedure: | ||
| 525 | runServiceListChecksHandler.ServeHTTP(w, r) | 654 | runServiceListChecksHandler.ServeHTTP(w, r) | ||
| 526 | case RunServiceListRunnersProcedure: | 655 | case RunServiceListRunnersProcedure: | ||
| @@ -572,6 +701,18 @@ func (UnimplementedRunServiceHandler) DispatchWorkflow(context.Context, *connect | |||||
| 572 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.DispatchWorkflow is not implemented")) | 701 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.DispatchWorkflow is not implemented")) | ||
| 573 | } | 702 | } | ||
| 574 | 703 | ||||
| 704 | + | func (UnimplementedRunServiceHandler) ListWorkflows(context.Context, *connect.Request[ermite.ListWorkflowsRequest]) (*connect.Response[ermite.ListWorkflowsResponse], error) { | |||
| 705 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.ListWorkflows is not implemented")) | |||
| 706 | + | } | |||
| 707 | + | ||||
| 708 | + | func (UnimplementedRunServiceHandler) DisableWorkflow(context.Context, *connect.Request[ermite.DisableWorkflowRequest]) (*connect.Response[ermite.DisableWorkflowResponse], error) { | |||
| 709 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.DisableWorkflow is not implemented")) | |||
| 710 | + | } | |||
| 711 | + | ||||
| 712 | + | func (UnimplementedRunServiceHandler) EnableWorkflow(context.Context, *connect.Request[ermite.EnableWorkflowRequest]) (*connect.Response[ermite.EnableWorkflowResponse], error) { | |||
| 713 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.EnableWorkflow is not implemented")) | |||
| 714 | + | } | |||
| 715 | + | ||||
| 575 | func (UnimplementedRunServiceHandler) ListChecks(context.Context, *connect.Request[ermite.ListChecksRequest]) (*connect.Response[ermite.ListChecksResponse], error) { | 716 | func (UnimplementedRunServiceHandler) ListChecks(context.Context, *connect.Request[ermite.ListChecksRequest]) (*connect.Response[ermite.ListChecksResponse], error) { | ||
| 576 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.ListChecks is not implemented")) | 717 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RunService.ListChecks is not implemented")) | ||
| 577 | } | 718 | } | ||
M gen/ermite/run.pb.go +571 -113 generated file: collapsed
M gen_test.go +13 -0
| @@ -66,6 +66,9 @@ func TestProcedures(t *testing.T) { | |||
| 66 | 66 | {ermiteconnect.RunServiceCancelRunProcedure, "/ermite.RunService/CancelRun"}, | |
| 67 | 67 | {ermiteconnect.RunServiceRerunRunProcedure, "/ermite.RunService/RerunRun"}, | |
| 68 | 68 | {ermiteconnect.RunServiceDispatchWorkflowProcedure, "/ermite.RunService/DispatchWorkflow"}, | |
| 69 | + | {ermiteconnect.RunServiceListWorkflowsProcedure, "/ermite.RunService/ListWorkflows"}, | |
| 70 | + | {ermiteconnect.RunServiceDisableWorkflowProcedure, "/ermite.RunService/DisableWorkflow"}, | |
| 71 | + | {ermiteconnect.RunServiceEnableWorkflowProcedure, "/ermite.RunService/EnableWorkflow"}, | |
| 69 | 72 | {ermiteconnect.RunServiceListChecksProcedure, "/ermite.RunService/ListChecks"}, | |
| 70 | 73 | {ermiteconnect.RunServiceListRunnersProcedure, "/ermite.RunService/ListRunners"}, | |
| 71 | 74 | {ermiteconnect.RunServiceRemoveRunnerProcedure, "/ermite.RunService/RemoveRunner"}, | |
| @@ -489,6 +492,13 @@ func TestCI3FieldNumbers(t *testing.T) { | |||
| 489 | 492 | {&ermite.RunJob{}, map[string]int{"concurrency_group": 20}}, | |
| 490 | 493 | {&ermite.DispatchWorkflowRequest{}, map[string]int{"repo": 1, "workflow_path": 2, "ref": 3, "inputs": 4}}, | |
| 491 | 494 | {&ermite.DispatchWorkflowResponse{}, map[string]int{"run": 1}}, | |
| 495 | + | {&ermite.Workflow{}, map[string]int{"path": 1, "name": 2, "state": 3}}, | |
| 496 | + | {&ermite.ListWorkflowsRequest{}, map[string]int{"repo": 1}}, | |
| 497 | + | {&ermite.ListWorkflowsResponse{}, map[string]int{"workflows": 1}}, | |
| 498 | + | {&ermite.DisableWorkflowRequest{}, map[string]int{"repo": 1, "workflow": 2}}, | |
| 499 | + | {&ermite.DisableWorkflowResponse{}, map[string]int{"workflow": 1}}, | |
| 500 | + | {&ermite.EnableWorkflowRequest{}, map[string]int{"repo": 1, "workflow": 2}}, | |
| 501 | + | {&ermite.EnableWorkflowResponse{}, map[string]int{"workflow": 1}}, | |
| 492 | 502 | {&ermite.SettingsScope{}, map[string]int{"instance": 1, "repo": 2, "environment": 3}}, | |
| 493 | 503 | {&ermite.EnvironmentRef{}, map[string]int{"repo": 1, "name": 2}}, | |
| 494 | 504 | {&ermite.Secret{}, map[string]int{"name": 1, "updated_at": 2}}, | |
| @@ -527,6 +537,9 @@ func TestCI3RoundTrip(t *testing.T) { | |||
| 527 | 537 | &ermite.RunJob{Id: "j1", Status: ermite.CiStatus_CI_STATUS_PENDING, ConcurrencyGroup: "deploy"}, | |
| 528 | 538 | &ermite.DispatchWorkflowRequest{Repo: "ermite", WorkflowPath: "ci.yml", Ref: "main", Inputs: map[string]string{"debug": "true"}}, | |
| 529 | 539 | &ermite.DispatchWorkflowResponse{Run: &ermite.Run{Id: 9, Event: "workflow_dispatch"}}, | |
| 540 | + | &ermite.ListWorkflowsResponse{Workflows: []*ermite.Workflow{{Path: ".github/workflows/release.yml", Name: "Release", State: ermite.WorkflowState_WORKFLOW_STATE_DISABLED}}}, | |
| 541 | + | &ermite.DisableWorkflowRequest{Repo: "ermite", Workflow: "release.yml"}, | |
| 542 | + | &ermite.EnableWorkflowResponse{Workflow: &ermite.Workflow{Path: ".github/workflows/release.yml", State: ermite.WorkflowState_WORKFLOW_STATE_ENABLED}}, | |
| 530 | 543 | &ermite.SetSecretRequest{Scope: &ermite.SettingsScope{Where: &ermite.SettingsScope_Instance{Instance: true}}, Name: "A", Value: "v"}, | |
| 531 | 544 | &ermite.SetSecretRequest{Scope: &ermite.SettingsScope{Where: &ermite.SettingsScope_Repo{Repo: "ermite"}}, Name: "A", Value: "v"}, | |
| 532 | 545 | &ermite.SetVariableRequest{ | |
| @@ -66,6 +66,9 @@ func TestProcedures(t *testing.T) { | |||||
| 66 | {ermiteconnect.RunServiceCancelRunProcedure, "/ermite.RunService/CancelRun"}, | 66 | {ermiteconnect.RunServiceCancelRunProcedure, "/ermite.RunService/CancelRun"}, | ||
| 67 | {ermiteconnect.RunServiceRerunRunProcedure, "/ermite.RunService/RerunRun"}, | 67 | {ermiteconnect.RunServiceRerunRunProcedure, "/ermite.RunService/RerunRun"}, | ||
| 68 | {ermiteconnect.RunServiceDispatchWorkflowProcedure, "/ermite.RunService/DispatchWorkflow"}, | 68 | {ermiteconnect.RunServiceDispatchWorkflowProcedure, "/ermite.RunService/DispatchWorkflow"}, | ||
| 69 | + | {ermiteconnect.RunServiceListWorkflowsProcedure, "/ermite.RunService/ListWorkflows"}, | |||
| 70 | + | {ermiteconnect.RunServiceDisableWorkflowProcedure, "/ermite.RunService/DisableWorkflow"}, | |||
| 71 | + | {ermiteconnect.RunServiceEnableWorkflowProcedure, "/ermite.RunService/EnableWorkflow"}, | |||
| 69 | {ermiteconnect.RunServiceListChecksProcedure, "/ermite.RunService/ListChecks"}, | 72 | {ermiteconnect.RunServiceListChecksProcedure, "/ermite.RunService/ListChecks"}, | ||
| 70 | {ermiteconnect.RunServiceListRunnersProcedure, "/ermite.RunService/ListRunners"}, | 73 | {ermiteconnect.RunServiceListRunnersProcedure, "/ermite.RunService/ListRunners"}, | ||
| 71 | {ermiteconnect.RunServiceRemoveRunnerProcedure, "/ermite.RunService/RemoveRunner"}, | 74 | {ermiteconnect.RunServiceRemoveRunnerProcedure, "/ermite.RunService/RemoveRunner"}, | ||
| @@ -489,6 +492,13 @@ func TestCI3FieldNumbers(t *testing.T) { | |||||
| 489 | {&ermite.RunJob{}, map[string]int{"concurrency_group": 20}}, | 492 | {&ermite.RunJob{}, map[string]int{"concurrency_group": 20}}, | ||
| 490 | {&ermite.DispatchWorkflowRequest{}, map[string]int{"repo": 1, "workflow_path": 2, "ref": 3, "inputs": 4}}, | 493 | {&ermite.DispatchWorkflowRequest{}, map[string]int{"repo": 1, "workflow_path": 2, "ref": 3, "inputs": 4}}, | ||
| 491 | {&ermite.DispatchWorkflowResponse{}, map[string]int{"run": 1}}, | 494 | {&ermite.DispatchWorkflowResponse{}, map[string]int{"run": 1}}, | ||
| 495 | + | {&ermite.Workflow{}, map[string]int{"path": 1, "name": 2, "state": 3}}, | |||
| 496 | + | {&ermite.ListWorkflowsRequest{}, map[string]int{"repo": 1}}, | |||
| 497 | + | {&ermite.ListWorkflowsResponse{}, map[string]int{"workflows": 1}}, | |||
| 498 | + | {&ermite.DisableWorkflowRequest{}, map[string]int{"repo": 1, "workflow": 2}}, | |||
| 499 | + | {&ermite.DisableWorkflowResponse{}, map[string]int{"workflow": 1}}, | |||
| 500 | + | {&ermite.EnableWorkflowRequest{}, map[string]int{"repo": 1, "workflow": 2}}, | |||
| 501 | + | {&ermite.EnableWorkflowResponse{}, map[string]int{"workflow": 1}}, | |||
| 492 | {&ermite.SettingsScope{}, map[string]int{"instance": 1, "repo": 2, "environment": 3}}, | 502 | {&ermite.SettingsScope{}, map[string]int{"instance": 1, "repo": 2, "environment": 3}}, | ||
| 493 | {&ermite.EnvironmentRef{}, map[string]int{"repo": 1, "name": 2}}, | 503 | {&ermite.EnvironmentRef{}, map[string]int{"repo": 1, "name": 2}}, | ||
| 494 | {&ermite.Secret{}, map[string]int{"name": 1, "updated_at": 2}}, | 504 | {&ermite.Secret{}, map[string]int{"name": 1, "updated_at": 2}}, | ||
| @@ -527,6 +537,9 @@ func TestCI3RoundTrip(t *testing.T) { | |||||
| 527 | &ermite.RunJob{Id: "j1", Status: ermite.CiStatus_CI_STATUS_PENDING, ConcurrencyGroup: "deploy"}, | 537 | &ermite.RunJob{Id: "j1", Status: ermite.CiStatus_CI_STATUS_PENDING, ConcurrencyGroup: "deploy"}, | ||
| 528 | &ermite.DispatchWorkflowRequest{Repo: "ermite", WorkflowPath: "ci.yml", Ref: "main", Inputs: map[string]string{"debug": "true"}}, | 538 | &ermite.DispatchWorkflowRequest{Repo: "ermite", WorkflowPath: "ci.yml", Ref: "main", Inputs: map[string]string{"debug": "true"}}, | ||
| 529 | &ermite.DispatchWorkflowResponse{Run: &ermite.Run{Id: 9, Event: "workflow_dispatch"}}, | 539 | &ermite.DispatchWorkflowResponse{Run: &ermite.Run{Id: 9, Event: "workflow_dispatch"}}, | ||
| 540 | + | &ermite.ListWorkflowsResponse{Workflows: []*ermite.Workflow{{Path: ".github/workflows/release.yml", Name: "Release", State: ermite.WorkflowState_WORKFLOW_STATE_DISABLED}}}, | |||
| 541 | + | &ermite.DisableWorkflowRequest{Repo: "ermite", Workflow: "release.yml"}, | |||
| 542 | + | &ermite.EnableWorkflowResponse{Workflow: &ermite.Workflow{Path: ".github/workflows/release.yml", State: ermite.WorkflowState_WORKFLOW_STATE_ENABLED}}, | |||
| 530 | &ermite.SetSecretRequest{Scope: &ermite.SettingsScope{Where: &ermite.SettingsScope_Instance{Instance: true}}, Name: "A", Value: "v"}, | 543 | &ermite.SetSecretRequest{Scope: &ermite.SettingsScope{Where: &ermite.SettingsScope_Instance{Instance: true}}, Name: "A", Value: "v"}, | ||
| 531 | &ermite.SetSecretRequest{Scope: &ermite.SettingsScope{Where: &ermite.SettingsScope_Repo{Repo: "ermite"}}, Name: "A", Value: "v"}, | 544 | &ermite.SetSecretRequest{Scope: &ermite.SettingsScope{Where: &ermite.SettingsScope_Repo{Repo: "ermite"}}, Name: "A", Value: "v"}, | ||
| 532 | &ermite.SetVariableRequest{ | 545 | &ermite.SetVariableRequest{ | ||
M proto/ermite/run.proto +96 -2
| @@ -282,14 +282,16 @@ message Runner { | |||
| 282 | 282 | // request (other fields must be unchanged). | |
| 283 | 283 | // | |
| 284 | 284 | // Servers that implement the runs side (every RPC but ListRunners, | |
| 285 | - | // RemoveRunner, ListAnnotations, GetStepSummaries and DispatchWorkflow) list | |
| 285 | + | // RemoveRunner, ListAnnotations, GetStepSummaries, DispatchWorkflow and the | |
| 286 | + | // workflow state RPCs) list | |
| 286 | 287 | // "ci.runs" in | |
| 287 | 288 | // GetVersionResponse.features; those that | |
| 288 | 289 | // implement the runners side list "ci.runners", as RunnerService does. A server | |
| 289 | 290 | // that honours the filters of ListRunsRequest also lists "ci.runs.filters". | |
| 290 | 291 | // ListAnnotations needs "ci.annotations" and GetStepSummaries "ci.summaries", | |
| 291 | 292 | // which are also the features of the runner side of each. DispatchWorkflow | |
| 292 | - | // needs "ci.dispatch". RunJob.concurrency_group and CI_STATUS_PENDING belong | |
| 293 | + | // needs "ci.dispatch". ListWorkflows, DisableWorkflow and EnableWorkflow need | |
| 294 | + | // "ci.workflows". RunJob.concurrency_group and CI_STATUS_PENDING belong | |
| 293 | 295 | // to "ci.concurrency", Run.trusted to "ci.secrets". | |
| 294 | 296 | service RunService { | |
| 295 | 297 | // ListRuns lists the runs of a repository, highest id (newest) first. Each | |
| @@ -371,6 +373,36 @@ service RunService { | |||
| 371 | 373 | // missing while required without default, or has a value of the wrong type | |
| 372 | 374 | // or outside its choices, or if the workflow file is not valid. | |
| 373 | 375 | rpc DispatchWorkflow(DispatchWorkflowRequest) returns (DispatchWorkflowResponse); | |
| 376 | + | // ListWorkflows lists the workflow files of a repository with their state, | |
| 377 | + | // like `gh workflow list --all`, ordered by path: the files of | |
| 378 | + | // .github/workflows at the tip of the default branch, and the files that are | |
| 379 | + | // disabled even if the default branch has no such file (a repository that | |
| 380 | + | // is empty, or a file not pushed yet). Needs the same access as reading the | |
| 381 | + | // repository. A repository without a default branch lists only its disabled | |
| 382 | + | // files. Only servers with the feature "ci.workflows" implement it | |
| 383 | + | // (unimplemented otherwise). Fails with not_found if the repository does not | |
| 384 | + | // exist or cannot be read. | |
| 385 | + | rpc ListWorkflows(ListWorkflowsRequest) returns (ListWorkflowsResponse); | |
| 386 | + | // DisableWorkflow disables a workflow file of a repository, like `gh | |
| 387 | + | // workflow disable`: no event starts a run of it any more (push, | |
| 388 | + | // pull_request, pull_request_target, schedule, workflow_run, create, | |
| 389 | + | // delete, and workflow_dispatch). Runs already started are not affected. | |
| 390 | + | // The state belongs to the repository and to the file path, not to a | |
| 391 | + | // commit or a branch, and the file does not have to exist: it can be set on | |
| 392 | + | // an empty repository, before the first push. Disabling a disabled workflow | |
| 393 | + | // succeeds and changes nothing. Needs the repo:admin scope on the | |
| 394 | + | // repository, and a token that is not limited to branch prefixes. Only | |
| 395 | + | // servers with the feature "ci.workflows" implement it (unimplemented | |
| 396 | + | // otherwise). Fails with not_found if the repository does not exist or the | |
| 397 | + | // caller cannot read it, permission_denied without the scope, and | |
| 398 | + | // invalid_argument if the workflow is not a file name or a path of | |
| 399 | + | // .github/workflows ending in .yml or .yaml. | |
| 400 | + | rpc DisableWorkflow(DisableWorkflowRequest) returns (DisableWorkflowResponse); | |
| 401 | + | // EnableWorkflow undoes DisableWorkflow, like `gh workflow enable`. Enabling | |
| 402 | + | // a workflow that is not disabled succeeds and changes nothing. Same access | |
| 403 | + | // and errors as DisableWorkflow. Events that happened while the workflow was | |
| 404 | + | // disabled are not replayed. | |
| 405 | + | rpc EnableWorkflow(EnableWorkflowRequest) returns (EnableWorkflowResponse); | |
| 374 | 406 | // ListChecks lists the checks of a commit: for each job of each run on that | |
| 375 | 407 | // sha, the one of the latest attempt, ordered by creation time then name. | |
| 376 | 408 | // A commit without a run has no check: the list is empty, which is not an | |
| @@ -661,3 +693,65 @@ message RemoveRunnerRequest { | |||
| 661 | 693 | ||
| 662 | 694 | // RemoveRunnerResponse is empty. | |
| 663 | 695 | message RemoveRunnerResponse {} | |
| 696 | + | ||
| 697 | + | // WorkflowState tells whether a workflow file starts runs. | |
| 698 | + | enum WorkflowState { | |
| 699 | + | WORKFLOW_STATE_UNSPECIFIED = 0; | |
| 700 | + | // The workflow starts runs for the events it declares. | |
| 701 | + | WORKFLOW_STATE_ENABLED = 1; | |
| 702 | + | // The workflow starts no run for any event (DisableWorkflow). | |
| 703 | + | WORKFLOW_STATE_DISABLED = 2; | |
| 704 | + | } | |
| 705 | + | ||
| 706 | + | // Workflow is a workflow file of a repository and its state. | |
| 707 | + | message Workflow { | |
| 708 | + | // Path is the path of the file, always ".github/workflows/<file>". | |
| 709 | + | string path = 1; | |
| 710 | + | // Name is the "name:" of the workflow at the tip of the default branch, the | |
| 711 | + | // path when the file has none. Empty when the default branch has no such file | |
| 712 | + | // or it cannot be read as a workflow. | |
| 713 | + | string name = 2; | |
| 714 | + | // State is ENABLED or DISABLED. | |
| 715 | + | WorkflowState state = 3; | |
| 716 | + | } | |
| 717 | + | ||
| 718 | + | // ListWorkflowsRequest names the repository. | |
| 719 | + | message ListWorkflowsRequest { | |
| 720 | + | // Repo is the repository name. | |
| 721 | + | string repo = 1; | |
| 722 | + | } | |
| 723 | + | ||
| 724 | + | // ListWorkflowsResponse holds the workflows of a repository, ordered by path. | |
| 725 | + | message ListWorkflowsResponse { | |
| 726 | + | repeated Workflow workflows = 1; | |
| 727 | + | } | |
| 728 | + | ||
| 729 | + | // DisableWorkflowRequest names the workflow to disable. | |
| 730 | + | message DisableWorkflowRequest { | |
| 731 | + | // Repo is the repository name. | |
| 732 | + | string repo = 1; | |
| 733 | + | // Workflow is the path of the file (".github/workflows/release.yml") or its | |
| 734 | + | // file name ("release.yml"). The file does not have to exist. | |
| 735 | + | string workflow = 2; | |
| 736 | + | } | |
| 737 | + | ||
| 738 | + | // DisableWorkflowResponse holds the workflow after the change. | |
| 739 | + | message DisableWorkflowResponse { | |
| 740 | + | // Workflow has the normalized path and state DISABLED. | |
| 741 | + | Workflow workflow = 1; | |
| 742 | + | } | |
| 743 | + | ||
| 744 | + | // EnableWorkflowRequest names the workflow to enable. | |
| 745 | + | message EnableWorkflowRequest { | |
| 746 | + | // Repo is the repository name. | |
| 747 | + | string repo = 1; | |
| 748 | + | // Workflow is the path of the file or its file name, as in | |
| 749 | + | // DisableWorkflowRequest. | |
| 750 | + | string workflow = 2; | |
| 751 | + | } | |
| 752 | + | ||
| 753 | + | // EnableWorkflowResponse holds the workflow after the change. | |
| 754 | + | message EnableWorkflowResponse { | |
| 755 | + | // Workflow has the normalized path and state ENABLED. | |
| 756 | + | Workflow workflow = 1; | |
| 757 | + | } | |
| @@ -282,14 +282,16 @@ message Runner { | |||||
| 282 | // request (other fields must be unchanged). | 282 | // request (other fields must be unchanged). | ||
| 283 | // | 283 | // | ||
| 284 | // Servers that implement the runs side (every RPC but ListRunners, | 284 | // Servers that implement the runs side (every RPC but ListRunners, | ||
| 285 | - | // RemoveRunner, ListAnnotations, GetStepSummaries and DispatchWorkflow) list | 285 | + | // RemoveRunner, ListAnnotations, GetStepSummaries, DispatchWorkflow and the |
| 286 | + | // workflow state RPCs) list | |||
| 286 | // "ci.runs" in | 287 | // "ci.runs" in | ||
| 287 | // GetVersionResponse.features; those that | 288 | // GetVersionResponse.features; those that | ||
| 288 | // implement the runners side list "ci.runners", as RunnerService does. A server | 289 | // implement the runners side list "ci.runners", as RunnerService does. A server | ||
| 289 | // that honours the filters of ListRunsRequest also lists "ci.runs.filters". | 290 | // that honours the filters of ListRunsRequest also lists "ci.runs.filters". | ||
| 290 | // ListAnnotations needs "ci.annotations" and GetStepSummaries "ci.summaries", | 291 | // ListAnnotations needs "ci.annotations" and GetStepSummaries "ci.summaries", | ||
| 291 | // which are also the features of the runner side of each. DispatchWorkflow | 292 | // which are also the features of the runner side of each. DispatchWorkflow | ||
| 292 | - | // needs "ci.dispatch". RunJob.concurrency_group and CI_STATUS_PENDING belong | 293 | + | // needs "ci.dispatch". ListWorkflows, DisableWorkflow and EnableWorkflow need |
| 294 | + | // "ci.workflows". RunJob.concurrency_group and CI_STATUS_PENDING belong | |||
| 293 | // to "ci.concurrency", Run.trusted to "ci.secrets". | 295 | // to "ci.concurrency", Run.trusted to "ci.secrets". | ||
| 294 | service RunService { | 296 | service RunService { | ||
| 295 | // ListRuns lists the runs of a repository, highest id (newest) first. Each | 297 | // ListRuns lists the runs of a repository, highest id (newest) first. Each | ||
| @@ -371,6 +373,36 @@ service RunService { | |||||
| 371 | // missing while required without default, or has a value of the wrong type | 373 | // missing while required without default, or has a value of the wrong type | ||
| 372 | // or outside its choices, or if the workflow file is not valid. | 374 | // or outside its choices, or if the workflow file is not valid. | ||
| 373 | rpc DispatchWorkflow(DispatchWorkflowRequest) returns (DispatchWorkflowResponse); | 375 | rpc DispatchWorkflow(DispatchWorkflowRequest) returns (DispatchWorkflowResponse); | ||
| 376 | + | // ListWorkflows lists the workflow files of a repository with their state, | |||
| 377 | + | // like `gh workflow list --all`, ordered by path: the files of | |||
| 378 | + | // .github/workflows at the tip of the default branch, and the files that are | |||
| 379 | + | // disabled even if the default branch has no such file (a repository that | |||
| 380 | + | // is empty, or a file not pushed yet). Needs the same access as reading the | |||
| 381 | + | // repository. A repository without a default branch lists only its disabled | |||
| 382 | + | // files. Only servers with the feature "ci.workflows" implement it | |||
| 383 | + | // (unimplemented otherwise). Fails with not_found if the repository does not | |||
| 384 | + | // exist or cannot be read. | |||
| 385 | + | rpc ListWorkflows(ListWorkflowsRequest) returns (ListWorkflowsResponse); | |||
| 386 | + | // DisableWorkflow disables a workflow file of a repository, like `gh | |||
| 387 | + | // workflow disable`: no event starts a run of it any more (push, | |||
| 388 | + | // pull_request, pull_request_target, schedule, workflow_run, create, | |||
| 389 | + | // delete, and workflow_dispatch). Runs already started are not affected. | |||
| 390 | + | // The state belongs to the repository and to the file path, not to a | |||
| 391 | + | // commit or a branch, and the file does not have to exist: it can be set on | |||
| 392 | + | // an empty repository, before the first push. Disabling a disabled workflow | |||
| 393 | + | // succeeds and changes nothing. Needs the repo:admin scope on the | |||
| 394 | + | // repository, and a token that is not limited to branch prefixes. Only | |||
| 395 | + | // servers with the feature "ci.workflows" implement it (unimplemented | |||
| 396 | + | // otherwise). Fails with not_found if the repository does not exist or the | |||
| 397 | + | // caller cannot read it, permission_denied without the scope, and | |||
| 398 | + | // invalid_argument if the workflow is not a file name or a path of | |||
| 399 | + | // .github/workflows ending in .yml or .yaml. | |||
| 400 | + | rpc DisableWorkflow(DisableWorkflowRequest) returns (DisableWorkflowResponse); | |||
| 401 | + | // EnableWorkflow undoes DisableWorkflow, like `gh workflow enable`. Enabling | |||
| 402 | + | // a workflow that is not disabled succeeds and changes nothing. Same access | |||
| 403 | + | // and errors as DisableWorkflow. Events that happened while the workflow was | |||
| 404 | + | // disabled are not replayed. | |||
| 405 | + | rpc EnableWorkflow(EnableWorkflowRequest) returns (EnableWorkflowResponse); | |||
| 374 | // ListChecks lists the checks of a commit: for each job of each run on that | 406 | // ListChecks lists the checks of a commit: for each job of each run on that | ||
| 375 | // sha, the one of the latest attempt, ordered by creation time then name. | 407 | // sha, the one of the latest attempt, ordered by creation time then name. | ||
| 376 | // A commit without a run has no check: the list is empty, which is not an | 408 | // A commit without a run has no check: the list is empty, which is not an | ||
| @@ -661,3 +693,65 @@ message RemoveRunnerRequest { | |||||
| 661 | 693 | ||||
| 662 | // RemoveRunnerResponse is empty. | 694 | // RemoveRunnerResponse is empty. | ||
| 663 | message RemoveRunnerResponse {} | 695 | message RemoveRunnerResponse {} | ||
| 696 | + | ||||
| 697 | + | // WorkflowState tells whether a workflow file starts runs. | |||
| 698 | + | enum WorkflowState { | |||
| 699 | + | WORKFLOW_STATE_UNSPECIFIED = 0; | |||
| 700 | + | // The workflow starts runs for the events it declares. | |||
| 701 | + | WORKFLOW_STATE_ENABLED = 1; | |||
| 702 | + | // The workflow starts no run for any event (DisableWorkflow). | |||
| 703 | + | WORKFLOW_STATE_DISABLED = 2; | |||
| 704 | + | } | |||
| 705 | + | ||||
| 706 | + | // Workflow is a workflow file of a repository and its state. | |||
| 707 | + | message Workflow { | |||
| 708 | + | // Path is the path of the file, always ".github/workflows/<file>". | |||
| 709 | + | string path = 1; | |||
| 710 | + | // Name is the "name:" of the workflow at the tip of the default branch, the | |||
| 711 | + | // path when the file has none. Empty when the default branch has no such file | |||
| 712 | + | // or it cannot be read as a workflow. | |||
| 713 | + | string name = 2; | |||
| 714 | + | // State is ENABLED or DISABLED. | |||
| 715 | + | WorkflowState state = 3; | |||
| 716 | + | } | |||
| 717 | + | ||||
| 718 | + | // ListWorkflowsRequest names the repository. | |||
| 719 | + | message ListWorkflowsRequest { | |||
| 720 | + | // Repo is the repository name. | |||
| 721 | + | string repo = 1; | |||
| 722 | + | } | |||
| 723 | + | ||||
| 724 | + | // ListWorkflowsResponse holds the workflows of a repository, ordered by path. | |||
| 725 | + | message ListWorkflowsResponse { | |||
| 726 | + | repeated Workflow workflows = 1; | |||
| 727 | + | } | |||
| 728 | + | ||||
| 729 | + | // DisableWorkflowRequest names the workflow to disable. | |||
| 730 | + | message DisableWorkflowRequest { | |||
| 731 | + | // Repo is the repository name. | |||
| 732 | + | string repo = 1; | |||
| 733 | + | // Workflow is the path of the file (".github/workflows/release.yml") or its | |||
| 734 | + | // file name ("release.yml"). The file does not have to exist. | |||
| 735 | + | string workflow = 2; | |||
| 736 | + | } | |||
| 737 | + | ||||
| 738 | + | // DisableWorkflowResponse holds the workflow after the change. | |||
| 739 | + | message DisableWorkflowResponse { | |||
| 740 | + | // Workflow has the normalized path and state DISABLED. | |||
| 741 | + | Workflow workflow = 1; | |||
| 742 | + | } | |||
| 743 | + | ||||
| 744 | + | // EnableWorkflowRequest names the workflow to enable. | |||
| 745 | + | message EnableWorkflowRequest { | |||
| 746 | + | // Repo is the repository name. | |||
| 747 | + | string repo = 1; | |||
| 748 | + | // Workflow is the path of the file or its file name, as in | |||
| 749 | + | // DisableWorkflowRequest. | |||
| 750 | + | string workflow = 2; | |||
| 751 | + | } | |||
| 752 | + | ||||
| 753 | + | // EnableWorkflowResponse holds the workflow after the change. | |||
| 754 | + | message EnableWorkflowResponse { | |||
| 755 | + | // Workflow has the normalized path and state ENABLED. | |||
| 756 | + | Workflow workflow = 1; | |||
| 757 | + | } | |||