Backport #37372 by @bircni Fixes the issue that status report always shows waiting to run, when already running https://github.com/go-gitea/gitea/issues/36906#issuecomment-4294545813 Co-authored-by: Nicolas <bircni@icloud.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
@@ -143,57 +142,59 @@ func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event,
|
|||||||
if wfs, err := jobparser.Parse(job.WorkflowPayload); err == nil && len(wfs) > 0 {
|
if wfs, err := jobparser.Parse(job.WorkflowPayload); err == nil && len(wfs) > 0 {
|
||||||
runName = wfs[0].Name
|
runName = wfs[0].Name
|
||||||
}
|
}
|
||||||
ctxName := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)
|
ctxName := strings.TrimSpace(fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)) // git_model.NewCommitStatus also trims spaces
|
||||||
ctxName = strings.TrimSpace(ctxName) // git_model.NewCommitStatus also trims spaces
|
|
||||||
state := toCommitStatus(job.Status)
|
state := toCommitStatus(job.Status)
|
||||||
if statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commitID, db.ListOptionsAll); err == nil {
|
targetURL := fmt.Sprintf("%s/jobs/%d", run.Link(), job.ID)
|
||||||
for _, v := range statuses {
|
description := toCommitStatusDescription(job)
|
||||||
if v.Context == ctxName {
|
|
||||||
if v.State == state {
|
statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commitID, db.ListOptionsAll)
|
||||||
// no need to update
|
if err != nil {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("GetLatestCommitStatus: %w", err)
|
return fmt.Errorf("GetLatestCommitStatus: %w", err)
|
||||||
}
|
}
|
||||||
|
for _, v := range statuses {
|
||||||
var description string
|
if v.Context == ctxName {
|
||||||
switch job.Status {
|
if v.State == state && v.TargetURL == targetURL && v.Description == description {
|
||||||
// TODO: if we want support description in different languages, we need to support i18n placeholders in it
|
return nil
|
||||||
case actions_model.StatusSuccess:
|
}
|
||||||
description = fmt.Sprintf("Successful in %s", job.Duration())
|
break
|
||||||
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"
|
|
||||||
default:
|
|
||||||
description = "Unknown status: " + strconv.Itoa(int(job.Status))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
creator := user_model.NewActionsUser()
|
creator := user_model.NewActionsUser()
|
||||||
status := git_model.CommitStatus{
|
status := git_model.CommitStatus{
|
||||||
SHA: commitID,
|
SHA: commitID,
|
||||||
TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), job.ID),
|
TargetURL: targetURL,
|
||||||
Description: description,
|
Description: description,
|
||||||
Context: ctxName,
|
Context: ctxName,
|
||||||
CreatorID: creator.ID,
|
|
||||||
State: state,
|
State: state,
|
||||||
|
CreatorID: creator.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
return commitstatus_service.CreateCommitStatus(ctx, repo, creator, commitID, &status)
|
return commitstatus_service.CreateCommitStatus(ctx, repo, creator, commitID, &status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toCommitStatusDescription(job *actions_model.ActionRunJob) string {
|
||||||
|
switch job.Status {
|
||||||
|
// TODO: if we want support description in different languages, we need to support i18n placeholders in it
|
||||||
|
case actions_model.StatusSuccess:
|
||||||
|
return fmt.Sprintf("Successful in %s", job.Duration())
|
||||||
|
case actions_model.StatusFailure:
|
||||||
|
return fmt.Sprintf("Failing after %s", job.Duration())
|
||||||
|
case actions_model.StatusCancelled:
|
||||||
|
return "Has been cancelled"
|
||||||
|
case actions_model.StatusSkipped:
|
||||||
|
return "Has been skipped"
|
||||||
|
case actions_model.StatusRunning:
|
||||||
|
return "Has started running"
|
||||||
|
case actions_model.StatusWaiting:
|
||||||
|
return "Waiting to run"
|
||||||
|
case actions_model.StatusBlocked:
|
||||||
|
return "Blocked by required conditions"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Unknown status: %d", job.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func toCommitStatus(status actions_model.Status) commitstatus.CommitStatusState {
|
func toCommitStatus(status actions_model.Status) commitstatus.CommitStatusState {
|
||||||
switch status {
|
switch status {
|
||||||
case actions_model.StatusSuccess:
|
case actions_model.StatusSuccess:
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
"code.gitea.io/gitea/modules/commitstatus"
|
||||||
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateCommitStatus_Dedupe(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||||
|
gitRepo, err := gitrepo.OpenRepository(t.Context(), repo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
|
commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
run := &actions_model.ActionRun{
|
||||||
|
ID: 99001,
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Repo: repo,
|
||||||
|
WorkflowID: "status-dedupe-test.yaml",
|
||||||
|
}
|
||||||
|
job := &actions_model.ActionRunJob{
|
||||||
|
ID: 99002,
|
||||||
|
RunID: run.ID,
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Name: "status-dedupe-job",
|
||||||
|
Status: actions_model.StatusWaiting,
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedContext := "status-dedupe-test.yaml / status-dedupe-job (push)"
|
||||||
|
expectedTargetURL := run.Link() + "/jobs/99002"
|
||||||
|
|
||||||
|
require.NoError(t, createCommitStatus(t.Context(), repo, "push", commit.ID.String(), run, job))
|
||||||
|
|
||||||
|
statuses := findCommitStatusesForContext(t, repo.ID, commit.ID.String(), expectedContext)
|
||||||
|
require.Len(t, statuses, 1)
|
||||||
|
assert.Equal(t, commitstatus.CommitStatusPending, statuses[0].State)
|
||||||
|
assert.Equal(t, "Waiting to run", statuses[0].Description)
|
||||||
|
assert.Equal(t, expectedTargetURL, statuses[0].TargetURL)
|
||||||
|
|
||||||
|
job.Status = actions_model.StatusRunning
|
||||||
|
require.NoError(t, createCommitStatus(t.Context(), repo, "push", commit.ID.String(), run, job))
|
||||||
|
|
||||||
|
statuses = findCommitStatusesForContext(t, repo.ID, commit.ID.String(), expectedContext)
|
||||||
|
require.Len(t, statuses, 2)
|
||||||
|
assert.Equal(t, "Waiting to run", statuses[0].Description)
|
||||||
|
assert.Equal(t, commitstatus.CommitStatusPending, statuses[1].State)
|
||||||
|
assert.Equal(t, "Has started running", statuses[1].Description)
|
||||||
|
assert.Equal(t, expectedTargetURL, statuses[1].TargetURL)
|
||||||
|
|
||||||
|
require.NoError(t, createCommitStatus(t.Context(), repo, "push", commit.ID.String(), run, job))
|
||||||
|
statuses = findCommitStatusesForContext(t, repo.ID, commit.ID.String(), expectedContext)
|
||||||
|
assert.Len(t, statuses, 2)
|
||||||
|
|
||||||
|
job.Status = actions_model.StatusSuccess
|
||||||
|
require.NoError(t, createCommitStatus(t.Context(), repo, "push", commit.ID.String(), run, job))
|
||||||
|
statuses = findCommitStatusesForContext(t, repo.ID, commit.ID.String(), expectedContext)
|
||||||
|
require.Len(t, statuses, 3)
|
||||||
|
assert.Equal(t, commitstatus.CommitStatusSuccess, statuses[2].State)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findCommitStatusesForContext(t *testing.T, repoID int64, sha, context string) []*git_model.CommitStatus {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var statuses []*git_model.CommitStatus
|
||||||
|
err := db.GetEngine(t.Context()).
|
||||||
|
Where("repo_id = ? AND sha = ? AND context = ?", repoID, sha, context).
|
||||||
|
Asc("`index`").
|
||||||
|
Find(&statuses)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return statuses
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user