Skip to content

Add endpoint deleting workflow run #34337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 57 commits into from
May 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
83f1bba
Backend
NorthRealm May 1, 2025
a633f44
Refactor
NorthRealm May 1, 2025
29a0fe5
run.go jobIds len check
NorthRealm May 1, 2025
b92b3c5
API endpoint
NorthRealm May 1, 2025
cb05cf7
FIX
NorthRealm May 1, 2025
0dc984a
Swagger
NorthRealm May 1, 2025
61328b8
LINT FIX
NorthRealm May 1, 2025
c76fb69
Fix
NorthRealm May 2, 2025
05aa0e4
MOCK
NorthRealm May 2, 2025
5f6238b
Test
NorthRealm May 2, 2025
e5a8968
Mock
NorthRealm May 2, 2025
7e6e6ae
Test
NorthRealm May 2, 2025
8922924
Test
NorthRealm May 2, 2025
9435c3a
MOCK
NorthRealm May 2, 2025
1b759fb
Test
NorthRealm May 2, 2025
cf5bcfb
LINT FIX
NorthRealm May 2, 2025
51b328e
Merge remote-tracking branch 'upstream/main' into delete-run-1
NorthRealm May 2, 2025
1b5be81
UPDATE
NorthRealm May 3, 2025
63d2cda
Button
NorthRealm May 3, 2025
08f71c3
Button
NorthRealm May 3, 2025
933ffee
Button
NorthRealm May 3, 2025
fdc3893
update
NorthRealm May 3, 2025
40da061
Merge remote-tracking branch 'upstream/main' into delete-run-1
NorthRealm May 4, 2025
b78b1ba
Delete comment
NorthRealm May 4, 2025
a38a4ec
Fix
NorthRealm May 4, 2025
3bec240
Merge remote-tracking branch 'upstream/main' into delete-run-1
NorthRealm May 5, 2025
2d12444
Update
NorthRealm May 5, 2025
e4c5327
Update API
NorthRealm May 5, 2025
8d96bf2
update
NorthRealm May 6, 2025
3e77294
Locale
NorthRealm May 6, 2025
31bc854
Merge remote-tracking branch 'upstream/main' into delete-run-1
NorthRealm May 6, 2025
5236e8e
Merge remote-tracking branch 'upstream/main' into delete-run-1
NorthRealm May 8, 2025
85c80e1
Update deletion
NorthRealm May 8, 2025
4c7821c
update
NorthRealm May 8, 2025
0a53d98
Refactor
NorthRealm May 8, 2025
bd90b60
ADD
NorthRealm May 8, 2025
049eb90
fix
NorthRealm May 8, 2025
2718400
lint fix
NorthRealm May 8, 2025
2bbc402
update
NorthRealm May 9, 2025
08d5409
Merge remote-tracking branch 'upstream/main' into delete-run-1
NorthRealm May 9, 2025
1eed130
update
NorthRealm May 9, 2025
fbd1a0e
Update template
NorthRealm May 12, 2025
e7a66c1
comment
NorthRealm May 13, 2025
9a4eb9f
update
NorthRealm May 13, 2025
301c019
Merge remote-tracking branch 'upstream/main' into delete-run-1
NorthRealm May 13, 2025
a7df3c4
update
NorthRealm May 13, 2025
8bf09b9
update
NorthRealm May 13, 2025
9298b10
reqToken
NorthRealm May 13, 2025
04fb637
template
NorthRealm May 13, 2025
bf6131f
FIX
NorthRealm May 13, 2025
3822673
improve templates
wxiaoguang May 13, 2025
866656b
use GetRunByRepoAndID to simplify logic
wxiaoguang May 13, 2025
3a60c3d
use json error message
wxiaoguang May 13, 2025
7142a95
fix UpdateRun logic
wxiaoguang May 13, 2025
ad5234f
fix lint
wxiaoguang May 13, 2025
8012a7e
fix "not found" usage
wxiaoguang May 13, 2025
7588e0b
Merge branch 'main' into delete-run-1
wxiaoguang May 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions models/actions/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
Expand Down Expand Up @@ -343,13 +344,13 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
return committer.Commit()
}

func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
var run ActionRun
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run)
has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", runID, repoID).Get(&run)
if err != nil {
return nil, err
} else if !has {
return nil, fmt.Errorf("run with id %d: %w", id, util.ErrNotExist)
return nil, fmt.Errorf("run with id %d: %w", runID, util.ErrNotExist)
}

return &run, nil
Expand Down Expand Up @@ -420,17 +421,10 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {

if run.Status != 0 || slices.Contains(cols, "status") {
if run.RepoID == 0 {
run, err = GetRunByID(ctx, run.ID)
if err != nil {
return err
}
setting.PanicInDevOrTesting("RepoID should not be 0")
}
if run.Repo == nil {
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
if err != nil {
return err
}
run.Repo = repo
if err = run.LoadRepo(ctx); err != nil {
return err
}
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions models/actions/run_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (job *ActionRunJob) Duration() time.Duration {

func (job *ActionRunJob) LoadRun(ctx context.Context) error {
if job.Run == nil {
run, err := GetRunByID(ctx, job.RunID)
run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
if err != nil {
return err
}
Expand Down Expand Up @@ -142,7 +142,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
{
// Other goroutines may aggregate the status of the run and update it too.
// So we need load the run and its jobs before updating the run.
run, err := GetRunByID(ctx, job.RunID)
run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
if err != nil {
return 0, err
}
Expand Down
4 changes: 4 additions & 0 deletions models/actions/task_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func (tasks TaskList) LoadAttributes(ctx context.Context) error {
type FindTaskOptions struct {
db.ListOptions
RepoID int64
JobID int64
OwnerID int64
CommitSHA string
Status Status
Expand All @@ -61,6 +62,9 @@ func (opts FindTaskOptions) ToConds() builder.Cond {
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
if opts.JobID > 0 {
cond = cond.And(builder.Eq{"job_id": opts.JobID})
}
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
Expand Down
36 changes: 36 additions & 0 deletions models/fixtures/action_artifact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,39 @@
created_unix: 1730330775
updated_unix: 1730330775
expired_unix: 1738106775

-
id: 24
run_id: 795
runner_id: 1
repo_id: 2
owner_id: 2
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
storage_path: "27/5/1730330775594233150.chunk"
file_size: 1024
file_compressed_size: 1024
content_encoding: "application/zip"
artifact_path: "artifact-795-1.zip"
artifact_name: "artifact-795-1"
status: 2
created_unix: 1730330775
updated_unix: 1730330775
expired_unix: 1738106775

-
id: 25
run_id: 795
runner_id: 1
repo_id: 2
owner_id: 2
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
storage_path: "27/5/1730330775594233150.chunk"
file_size: 1024
file_compressed_size: 1024
content_encoding: "application/zip"
artifact_path: "artifact-795-2.zip"
artifact_name: "artifact-795-2"
status: 2
created_unix: 1730330775
updated_unix: 1730330775
expired_unix: 1738106775
22 changes: 21 additions & 1 deletion models/fixtures/action_run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
is_fork_pull_request: 0
status: 1
status: 6 # running
started: 1683636528
stopped: 1683636626
created: 1683636108
Expand All @@ -74,3 +74,23 @@
updated: 1683636626
need_approval: 0
approved_by: 0

-
id: 795
title: "to be deleted (test)"
repo_id: 2
owner_id: 2
workflow_id: "test.yaml"
index: 191
trigger_user_id: 1
ref: "refs/heads/test"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
is_fork_pull_request: 0
status: 2
started: 1683636528
stopped: 1683636626
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0
30 changes: 30 additions & 0 deletions models/fixtures/action_run_job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,33 @@
status: 5
started: 1683636528
stopped: 1683636626

-
id: 198
run_id: 795
repo_id: 2
owner_id: 2
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
name: job_1
attempt: 1
job_id: job_1
task_id: 53
status: 1
started: 1683636528
stopped: 1683636626

-
id: 199
run_id: 795
repo_id: 2
owner_id: 2
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
name: job_2
attempt: 1
job_id: job_2
task_id: 54
status: 2
started: 1683636528
stopped: 1683636626
40 changes: 40 additions & 0 deletions models/fixtures/action_task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,43 @@
log_length: 707
log_size: 90179
log_expired: 0
-
id: 53
job_id: 198
attempt: 1
runner_id: 1
status: 1
started: 1683636528
stopped: 1683636626
repo_id: 2
owner_id: 2
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784223
token_salt: ffffffffff
token_last_eight: ffffffff
log_filename: artifact-test2/2f/47.log
log_in_storage: 1
log_length: 0
log_size: 0
log_expired: 0
-
id: 54
job_id: 199
attempt: 1
runner_id: 1
status: 2
started: 1683636528
stopped: 1683636626
repo_id: 2
owner_id: 2
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784224
token_salt: ffffffffff
token_last_eight: ffffffff
log_filename: artifact-test2/2f/47.log
log_in_storage: 1
log_length: 0
log_size: 0
log_expired: 0
3 changes: 3 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3811,6 +3811,9 @@ runs.no_workflows.documentation = For more information on Gitea Actions, see <a
runs.no_runs = The workflow has no runs yet.
runs.empty_commit_message = (empty commit message)
runs.expire_log_message = Logs have been purged because they were too old.
runs.delete = Delete workflow run
runs.delete.description = Are you sure you want to permanently delete this workflow run? This action cannot be undone.
runs.not_done = This workflow run is not done.

workflow.disable = Disable Workflow
workflow.disable_success = Workflow '%s' disabled successfully.
Expand Down
5 changes: 4 additions & 1 deletion routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1279,7 +1279,10 @@ func Routes() *web.Router {
}, reqToken(), reqAdmin())
m.Group("/actions", func() {
m.Get("/tasks", repo.ListActionTasks)
m.Get("/runs/{run}/artifacts", repo.GetArtifactsOfRun)
m.Group("/runs/{run}", func() {
m.Get("/artifacts", repo.GetArtifactsOfRun)
m.Delete("", reqToken(), reqRepoWriter(unit.TypeActions), repo.DeleteActionRun)
})
m.Get("/artifacts", repo.GetArtifacts)
m.Group("/artifacts/{artifact_id}", func() {
m.Get("", repo.GetArtifact)
Expand Down
52 changes: 52 additions & 0 deletions routers/api/v1/repo/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,58 @@ func GetArtifactsOfRun(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, &res)
}

// DeleteActionRun Delete a workflow run
func DeleteActionRun(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/actions/runs/{run} repository deleteActionRun
// ---
// summary: Delete a workflow run
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: name of the owner
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repository
// type: string
// required: true
// - name: run
// in: path
// description: runid of the workflow run
// type: integer
// required: true
// responses:
// "204":
// description: "No Content"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"

runID := ctx.PathParamInt64("run")
run, err := actions_model.GetRunByRepoAndID(ctx, ctx.Repo.Repository.ID, runID)
if errors.Is(err, util.ErrNotExist) {
ctx.APIError(http.StatusNotFound, err)
return
} else if err != nil {
ctx.APIErrorInternal(err)
return
}
if !run.Status.IsDone() {
ctx.APIError(http.StatusBadRequest, "this workflow run is not done")
return
}

if err := actions_service.DeleteRun(ctx, run); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}

// GetArtifacts Lists all artifacts for a repository.
func GetArtifacts(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/actions/artifacts repository getArtifacts
Expand Down
2 changes: 2 additions & 0 deletions routers/web/repo/actions/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ func prepareWorkflowList(ctx *context.Context, workflows []Workflow) {
pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0

ctx.Data["AllowDeleteWorkflowRuns"] = ctx.Repo.CanWrite(unit.TypeActions)
}

// loadIsRefDeleted loads the IsRefDeleted field for each run in the list.
Expand Down
27 changes: 27 additions & 0 deletions routers/web/repo/actions/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,33 @@ func Approve(ctx *context_module.Context) {
ctx.JSON(http.StatusOK, struct{}{})
}

func Delete(ctx *context_module.Context) {
runIndex := getRunIndex(ctx)
repoID := ctx.Repo.Repository.ID

run, err := actions_model.GetRunByIndex(ctx, repoID, runIndex)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
ctx.JSONErrorNotFound()
return
}
ctx.ServerError("GetRunByIndex", err)
return
}

if !run.Status.IsDone() {
ctx.JSONError(ctx.Tr("actions.runs.not_done"))
return
}

if err := actions_service.DeleteRun(ctx, run); err != nil {
ctx.ServerError("DeleteRun", err)
return
}

ctx.JSONOK()
}

// getRunJobs gets the jobs of runIndex, and returns jobs[jobIndex], jobs.
// Any error will be written to the ctx.
// It never returns a nil job of an empty jobs, if the jobIndex is out of range, it will be treated as 0.
Expand Down
2 changes: 1 addition & 1 deletion routers/web/repo/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ func MergeUpstream(ctx *context.Context) {
_, err := repo_service.MergeUpstream(ctx, ctx.Doer, ctx.Repo.Repository, branchName)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
ctx.JSONError(ctx.Tr("error.not_found"))
ctx.JSONErrorNotFound()
return
} else if pull_service.IsErrMergeConflicts(err) {
ctx.JSONError(ctx.Tr("repo.pulls.merge_conflict"))
Expand Down
1 change: 1 addition & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,7 @@ func registerWebRoutes(m *web.Router) {
})
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
m.Post("/delete", reqRepoActionsWriter, actions.Delete)
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
m.Delete("/artifacts/{artifact_name}", reqRepoActionsWriter, actions.ArtifactsDeleteView)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
Expand Down
Loading