mirror of
https://github.com/go-gitea/gitea.git
synced 2026-01-13 08:35:35 +00:00
Partially backport #36336 1. correctly parse git protocol's "OldCommit NewCommit RefName" line, it should be explicitly split by space 2. trim space for the "commit status context name" to follow the same behavior of git_model.NewCommitStatus
180 lines
5.4 KiB
Go
180 lines
5.4 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
|
|
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"
|
|
actions_module "code.gitea.io/gitea/modules/actions"
|
|
"code.gitea.io/gitea/modules/commitstatus"
|
|
git "code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/log"
|
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
|
commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
|
|
|
|
"github.com/nektos/act/pkg/jobparser"
|
|
)
|
|
|
|
// 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
|
|
event string
|
|
)
|
|
switch run.Event {
|
|
case webhook_module.HookEventPush:
|
|
event = "push"
|
|
payload, err := run.GetPushEventPayload()
|
|
if err != nil {
|
|
return fmt.Errorf("GetPushEventPayload: %w", err)
|
|
}
|
|
if payload.HeadCommit == nil {
|
|
return errors.New("head commit is missing in event payload")
|
|
}
|
|
sha = payload.HeadCommit.ID
|
|
case // pull_request
|
|
webhook_module.HookEventPullRequest,
|
|
webhook_module.HookEventPullRequestSync,
|
|
webhook_module.HookEventPullRequestAssign,
|
|
webhook_module.HookEventPullRequestLabel,
|
|
webhook_module.HookEventPullRequestReviewRequest,
|
|
webhook_module.HookEventPullRequestMilestone:
|
|
if run.TriggerEvent == actions_module.GithubEventPullRequestTarget {
|
|
event = "pull_request_target"
|
|
} else {
|
|
event = "pull_request"
|
|
}
|
|
payload, err := run.GetPullRequestEventPayload()
|
|
if err != nil {
|
|
return fmt.Errorf("GetPullRequestEventPayload: %w", err)
|
|
}
|
|
if payload.PullRequest == nil {
|
|
return errors.New("pull request is missing in event payload")
|
|
} else if payload.PullRequest.Head == nil {
|
|
return errors.New("head of pull request is missing in event payload")
|
|
}
|
|
sha = payload.PullRequest.Head.Sha
|
|
case webhook_module.HookEventRelease:
|
|
event = string(run.Event)
|
|
sha = run.CommitSHA
|
|
default:
|
|
return nil
|
|
}
|
|
|
|
repo := run.Repo
|
|
// 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)
|
|
ctxname = strings.TrimSpace(ctxname) // git_model.NewCommitStatus also trims spaces
|
|
state := toCommitStatus(job.Status)
|
|
if statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll); err == nil {
|
|
for _, v := range statuses {
|
|
if v.Context == ctxname {
|
|
if v.State == state {
|
|
// no need to update
|
|
return nil
|
|
}
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
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"
|
|
}
|
|
|
|
index, err := getIndexOfJob(ctx, job)
|
|
if err != nil {
|
|
return fmt.Errorf("getIndexOfJob: %w", err)
|
|
}
|
|
|
|
creator := user_model.NewActionsUser()
|
|
commitID, err := git.NewIDFromString(sha)
|
|
if err != nil {
|
|
return fmt.Errorf("HashTypeInterfaceFromHashString: %w", err)
|
|
}
|
|
status := git_model.CommitStatus{
|
|
SHA: sha,
|
|
TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), index),
|
|
Description: description,
|
|
Context: ctxname,
|
|
CreatorID: creator.ID,
|
|
State: state,
|
|
}
|
|
|
|
return commitstatus_service.CreateCommitStatus(ctx, repo, creator, commitID.String(), &status)
|
|
}
|
|
|
|
func toCommitStatus(status actions_model.Status) commitstatus.CommitStatusState {
|
|
switch status {
|
|
case actions_model.StatusSuccess:
|
|
return commitstatus.CommitStatusSuccess
|
|
case actions_model.StatusFailure, actions_model.StatusCancelled:
|
|
return commitstatus.CommitStatusFailure
|
|
case actions_model.StatusWaiting, actions_model.StatusBlocked, actions_model.StatusRunning:
|
|
return commitstatus.CommitStatusPending
|
|
case actions_model.StatusSkipped:
|
|
return commitstatus.CommitStatusSkipped
|
|
default:
|
|
return commitstatus.CommitStatusError
|
|
}
|
|
}
|
|
|
|
func getIndexOfJob(ctx context.Context, job *actions_model.ActionRunJob) (int, error) {
|
|
// TODO: store job index as a field in ActionRunJob to avoid this
|
|
jobs, err := actions_model.GetRunJobsByRunID(ctx, job.RunID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
for i, v := range jobs {
|
|
if v.ID == job.ID {
|
|
return i, nil
|
|
}
|
|
}
|
|
return 0, nil
|
|
}
|