@ -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
sha string
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
@ -93,6 +94,26 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
return fmt . Errorf ( "getIndexOfJob: %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 ,
@ -100,9 +121,9 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
CommitStatus : & git_model . CommitStatus {
SHA : sha ,
TargetURL : fmt . Sprintf ( "%s/jobs/%d" , run . Link ( ) , index ) ,
Description : "" ,
Description : description ,
Context : ctxname ,
CreatorID : creator ID,
CreatorID : creator . ID,
State : state ,
} ,
} ) ; err != nil {
@ -114,9 +135,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