forgejo/services/actions/commit_status.go
Giteabot a4158d1904
Avoid panic caused by broken payload when creating commit status (#23216) (#23294)
Backport #23216

When creating commit status for Actons jobs, a payload with nil
`HeadCommit` will cause panic.

Reported at:
https://gitea.com/gitea/act_runner/issues/28#issuecomment-732166

Although the `HeadCommit` probably can not be nil after #23215,
`CreateCommitStatus` should protect itself, to avoid being broken in the
future.

In addition, it's enough to print error log instead of returning err
when `CreateCommitStatus` failed.

Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: delvh <dev.lh@web.de>
2023-03-04 14:23:49 +00:00

98 lines
2.6 KiB
Go

// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"context"
"fmt"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook"
)
func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) error {
if err := job.LoadAttributes(ctx); err != nil {
return fmt.Errorf("load run: %w", err)
}
run := job.Run
if run.Event != webhook_module.HookEventPush {
return nil
}
payload, err := run.GetPushEventPayload()
if err != nil {
return fmt.Errorf("GetPushEventPayload: %w", err)
}
// Since the payload comes from json data, we should check if it's broken, or it will cause panic
switch {
case payload.Repo == nil:
return fmt.Errorf("repo is missing in event payload")
case payload.Pusher == nil:
return fmt.Errorf("pusher is missing in event payload")
case payload.HeadCommit == nil:
return fmt.Errorf("head commit is missing in event payload")
}
creator, err := user_model.GetUserByID(ctx, payload.Pusher.ID)
if err != nil {
return fmt.Errorf("GetUserByID: %w", err)
}
repo := run.Repo
sha := payload.HeadCommit.ID
ctxname := job.Name
state := toCommitStatus(job.Status)
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{}); err == nil {
for _, v := range statuses {
if v.Context == ctxname {
if v.State == state {
return nil
}
break
}
}
} else {
return fmt.Errorf("GetLatestCommitStatus: %w", err)
}
if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
Repo: repo,
SHA: payload.HeadCommit.ID,
Creator: creator,
CommitStatus: &git_model.CommitStatus{
SHA: sha,
TargetURL: run.Link(),
Description: "",
Context: ctxname,
CreatorID: payload.Pusher.ID,
State: state,
},
}); err != nil {
return fmt.Errorf("NewCommitStatus: %w", err)
}
return nil
}
func toCommitStatus(status actions_model.Status) api.CommitStatusState {
switch status {
case actions_model.StatusSuccess:
return api.CommitStatusSuccess
case actions_model.StatusFailure, actions_model.StatusCancelled, actions_model.StatusSkipped:
return api.CommitStatusFailure
case actions_model.StatusWaiting, actions_model.StatusBlocked:
return api.CommitStatusPending
case actions_model.StatusRunning:
return api.CommitStatusRunning
default:
return api.CommitStatusError
}
}