feat(actions): add force-cancel workflow run API (#38756)

Add `POST /repos/{owner}/{repo}/actions/runs/{run}/force-cancel`, the
counterpart of [GitHub's force-cancel endpoint](https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2026-03-10#force-cancel-a-workflow-run).

It cancels a run like `POST .../cancel`, but bypasses the graceful
cancelling handshake with the runner and stops running tasks
immediately.

Permissions and responses match the `/cancel` endpoint.

References:

- https://github.blog/changelog/2023-09-21-github-actions-force-cancel-workflows/
- https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2026-03-10#force-cancel-a-workflow-run
- https://github.com/orgs/community/discussions/123240
This commit is contained in:
Zettat123
2026-08-07 08:38:14 -06:00
committed by GitHub
parent 2657756cac
commit 9fc5d20006
10 changed files with 396 additions and 19 deletions
+1
View File
@@ -1363,6 +1363,7 @@ func Routes() *web.Router {
m.Post("/rerun", reqToken(), reqRepoWriter(unit.TypeActions), repo.RerunWorkflowRun)
m.Post("/rerun-failed-jobs", reqToken(), reqRepoWriter(unit.TypeActions), repo.RerunFailedWorkflowRun)
m.Post("/cancel", reqToken(), reqRepoWriter(unit.TypeActions), repo.CancelWorkflowRun)
m.Post("/force-cancel", reqToken(), reqRepoWriter(unit.TypeActions), repo.ForceCancelWorkflowRun)
m.Post("/approve", reqToken(), reqRepoWriter(unit.TypeActions), repo.ApproveWorkflowRun)
m.Group("/jobs", func() {
m.Get("", repo.ListWorkflowRunJobs)
+51 -1
View File
@@ -88,6 +88,51 @@ func CancelWorkflowRun(ctx *context.APIContext) {
// "409":
// "$ref": "#/responses/conflict"
cancelWorkflowRun(ctx, false)
}
func ForceCancelWorkflowRun(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/actions/runs/{run}/force-cancel repository forceCancelWorkflowRun
// ---
// summary: Force-cancel a workflow run
// description: |
// Cancels a workflow run without waiting for its runners to acknowledge the cancellation.
// The jobs are marked cancelled at once and anything a runner reports for them afterwards is discarded.
// Only use this endpoint when the workflow run does not respond to `POST /repos/{owner}/{repo}/actions/runs/{run}/cancel`.
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repository
// type: string
// required: true
// - name: run
// in: path
// description: run ID
// type: integer
// required: true
// responses:
// "200":
// "$ref": "#/responses/WorkflowRun"
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "409":
// "$ref": "#/responses/conflict"
cancelWorkflowRun(ctx, true)
}
func cancelWorkflowRun(ctx *context.APIContext, force bool) {
run, jobs := getCurrentRepoActionRunJobsByID(ctx)
if ctx.Written() {
return
@@ -99,7 +144,12 @@ func CancelWorkflowRun(ctx *context.APIContext) {
return
}
run, err := actions_service.CancelRun(ctx, run, jobs)
var err error
if force {
run, err = actions_service.ForceCancelRun(ctx, run, jobs)
} else {
run, err = actions_service.CancelRun(ctx, run, jobs)
}
if err != nil {
ctx.APIErrorAuto(err)
return