feat(actions): Add Actions API endpoints for workflow run management and logs (#35382)

Implements the missing REST API endpoints for Actions workflow run
management:

1. `POST /actions/runs/{run}/cancel` cancels a run and its jobs, `409`
when it already finished
1. `POST /actions/runs/{run}/approve` approves a run awaiting approval,
idempotent, `409` when it never awaited one
1. `GET /actions/runs/{run}/logs` downloads the latest attempt's job
logs as a zip archive

`ActionWorkflowRun` gains `created_at`, `updated_at` and the `jobs_url`,
`logs_url`, `artifacts_url`, `cancel_url` and `rerun_url` fields, and
now always emits `conclusion` and `head_branch`.

Cancellation is shared with the web handler in `services/actions`.

Fixes https://github.com/go-gitea/gitea/issues/35176
Fixes https://github.com/go-gitea/gitea/issues/36554

---------

Co-authored-by: Claude Sonnet 4.6 <claude-sonnet-4-6@anthropic.com>
Co-authored-by: OpenCode Agent <opencode@rossgolder.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Ross Golder
2026-08-02 11:22:07 +07:00
committed by GitHub
parent c5b6e044d7
commit a65f422b89
27 changed files with 1230 additions and 177 deletions
+191
View File
@@ -2237,6 +2237,16 @@
"actor": {
"$ref": "#/components/schemas/User"
},
"artifacts_url": {
"format": "uri",
"type": "string",
"x-go-name": "ArtifactsURL"
},
"cancel_url": {
"format": "uri",
"type": "string",
"x-go-name": "CancelURL"
},
"completed_at": {
"format": "date-time",
"type": "string",
@@ -2246,6 +2256,11 @@
"type": "string",
"x-go-name": "Conclusion"
},
"created_at": {
"format": "date-time",
"type": "string",
"x-go-name": "CreatedAt"
},
"display_title": {
"type": "string",
"x-go-name": "DisplayTitle"
@@ -2275,6 +2290,16 @@
"type": "integer",
"x-go-name": "ID"
},
"jobs_url": {
"format": "uri",
"type": "string",
"x-go-name": "JobsURL"
},
"logs_url": {
"format": "uri",
"type": "string",
"x-go-name": "LogsURL"
},
"path": {
"type": "string",
"x-go-name": "Path"
@@ -2300,6 +2325,11 @@
"type": "integer",
"x-go-name": "RepositoryID"
},
"rerun_url": {
"format": "uri",
"type": "string",
"x-go-name": "RerunURL"
},
"run_attempt": {
"description": "RunAttempt is 1-based for runs created after ActionRunAttempt was introduced.\nA value of 0 is a legacy-only sentinel for runs created before attempts existed\nand indicates no corresponding /attempts/{n} resource is available.",
"format": "int64",
@@ -2323,6 +2353,11 @@
"trigger_actor": {
"$ref": "#/components/schemas/User"
},
"updated_at": {
"format": "date-time",
"type": "string",
"x-go-name": "UpdatedAt"
},
"url": {
"format": "uri",
"type": "string",
@@ -16455,6 +16490,61 @@
]
}
},
"/repos/{owner}/{repo}/actions/runs/{run}/approve": {
"post": {
"operationId": "approveWorkflowRun",
"parameters": [
{
"description": "owner of the repo",
"in": "path",
"name": "owner",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "name of the repository",
"in": "path",
"name": "repo",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "run ID",
"in": "path",
"name": "run",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"$ref": "#/components/responses/WorkflowRun"
},
"400": {
"$ref": "#/components/responses/error"
},
"403": {
"$ref": "#/components/responses/forbidden"
},
"404": {
"$ref": "#/components/responses/notFound"
},
"409": {
"$ref": "#/components/responses/conflict"
}
},
"summary": "Approve a workflow run that requires approval",
"tags": [
"repository"
]
}
},
"/repos/{owner}/{repo}/actions/runs/{run}/artifacts": {
"get": {
"operationId": "getArtifactsOfRun",
@@ -16652,6 +16742,61 @@
]
}
},
"/repos/{owner}/{repo}/actions/runs/{run}/cancel": {
"post": {
"operationId": "cancelWorkflowRun",
"parameters": [
{
"description": "owner of the repo",
"in": "path",
"name": "owner",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "name of the repository",
"in": "path",
"name": "repo",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "run ID",
"in": "path",
"name": "run",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"$ref": "#/components/responses/WorkflowRun"
},
"400": {
"$ref": "#/components/responses/error"
},
"403": {
"$ref": "#/components/responses/forbidden"
},
"404": {
"$ref": "#/components/responses/notFound"
},
"409": {
"$ref": "#/components/responses/conflict"
}
},
"summary": "Cancel a workflow run and its jobs",
"tags": [
"repository"
]
}
},
"/repos/{owner}/{repo}/actions/runs/{run}/jobs": {
"get": {
"operationId": "listWorkflowRunJobs",
@@ -16811,6 +16956,52 @@
]
}
},
"/repos/{owner}/{repo}/actions/runs/{run}/logs": {
"get": {
"operationId": "getWorkflowRunLogs",
"parameters": [
{
"description": "owner of the repo",
"in": "path",
"name": "owner",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "name of the repository",
"in": "path",
"name": "repo",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "run ID",
"in": "path",
"name": "run",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "Logs archive"
},
"404": {
"$ref": "#/components/responses/notFound"
}
},
"summary": "Download workflow run logs as archive",
"tags": [
"repository"
]
}
},
"/repos/{owner}/{repo}/actions/runs/{run}/rerun": {
"post": {
"operationId": "rerunWorkflowRun",