|
|
|
@ -6,79 +6,80 @@ package actions |
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"path" |
|
|
|
|
|
|
|
|
|
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" |
|
|
|
|
"code.gitea.io/gitea/modules/log" |
|
|
|
|
api "code.gitea.io/gitea/modules/structs" |
|
|
|
|
webhook_module "code.gitea.io/gitea/modules/webhook" |
|
|
|
|
|
|
|
|
|
"github.com/nektos/act/pkg/jobparser" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) error { |
|
|
|
|
// CreateCommitStatus creates a commit status for the given job.
|
|
|
|
|
// It won't return an error failed, but will log it, because it's not critical.
|
|
|
|
|
func CreateCommitStatus(ctx context.Context, jobs ...*actions_model.ActionRunJob) { |
|
|
|
|
for _, job := range jobs { |
|
|
|
|
if err := createCommitStatus(ctx, job); err != nil { |
|
|
|
|
log.Error("Failed to create commit status for job %d: %v", job.ID, err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
sha string |
|
|
|
|
creatorID int64 |
|
|
|
|
event string |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
switch run.Event { |
|
|
|
|
case webhook_module.HookEventPush: |
|
|
|
|
event = "push" |
|
|
|
|
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: |
|
|
|
|
if payload.HeadCommit == nil { |
|
|
|
|
return fmt.Errorf("head commit is missing in event payload") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sha = payload.HeadCommit.ID |
|
|
|
|
creatorID = payload.Pusher.ID |
|
|
|
|
case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync: |
|
|
|
|
event = "pull_request" |
|
|
|
|
payload, err := run.GetPullRequestEventPayload() |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("GetPullRequestEventPayload: %w", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch { |
|
|
|
|
case payload.PullRequest == nil: |
|
|
|
|
if payload.PullRequest == nil { |
|
|
|
|
return fmt.Errorf("pull request is missing in event payload") |
|
|
|
|
case payload.PullRequest.Head == nil: |
|
|
|
|
} else if payload.PullRequest.Head == nil { |
|
|
|
|
return fmt.Errorf("head of pull request is missing in event payload") |
|
|
|
|
case payload.PullRequest.Head.Repository == nil: |
|
|
|
|
return fmt.Errorf("head repository of pull request is missing in event payload") |
|
|
|
|
case payload.PullRequest.Head.Repository.Owner == nil: |
|
|
|
|
return fmt.Errorf("owner of head repository of pull request is missing in evnt payload") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sha = payload.PullRequest.Head.Sha |
|
|
|
|
creatorID = payload.PullRequest.Head.Repository.Owner.ID |
|
|
|
|
default: |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
repo := run.Repo |
|
|
|
|
ctxname := job.Name |
|
|
|
|
state := toCommitStatus(job.Status) |
|
|
|
|
creator, err := user_model.GetUserByID(ctx, creatorID) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("GetUserByID: %w", err) |
|
|
|
|
// TODO: store workflow name as a field in ActionRun to avoid parsing
|
|
|
|
|
runName := path.Base(run.WorkflowID) |
|
|
|
|
if wfs, err := jobparser.Parse(job.WorkflowPayload); err == nil && len(wfs) > 0 { |
|
|
|
|
runName = wfs[0].Name |
|
|
|
|
} |
|
|
|
|
ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event) |
|
|
|
|
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 { |
|
|
|
|
// no need to update
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
break |
|
|
|
@ -88,6 +89,26 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er |
|
|
|
|
return fmt.Errorf("GetLatestCommitStatus: %w", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
description := "" |
|
|
|
|
switch job.Status { |
|
|
|
|
// TODO: if we want support description in different languages, we need to support i18n placeholders in it
|
|
|
|
|
case actions_model.StatusSuccess: |
|
|
|
|
description = fmt.Sprintf("Successful in %s", job.Duration()) |
|
|
|
|
case actions_model.StatusFailure: |
|
|
|
|
description = fmt.Sprintf("Failing after %s", job.Duration()) |
|
|
|
|
case actions_model.StatusCancelled: |
|
|
|
|
description = "Has been cancelled" |
|
|
|
|
case actions_model.StatusSkipped: |
|
|
|
|
description = "Has been skipped" |
|
|
|
|
case actions_model.StatusRunning: |
|
|
|
|
description = "Has started running" |
|
|
|
|
case actions_model.StatusWaiting: |
|
|
|
|
description = "Waiting to run" |
|
|
|
|
case actions_model.StatusBlocked: |
|
|
|
|
description = "Blocked by required conditions" |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
creator := user_model.NewActionsUser() |
|
|
|
|
if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ |
|
|
|
|
Repo: repo, |
|
|
|
|
SHA: sha, |
|
|
|
@ -95,9 +116,9 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er |
|
|
|
|
CommitStatus: &git_model.CommitStatus{ |
|
|
|
|
SHA: sha, |
|
|
|
|
TargetURL: run.Link(), |
|
|
|
|
Description: "", |
|
|
|
|
Description: description, |
|
|
|
|
Context: ctxname, |
|
|
|
|
CreatorID: creatorID, |
|
|
|
|
CreatorID: creator.ID, |
|
|
|
|
State: state, |
|
|
|
|
}, |
|
|
|
|
}); err != nil { |
|
|
|
@ -109,9 +130,9 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er |
|
|
|
|
|
|
|
|
|
func toCommitStatus(status actions_model.Status) api.CommitStatusState { |
|
|
|
|
switch status { |
|
|
|
|
case actions_model.StatusSuccess: |
|
|
|
|
case actions_model.StatusSuccess, actions_model.StatusSkipped: |
|
|
|
|
return api.CommitStatusSuccess |
|
|
|
|
case actions_model.StatusFailure, actions_model.StatusCancelled, actions_model.StatusSkipped: |
|
|
|
|
case actions_model.StatusFailure, actions_model.StatusCancelled: |
|
|
|
|
return api.CommitStatusFailure |
|
|
|
|
case actions_model.StatusWaiting, actions_model.StatusBlocked: |
|
|
|
|
return api.CommitStatusPending |
|
|
|
|