mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-15 13:41:12 +00:00
fix: commit status reporting (backport #37372)
Backport of 8e85454a50
Fixes the issue that status report always shows "waiting to run" when
already running, by comparing state, targetURL, and description (not
just state) in the deduplication check.
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (claude-sonnet-4-5) <noreply@anthropic.com>
Agent-Logs-Url: https://github.com/go-gitea/gitea/sessions/1b315e9f-698c-4564-9ffa-aac1b9cb1692
Co-authored-by: silverwind <115237+silverwind@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
0280455356
commit
750a9079fb
@@ -8,7 +8,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
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 {
|
||||
runName = wfs[0].Name
|
||||
}
|
||||
ctxName := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)
|
||||
ctxName = strings.TrimSpace(ctxName) // git_model.NewCommitStatus also trims spaces
|
||||
ctxName := strings.TrimSpace(fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)) // git_model.NewCommitStatus also trims spaces
|
||||
state := toCommitStatus(job.Status)
|
||||
if statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commitID, db.ListOptionsAll); err == nil {
|
||||
for _, v := range statuses {
|
||||
if v.Context == ctxName {
|
||||
if v.State == state {
|
||||
// no need to update
|
||||
return nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
targetURL := fmt.Sprintf("%s/jobs/%d", run.Link(), job.ID)
|
||||
description := toCommitStatusDescription(job)
|
||||
|
||||
statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commitID, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetLatestCommitStatus: %w", err)
|
||||
}
|
||||
|
||||
var description 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:
|
||||
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"
|
||||
default:
|
||||
description = "Unknown status: " + strconv.Itoa(int(job.Status))
|
||||
for _, v := range statuses {
|
||||
if v.Context == ctxName {
|
||||
if v.State == state && v.TargetURL == targetURL && v.Description == description {
|
||||
return nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
creator := user_model.NewActionsUser()
|
||||
status := git_model.CommitStatus{
|
||||
SHA: commitID,
|
||||
TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), job.ID),
|
||||
TargetURL: targetURL,
|
||||
Description: description,
|
||||
Context: ctxName,
|
||||
CreatorID: creator.ID,
|
||||
State: state,
|
||||
CreatorID: creator.ID,
|
||||
}
|
||||
|
||||
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 {
|
||||
switch status {
|
||||
case actions_model.StatusSuccess:
|
||||
|
||||
88
services/actions/commit_status_test.go
Normal file
88
services/actions/commit_status_test.go
Normal file
@@ -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