loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Refactor commit status for Actions jobs (#23786)

Before:
<img width="353" alt="xnip_230329_163852"
src="https://user-images.githubusercontent.com/9418365/228479807-424452df-10fa-45cf-ae4b-09939c0ed54c.png">
After:
<img width="508" alt="xnip_230329_163358"
src="https://user-images.githubusercontent.com/9418365/228479923-537b54fe-9564-4105-a068-bcc75fa2a7ea.png">

Highlights:
- Treat `StatusSkipped` as `CommitStatusSuccess` instead of
`CommitStatusFailure`, so it fixed #23599.
- Use the bot user `gitea-actions` instead of the trigger as the creator
of commit status.
- New format `<run_name> / <job_name> / (<event>)` for the context of
commit status to avoid conflicts.
- Add descriptions for commit status.
- Add the missing calls to `CreateCommitStatus`.
- Refactor `CreateCommitStatus` to make it easier to use.

authored by

Jason Song and committed by
GitHub
3e8db31a e57e1144

+75 -70
+1 -4
routers/api/actions/runner/runner.go
··· 149 149 return nil, status.Errorf(codes.Internal, "load job: %v", err) 150 150 } 151 151 152 - if err := actions_service.CreateCommitStatus(ctx, task.Job); err != nil { 153 - log.Error("Update commit status for job %v failed: %v", task.Job.ID, err) 154 - // go on 155 - } 152 + actions_service.CreateCommitStatus(ctx, task.Job) 156 153 157 154 if req.Msg.State.Result != runnerv1.Result_RESULT_UNSPECIFIED { 158 155 if err := actions_service.EmitJobsIfReady(task.Job.RunID); err != nil {
+3
routers/api/actions/runner/utils.go
··· 13 13 "code.gitea.io/gitea/modules/log" 14 14 secret_module "code.gitea.io/gitea/modules/secret" 15 15 "code.gitea.io/gitea/modules/setting" 16 + "code.gitea.io/gitea/services/actions" 16 17 17 18 runnerv1 "code.gitea.io/actions-proto-go/runner/v1" 18 19 "google.golang.org/protobuf/types/known/structpb" ··· 26 27 if !ok { 27 28 return nil, false, nil 28 29 } 30 + 31 + actions.CreateCommitStatus(ctx, t.Job) 29 32 30 33 task := &runnerv1.Task{ 31 34 Id: t.ID,
+4 -11
routers/web/repo/actions/view.go
··· 16 16 "code.gitea.io/gitea/modules/actions" 17 17 "code.gitea.io/gitea/modules/base" 18 18 context_module "code.gitea.io/gitea/modules/context" 19 - "code.gitea.io/gitea/modules/log" 20 19 "code.gitea.io/gitea/modules/timeutil" 21 20 "code.gitea.io/gitea/modules/util" 22 21 "code.gitea.io/gitea/modules/web" ··· 264 263 return 265 264 } 266 265 267 - if err := actions_service.CreateCommitStatus(ctx, job); err != nil { 268 - log.Error("Update commit status for job %v failed: %v", job.ID, err) 269 - // go on 270 - } 266 + actions_service.CreateCommitStatus(ctx, job) 271 267 272 268 ctx.JSON(http.StatusOK, struct{}{}) 273 269 } ··· 308 304 return 309 305 } 310 306 311 - for _, job := range jobs { 312 - if err := actions_service.CreateCommitStatus(ctx, job); err != nil { 313 - log.Error("Update commit status for job %v failed: %v", job.ID, err) 314 - // go on 315 - } 316 - } 307 + actions_service.CreateCommitStatus(ctx, jobs...) 317 308 318 309 ctx.JSON(http.StatusOK, struct{}{}) 319 310 } ··· 348 339 ctx.Error(http.StatusInternalServerError, err.Error()) 349 340 return 350 341 } 342 + 343 + actions_service.CreateCommitStatus(ctx, jobs...) 351 344 352 345 ctx.JSON(http.StatusOK, struct{}{}) 353 346 }
+2 -10
services/actions/clear_tasks.go
··· 64 64 } 65 65 } 66 66 67 - for _, job := range jobs { 68 - if err := CreateCommitStatus(ctx, job); err != nil { 69 - log.Error("Update commit status for job %v failed: %v", job.ID, err) 70 - // go on 71 - } 72 - } 67 + CreateCommitStatus(ctx, jobs...) 73 68 74 69 return nil 75 70 } ··· 96 91 log.Warn("cancel abandoned job %v: %v", job.ID, err) 97 92 // go on 98 93 } 99 - if err := CreateCommitStatus(ctx, job); err != nil { 100 - log.Error("Update commit status for job %v failed: %v", job.ID, err) 101 - // go on 102 - } 94 + CreateCommitStatus(ctx, job) 103 95 } 104 96 105 97 return nil
+54 -33
services/actions/commit_status.go
··· 6 6 import ( 7 7 "context" 8 8 "fmt" 9 + "path" 9 10 10 11 actions_model "code.gitea.io/gitea/models/actions" 11 12 "code.gitea.io/gitea/models/db" 12 13 git_model "code.gitea.io/gitea/models/git" 13 14 user_model "code.gitea.io/gitea/models/user" 15 + "code.gitea.io/gitea/modules/log" 14 16 api "code.gitea.io/gitea/modules/structs" 15 17 webhook_module "code.gitea.io/gitea/modules/webhook" 18 + 19 + "github.com/nektos/act/pkg/jobparser" 16 20 ) 17 21 18 - func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) error { 22 + // CreateCommitStatus creates a commit status for the given job. 23 + // It won't return an error failed, but will log it, because it's not critical. 24 + func CreateCommitStatus(ctx context.Context, jobs ...*actions_model.ActionRunJob) { 25 + for _, job := range jobs { 26 + if err := createCommitStatus(ctx, job); err != nil { 27 + log.Error("Failed to create commit status for job %d: %v", job.ID, err) 28 + } 29 + } 30 + } 31 + 32 + func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) error { 19 33 if err := job.LoadAttributes(ctx); err != nil { 20 34 return fmt.Errorf("load run: %w", err) 21 35 } 22 36 23 37 run := job.Run 38 + 24 39 var ( 25 - sha string 26 - creatorID int64 40 + sha string 41 + event string 27 42 ) 28 - 29 43 switch run.Event { 30 44 case webhook_module.HookEventPush: 45 + event = "push" 31 46 payload, err := run.GetPushEventPayload() 32 47 if err != nil { 33 48 return fmt.Errorf("GetPushEventPayload: %w", err) 34 49 } 35 - 36 - // Since the payload comes from json data, we should check if it's broken, or it will cause panic 37 - switch { 38 - case payload.Repo == nil: 39 - return fmt.Errorf("repo is missing in event payload") 40 - case payload.Pusher == nil: 41 - return fmt.Errorf("pusher is missing in event payload") 42 - case payload.HeadCommit == nil: 50 + if payload.HeadCommit == nil { 43 51 return fmt.Errorf("head commit is missing in event payload") 44 52 } 45 - 46 53 sha = payload.HeadCommit.ID 47 - creatorID = payload.Pusher.ID 48 54 case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync: 55 + event = "pull_request" 49 56 payload, err := run.GetPullRequestEventPayload() 50 57 if err != nil { 51 58 return fmt.Errorf("GetPullRequestEventPayload: %w", err) 52 59 } 53 - 54 - switch { 55 - case payload.PullRequest == nil: 60 + if payload.PullRequest == nil { 56 61 return fmt.Errorf("pull request is missing in event payload") 57 - case payload.PullRequest.Head == nil: 62 + } else if payload.PullRequest.Head == nil { 58 63 return fmt.Errorf("head of pull request is missing in event payload") 59 - case payload.PullRequest.Head.Repository == nil: 60 - return fmt.Errorf("head repository of pull request is missing in event payload") 61 - case payload.PullRequest.Head.Repository.Owner == nil: 62 - return fmt.Errorf("owner of head repository of pull request is missing in evnt payload") 63 64 } 64 - 65 65 sha = payload.PullRequest.Head.Sha 66 - creatorID = payload.PullRequest.Head.Repository.Owner.ID 67 66 default: 68 67 return nil 69 68 } 70 69 71 70 repo := run.Repo 72 - ctxname := job.Name 73 - state := toCommitStatus(job.Status) 74 - creator, err := user_model.GetUserByID(ctx, creatorID) 75 - if err != nil { 76 - return fmt.Errorf("GetUserByID: %w", err) 71 + // TODO: store workflow name as a field in ActionRun to avoid parsing 72 + runName := path.Base(run.WorkflowID) 73 + if wfs, err := jobparser.Parse(job.WorkflowPayload); err == nil && len(wfs) > 0 { 74 + runName = wfs[0].Name 77 75 } 76 + ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event) 77 + state := toCommitStatus(job.Status) 78 78 if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{}); err == nil { 79 79 for _, v := range statuses { 80 80 if v.Context == ctxname { 81 81 if v.State == state { 82 + // no need to update 82 83 return nil 83 84 } 84 85 break ··· 88 89 return fmt.Errorf("GetLatestCommitStatus: %w", err) 89 90 } 90 91 92 + description := "" 93 + switch job.Status { 94 + // TODO: if we want support description in different languages, we need to support i18n placeholders in it 95 + case actions_model.StatusSuccess: 96 + description = fmt.Sprintf("Successful in %s", job.Duration()) 97 + case actions_model.StatusFailure: 98 + description = fmt.Sprintf("Failing after %s", job.Duration()) 99 + case actions_model.StatusCancelled: 100 + description = "Has been cancelled" 101 + case actions_model.StatusSkipped: 102 + description = "Has been skipped" 103 + case actions_model.StatusRunning: 104 + description = "Has started running" 105 + case actions_model.StatusWaiting: 106 + description = "Waiting to run" 107 + case actions_model.StatusBlocked: 108 + description = "Blocked by required conditions" 109 + } 110 + 111 + creator := user_model.NewActionsUser() 91 112 if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ 92 113 Repo: repo, 93 114 SHA: sha, ··· 95 116 CommitStatus: &git_model.CommitStatus{ 96 117 SHA: sha, 97 118 TargetURL: run.Link(), 98 - Description: "", 119 + Description: description, 99 120 Context: ctxname, 100 - CreatorID: creatorID, 121 + CreatorID: creator.ID, 101 122 State: state, 102 123 }, 103 124 }); err != nil { ··· 109 130 110 131 func toCommitStatus(status actions_model.Status) api.CommitStatusState { 111 132 switch status { 112 - case actions_model.StatusSuccess: 133 + case actions_model.StatusSuccess, actions_model.StatusSkipped: 113 134 return api.CommitStatusSuccess 114 - case actions_model.StatusFailure, actions_model.StatusCancelled, actions_model.StatusSkipped: 135 + case actions_model.StatusFailure, actions_model.StatusCancelled: 115 136 return api.CommitStatusFailure 116 137 case actions_model.StatusWaiting, actions_model.StatusBlocked: 117 138 return api.CommitStatusPending
+10 -6
services/actions/job_emitter.go
··· 45 45 } 46 46 47 47 func checkJobsOfRun(ctx context.Context, runID int64) error { 48 - return db.WithTx(ctx, func(ctx context.Context) error { 49 - jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: runID}) 50 - if err != nil { 51 - return err 52 - } 48 + jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: runID}) 49 + if err != nil { 50 + return err 51 + } 52 + if err := db.WithTx(ctx, func(ctx context.Context) error { 53 53 idToJobs := make(map[string][]*actions_model.ActionRunJob, len(jobs)) 54 54 for _, job := range jobs { 55 55 idToJobs[job.JobID] = append(idToJobs[job.JobID], job) ··· 67 67 } 68 68 } 69 69 return nil 70 - }) 70 + }); err != nil { 71 + return err 72 + } 73 + CreateCommitStatus(ctx, jobs...) 74 + return nil 71 75 } 72 76 73 77 type jobStatusResolver struct {
+1 -6
services/actions/notifier_helper.go
··· 185 185 if jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: run.ID}); err != nil { 186 186 log.Error("FindRunJobs: %v", err) 187 187 } else { 188 - for _, job := range jobs { 189 - if err := CreateCommitStatus(ctx, job); err != nil { 190 - log.Error("Update commit status for job %v failed: %v", job.ID, err) 191 - // go on 192 - } 193 - } 188 + CreateCommitStatus(ctx, jobs...) 194 189 } 195 190 196 191 }