mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-23 18:54:39 +02:00
enhance(actions): add pending job status and align job statuses with GitHub (#39376)
This commit is contained in:
1 parent
0d09986790
commit
9e7b302bd3
45 files changed
+397
-189
No files matched your search
@@ -53,6 +53,10 @@ func CreateCommitStatusForRunJobs(ctx context.Context, run *actions_model.Action
|
||||
scopedPrefix = actions_model.ScopedStatusContextPrefix(ctx, run.WorkflowRepoID)
|
||||
}
|
||||
|
||||
var pendingFilter *pendingJobFilter
|
||||
if slices.ContainsFunc(jobs, func(job *actions_model.ActionRunJob) bool { return job.Status.IsPending() && !job.IsMatrixDeferred }) {
|
||||
pendingFilter = newPendingJobFilter(ctx, run)
|
||||
}
|
||||
for _, job := range jobs {
|
||||
// A deferred-matrix placeholder's name changes when it expands, so a status created while it
|
||||
// waits would be orphaned. The emitter reloads the jobs after expanding and creates them
|
||||
@@ -61,7 +65,7 @@ func CreateCommitStatusForRunJobs(ctx context.Context, run *actions_model.Action
|
||||
if job.IsMatrixDeferred && !job.Status.IsDone() {
|
||||
continue
|
||||
}
|
||||
if err = createCommitStatus(ctx, run.Repo, event, commitID, scopedPrefix, run, job); err != nil {
|
||||
if err = createCommitStatus(ctx, run.Repo, event, commitID, scopedPrefix, run, job, pendingFilter); err != nil {
|
||||
log.Error("Failed to create commit status for job %d: %v", job.ID, err)
|
||||
}
|
||||
}
|
||||
@@ -151,7 +155,7 @@ func getCommitStatusEventNameAndCommitID(run *actions_model.ActionRun) (event, c
|
||||
return event, commitID, nil
|
||||
}
|
||||
|
||||
func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event, commitID, scopedPrefix string, run *actions_model.ActionRun, job *actions_model.ActionRunJob) error {
|
||||
func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event, commitID, scopedPrefix string, run *actions_model.ActionRun, job *actions_model.ActionRunJob, pendingFilter *pendingJobFilter) error {
|
||||
displayName := actions_module.WorkflowDisplayName(run.WorkflowID, job.WorkflowPayload)
|
||||
ctxName := actions_module.WorkflowStatusContextName(displayName, job.Name, event) // git_model.NewCommitStatus also trims spaces
|
||||
if run.IsScopedRun {
|
||||
@@ -160,7 +164,35 @@ func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event,
|
||||
ctxName = actions_module.ScopedWorkflowStatusContextName(scopedPrefix, displayName, job.Name, event)
|
||||
}
|
||||
targetURL := fmt.Sprintf("%s/jobs/%d", run.Link(), job.ID)
|
||||
return createWorkflowCommitStatus(ctx, repo, commitID, ctxName, run.WorkflowID, toCommitStatus(job.Status), targetURL, toCommitStatusDescription(job))
|
||||
return createWorkflowCommitStatus(ctx, repo, commitID, ctxName, run.WorkflowID, toCommitStatus(job.Status), targetURL, toCommitStatusDescription(job), pendingFilter.onlyReplace(job, ctxName))
|
||||
}
|
||||
|
||||
// pendingJobFilter keeps optional Pending jobs from posting new statuses
|
||||
type pendingJobFilter struct {
|
||||
requiredGlobs []glob.Glob
|
||||
}
|
||||
|
||||
func newPendingJobFilter(ctx context.Context, run *actions_model.ActionRun) *pendingJobFilter {
|
||||
rules, err := git_model.FindRepoProtectedBranchRules(ctx, run.RepoID)
|
||||
if err != nil {
|
||||
log.Error("FindRepoProtectedBranchRules: %v", err)
|
||||
return nil
|
||||
}
|
||||
if slices.ContainsFunc(rules, func(rule *git_model.ProtectedBranch) bool {
|
||||
return rule.EnableStatusCheck && len(rule.StatusCheckContexts) == 0
|
||||
}) {
|
||||
return nil
|
||||
}
|
||||
requiredGlobs, err := requiredStatusContextGlobs(ctx, run.Repo, rules)
|
||||
if err != nil {
|
||||
log.Error("requiredStatusContextGlobs: %v", err)
|
||||
return nil
|
||||
}
|
||||
return &pendingJobFilter{requiredGlobs: requiredGlobs}
|
||||
}
|
||||
|
||||
func (f *pendingJobFilter) onlyReplace(job *actions_model.ActionRunJob, ctxName string) bool {
|
||||
return f != nil && job.Status.IsPending() && !slices.ContainsFunc(f.requiredGlobs, func(gp glob.Glob) bool { return gp.Match(ctxName) })
|
||||
}
|
||||
|
||||
// getAllRequiredStatusContextGlobs returns the compiled globs of every status-check context required in the repo:
|
||||
@@ -170,6 +202,10 @@ func getAllRequiredStatusContextGlobs(ctx context.Context, repo *repo_model.Repo
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FindRepoProtectedBranchRules: %w", err)
|
||||
}
|
||||
return requiredStatusContextGlobs(ctx, repo, rules)
|
||||
}
|
||||
|
||||
func requiredStatusContextGlobs(ctx context.Context, repo *repo_model.Repository, rules git_model.ProtectedBranchRules) ([]glob.Glob, error) {
|
||||
required, err := pull_service.EffectiveRequiredContexts(ctx, repo, rules...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("EffectiveRequiredContexts: %w", err)
|
||||
@@ -246,7 +282,7 @@ func CreateSkippedCommitStatusForFilteredWorkflow(ctx context.Context, repo *rep
|
||||
continue
|
||||
}
|
||||
// "Skipped" mirrors toCommitStatusDescription for StatusSkipped.
|
||||
if err := createWorkflowCommitStatus(ctx, repo, commitID, ctxName, workflowID, commitstatus.CommitStatusSkipped, "", "Skipped"); err != nil {
|
||||
if err := createWorkflowCommitStatus(ctx, repo, commitID, ctxName, workflowID, commitstatus.CommitStatusSkipped, "", "Skipped", false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -254,7 +290,7 @@ func CreateSkippedCommitStatusForFilteredWorkflow(ctx context.Context, repo *rep
|
||||
}
|
||||
|
||||
// createWorkflowCommitStatus posts the commit status for one workflow-job context.
|
||||
func createWorkflowCommitStatus(ctx context.Context, repo *repo_model.Repository, commitID, ctxName, workflowID string, state commitstatus.CommitStatusState, targetURL, description string) error {
|
||||
func createWorkflowCommitStatus(ctx context.Context, repo *repo_model.Repository, commitID, ctxName, workflowID string, state commitstatus.CommitStatusState, targetURL, description string, onlyReplace bool) error {
|
||||
// Mix the workflow file path into the hash so two workflow files that
|
||||
// share the same `name:` and job name produce distinct commit statuses
|
||||
// even though they render identically — matching GitHub's behavior
|
||||
@@ -280,14 +316,19 @@ func createWorkflowCommitStatus(ctx context.Context, repo *repo_model.Repository
|
||||
break
|
||||
}
|
||||
}
|
||||
hasPrevious := false
|
||||
for _, v := range statuses {
|
||||
if v.ContextHash == ctxHash {
|
||||
if v.State == state && v.TargetURL == targetURL && v.Description == description {
|
||||
return nil
|
||||
}
|
||||
hasPrevious = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if onlyReplace && !hasPrevious {
|
||||
return nil
|
||||
}
|
||||
|
||||
creator := user_model.NewActionsUser()
|
||||
status := git_model.CommitStatus{
|
||||
@@ -322,6 +363,8 @@ func toCommitStatusDescription(job *actions_model.ActionRunJob) string {
|
||||
return "Waiting to run"
|
||||
case actions_model.StatusBlocked:
|
||||
return "Blocked by required conditions"
|
||||
case actions_model.StatusPending:
|
||||
return "Waiting for needed jobs"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown status: %d", job.Status)
|
||||
}
|
||||
@@ -333,7 +376,7 @@ func toCommitStatus(status actions_model.Status) commitstatus.CommitStatusState
|
||||
return commitstatus.CommitStatusSuccess
|
||||
case actions_model.StatusFailure, actions_model.StatusCancelled:
|
||||
return commitstatus.CommitStatusFailure
|
||||
case actions_model.StatusWaiting, actions_model.StatusBlocked, actions_model.StatusRunning, actions_model.StatusCancelling:
|
||||
case actions_model.StatusWaiting, actions_model.StatusBlocked, actions_model.StatusPending, actions_model.StatusRunning, actions_model.StatusCancelling:
|
||||
return commitstatus.CommitStatusPending
|
||||
case actions_model.StatusSkipped:
|
||||
return commitstatus.CommitStatusSkipped
|
||||
|
||||
Reference in new issue
Block a user