From d271eb767d008ad590d246fe4092cbc508ecb746 Mon Sep 17 00:00:00 2001 From: Noel Date: Mon, 29 Jun 2026 16:44:30 +0200 Subject: [PATCH] feat(actions): add workflow-run cancel command Signed-off-by: Noel --- cmd/actions/runs.go | 1 + cmd/actions/runs/cancel.go | 64 +++++++++++++++++++++++++++++++++ cmd/actions/runs/cancel_test.go | 48 +++++++++++++++++++++++++ cmd/actions/runs/delete.go | 8 ++--- docs/CLI.md | 16 +++++++-- 5 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 cmd/actions/runs/cancel.go create mode 100644 cmd/actions/runs/cancel_test.go diff --git a/cmd/actions/runs.go b/cmd/actions/runs.go index 0b34d690..c7f6a9af 100644 --- a/cmd/actions/runs.go +++ b/cmd/actions/runs.go @@ -21,6 +21,7 @@ var CmdActionsRuns = cli.Command{ Commands: []*cli.Command{ &runs.CmdRunsList, &runs.CmdRunsView, + &runs.CmdRunsCancel, &runs.CmdRunsDelete, &runs.CmdRunsLogs, }, diff --git a/cmd/actions/runs/cancel.go b/cmd/actions/runs/cancel.go new file mode 100644 index 00000000..091afe6e --- /dev/null +++ b/cmd/actions/runs/cancel.go @@ -0,0 +1,64 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package runs + +import ( + stdctx "context" + "fmt" + "strconv" + + gitea "gitea.dev/sdk" + "gitea.dev/tea/cmd/flags" + "gitea.dev/tea/modules/context" + + "github.com/urfave/cli/v3" +) + +type actionsRunCancelService interface { + CancelRepoRun(ctx stdctx.Context, owner, repo string, runID int64) (*gitea.ActionsWorkflowRun, *gitea.Response, error) +} + +// CmdRunsCancel represents a sub command to cancel a workflow run. +var CmdRunsCancel = cli.Command{ + Name: "cancel", + Usage: "Cancel a workflow run", + Description: "Request cancellation of a queued or running workflow run", + ArgsUsage: "", + Action: runRunsCancel, + Flags: flags.AllDefaultFlags, +} + +func runRunsCancel(ctx stdctx.Context, cmd *cli.Command) error { + if cmd.Args().Len() == 0 { + return fmt.Errorf("run ID is required") + } + + c, err := context.InitCommand(cmd) + if err != nil { + return err + } + if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil { + return err + } + + runIDText := cmd.Args().First() + runID, err := strconv.ParseInt(runIDText, 10, 64) + if err != nil { + return fmt.Errorf("invalid run ID: %s", runIDText) + } + + if err := cancelRun(ctx, c.Login.Client().Actions, c.Owner, c.Repo, runID); err != nil { + return err + } + + fmt.Printf("Cancellation requested for run %d\n", runID) + return nil +} + +func cancelRun(ctx stdctx.Context, client actionsRunCancelService, owner, repo string, runID int64) error { + if _, _, err := client.CancelRepoRun(ctx, owner, repo, runID); err != nil { + return fmt.Errorf("failed to cancel run %d: %w", runID, err) + } + return nil +} diff --git a/cmd/actions/runs/cancel_test.go b/cmd/actions/runs/cancel_test.go new file mode 100644 index 00000000..70732706 --- /dev/null +++ b/cmd/actions/runs/cancel_test.go @@ -0,0 +1,48 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package runs + +import ( + stdctx "context" + "errors" + "testing" + + gitea "gitea.dev/sdk" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type fakeActionsRunCancelService struct { + owner string + repo string + runID int64 + err error +} + +func (f *fakeActionsRunCancelService) CancelRepoRun(_ stdctx.Context, owner, repo string, runID int64) (*gitea.ActionsWorkflowRun, *gitea.Response, error) { + f.owner = owner + f.repo = repo + f.runID = runID + return &gitea.ActionsWorkflowRun{ID: runID}, nil, f.err +} + +func TestCancelRun(t *testing.T) { + t.Parallel() + + client := &fakeActionsRunCancelService{} + require.NoError(t, cancelRun(t.Context(), client, "owner", "repo", 12)) + assert.Equal(t, "owner", client.owner) + assert.Equal(t, "repo", client.repo) + assert.EqualValues(t, 12, client.runID) +} + +func TestCancelRunError(t *testing.T) { + t.Parallel() + + client := &fakeActionsRunCancelService{err: errors.New("conflict")} + err := cancelRun(t.Context(), client, "owner", "repo", 12) + require.ErrorContains(t, err, "failed to cancel run 12") + assert.ErrorContains(t, err, "conflict") +} diff --git a/cmd/actions/runs/delete.go b/cmd/actions/runs/delete.go index 310afedd..08533123 100644 --- a/cmd/actions/runs/delete.go +++ b/cmd/actions/runs/delete.go @@ -14,12 +14,12 @@ import ( "github.com/urfave/cli/v3" ) -// CmdRunsDelete represents a sub command to delete/cancel workflow runs +// CmdRunsDelete represents a sub command to delete workflow runs var CmdRunsDelete = cli.Command{ Name: "delete", - Aliases: []string{"remove", "rm", "cancel"}, - Usage: "Delete or cancel a workflow run", - Description: "Delete (cancel) a workflow run from the repository", + Aliases: []string{"remove", "rm"}, + Usage: "Delete a workflow run", + Description: "Delete a completed workflow run from the repository", ArgsUsage: "", Action: runRunsDelete, Flags: append([]cli.Flag{ diff --git a/docs/CLI.md b/docs/CLI.md index 3f74a38e..ad64b26d 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -1607,9 +1607,21 @@ View workflow run details **--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional -#### delete, remove, rm, cancel +#### cancel -Delete or cancel a workflow run +Cancel a workflow run + +**--login, -l**="": Use a different Gitea Login. Optional + +**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json) + +**--remote, -R**="": Discover Gitea login from remote. Optional + +**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional + +#### delete, remove, rm + +Delete a workflow run **--confirm, -y**: confirm deletion without prompting