Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6e35de7
Rework actions status icons
silverwind Apr 14, 2026
9e266a6
Use "In progress" for StatusRunning description
silverwind Apr 14, 2026
6716fca
Move action-status enrichment off the CommitStatus model
silverwind Apr 19, 2026
1489ee0
Merge branch 'main' into acticons
silverwind Apr 19, 2026
c140daf
Shorten doc comments on CommitStatusActionInfo
silverwind Apr 19, 2026
20c0ef8
Move commit_status template into icons/
silverwind Apr 19, 2026
7c310e8
Fix cancelled icon and align action status UI with GitHub
silverwind Apr 21, 2026
92e8974
Use maps.Copy for CommitStatusActionInfo merge
silverwind Apr 21, 2026
1f03083
Merge remote-tracking branch 'origin/main' into acticons
silverwind Apr 23, 2026
f67c18f
Keep CanRead/HideActionsURL blocks at callsites
silverwind Apr 23, 2026
264861b
Cover the commit-status tippy tooltip for in-flight action jobs
silverwind Apr 23, 2026
5b83e49
Move action-status enrichment into a template-side lookup
silverwind Apr 23, 2026
e70ecdb
Move enrichment helper into its natural home
silverwind Apr 23, 2026
b0a485b
Tighten statusinfo package and template hook
silverwind Apr 23, 2026
6cb3859
Move artifact-V4 download helpers into services/actions
silverwind Apr 23, 2026
d6cd40b
Collapse statusinfo subpackage into modules/actions
silverwind Apr 23, 2026
e9bb781
Merge remote-tracking branch 'origin/main' into acticons
silverwind Apr 24, 2026
c76ed0f
Simplify action-status lookup in pulls/status.tmpl
silverwind Apr 24, 2026
561e690
Use maps.Keys for jobIDs collection
silverwind Apr 24, 2026
59715a1
Address PR review feedback
silverwind Apr 27, 2026
bc99d63
Rename IconVariant to IconSuffix, drop ternary branching
silverwind Apr 27, 2026
266f9bf
Merge branch 'main' into acticons
silverwind Apr 27, 2026
e4dc0c4
Merge branch 'main' into acticons
silverwind Apr 30, 2026
95d29a7
Update templates/repo/icons/action_status.tmpl
wxiaoguang May 1, 2026
eddeb3f
Merge remote-tracking branch 'origin/main' into acticons
silverwind May 8, 2026
e83604f
Compute ActionInfo inside status_items.tmpl
silverwind May 8, 2026
887b405
Compute ActionInfo only in the merge box, trim comments
silverwind May 8, 2026
e18443f
Replace integration test with focused unit test
silverwind May 8, 2026
8d80fac
Pass icon-suffix to ActionRunStatus in attempt dropdown
silverwind May 8, 2026
e1730a8
icon variant
wxiaoguang May 8, 2026
cdfaee6
clean up ActionInfo
wxiaoguang May 8, 2026
2035df6
remove ActionsUtils
wxiaoguang May 8, 2026
7ea4e6e
fix test
wxiaoguang May 8, 2026
adf0206
fix
wxiaoguang May 8, 2026
9d39052
Apply suggestion from @silverwind
silverwind May 8, 2026
e8ccdff
Apply suggestion from @silverwind
silverwind May 8, 2026
ca1428d
Merge branch 'main' into acticons
bircni May 8, 2026
890af6c
Rename ActionRunStatus to ActionStatusIcon and ActionsRunStatus to Ac…
silverwind May 8, 2026
dfb38d7
Use plain quotes in ActionStatusIcon
silverwind May 8, 2026
9cec488
Revert unrelated pnpm-workspace.yaml drift
silverwind May 8, 2026
221c66c
Merge branch 'main' into acticons
silverwind May 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions models/git/commit_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"strings"
"time"

actions_model "code.gitea.io/gitea/models/actions"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
Expand Down Expand Up @@ -47,6 +48,10 @@ type CommitStatus struct {
Creator *user_model.User `xorm:"-"`
CreatorID int64

// ActionStatus is more granular than State, which maps waiting/blocked/running
// all to CommitStatusPending. Set by LoadActionStatuses.
ActionStatus actions_model.Status `xorm:"-"`

CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
Expand Down Expand Up @@ -213,6 +218,20 @@ func (status *CommitStatus) LocaleString(lang translation.Locale) string {
return lang.TrString("repo.commitstatus." + status.State.String())
}

// GetDescription is Description with an override for waiting/blocked/running,
// whose stored text is only written on the first transition into any of them.
func (status *CommitStatus) GetDescription() string {
switch status.ActionStatus {
case actions_model.StatusWaiting:
return "Waiting to run"
case actions_model.StatusRunning:
return "In progress"
case actions_model.StatusBlocked:
return "Blocked by required conditions"
}
return status.Description
}

// HideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions
func (status *CommitStatus) HideActionsURL(ctx context.Context) {
if _, ok := status.cutTargetURLGiteaActionsPrefix(ctx); ok {
Expand Down
6 changes: 6 additions & 0 deletions routers/web/repo/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
actions_service "code.gitea.io/gitea/services/actions"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
pull_service "code.gitea.io/gitea/services/pull"
Expand Down Expand Up @@ -73,6 +74,11 @@ func Branches(ctx *context.Context) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
var flatStatuses []*git_model.CommitStatus
for _, cs := range commitStatuses {
flatStatuses = append(flatStatuses, cs...)
}
actions_service.LoadActionStatuses(ctx, flatStatuses)

commitStatus := make(map[string]*git_model.CommitStatus)
for commitID, cs := range commitStatuses {
Expand Down
2 changes: 2 additions & 0 deletions routers/web/repo/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"
actions_service "code.gitea.io/gitea/services/actions"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
git_service "code.gitea.io/gitea/services/git"
Expand Down Expand Up @@ -387,6 +388,7 @@ func Diff(ctx *context.Context) {
if !ctx.Repo.CanRead(unit_model.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, statuses)
}
actions_service.LoadActionStatuses(ctx, statuses)

ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["CommitStatuses"] = statuses
Expand Down
6 changes: 3 additions & 3 deletions routers/web/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}

actions_service.LoadActionStatuses(ctx, commitStatuses)
if len(commitStatuses) != 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(commitStatuses)
Expand Down Expand Up @@ -432,7 +432,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git_s
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}

actions_service.LoadActionStatuses(ctx, commitStatuses)
statusCheckData.LatestCommitStatus = git_model.CalcCommitStatus(commitStatuses)
if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
Expand Down Expand Up @@ -508,7 +508,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git_s
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}

actions_service.LoadActionStatuses(ctx, commitStatuses)
runs, err := actions_service.GetRunsFromCommitStatuses(ctx, commitStatuses)
if err != nil {
ctx.ServerError("GetRunsFromCommitStatuses", err)
Expand Down
4 changes: 4 additions & 0 deletions routers/web/repo/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/feed"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
actions_service "code.gitea.io/gitea/services/actions"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/forms"
Expand Down Expand Up @@ -102,6 +103,7 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
canReadActions := ctx.Repo.CanRead(unit.TypeActions)

releaseInfos := make([]*ReleaseInfo, 0, len(releases))
var flatStatuses []*git_model.CommitStatus
for _, r := range releases {
if r.Publisher, ok = cacheUsers[r.PublisherID]; !ok {
r.Publisher, err = user_model.GetPossibleUserByID(ctx, r.PublisherID)
Expand Down Expand Up @@ -141,10 +143,12 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)

info.CommitStatus = git_model.CalcCommitStatus(statuses)
info.CommitStatuses = statuses
flatStatuses = append(flatStatuses, statuses...)
}

releaseInfos = append(releaseInfos, info)
}
actions_service.LoadActionStatuses(ctx, flatStatuses)

return releaseInfos, nil
}
Expand Down
2 changes: 2 additions & 0 deletions routers/web/repo/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
actions_service "code.gitea.io/gitea/services/actions"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
Expand Down Expand Up @@ -143,6 +144,7 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
if !ctx.Repo.CanRead(unit_model.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, statuses)
}
actions_service.LoadActionStatuses(ctx, statuses)

ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["LatestCommitStatuses"] = statuses
Expand Down
43 changes: 42 additions & 1 deletion services/actions/commit_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,47 @@ func GetRunsFromCommitStatuses(ctx context.Context, statuses []*git_model.Commit
return runs, nil
}

// LoadActionStatuses sets CommitStatus.ActionStatus from the matching ActionRunJob.
func LoadActionStatuses(ctx context.Context, statuses []*git_model.CommitStatus) {
if len(statuses) == 0 {
return
}
statusByJobID := make(map[int64]*git_model.CommitStatus, len(statuses))
// Cache repo per RepoID across ParseGiteaActionsTargetURL lazy-loads,
// same as CommitStatusesHideActionsURL.
repoCache := make(map[int64]*repo_model.Repository)
for _, status := range statuses {
if status == nil {
continue
}
if status.Repo == nil {
status.Repo = repoCache[status.RepoID]
}
_, jobID, ok := status.ParseGiteaActionsTargetURL(ctx)
repoCache[status.RepoID] = status.Repo
if ok {
statusByJobID[jobID] = status
}
}
if len(statusByJobID) == 0 {
return
}
jobIDs := make([]int64, 0, len(statusByJobID))
for id := range statusByJobID {
jobIDs = append(jobIDs, id)
}
jobs := make(map[int64]*actions_model.ActionRunJob, len(jobIDs))
if err := db.GetEngine(ctx).In("id", jobIDs).Cols("id", "status").Find(&jobs); err != nil {
log.Error("LoadActionStatuses: find action run jobs: %v", err)
return
}
for jobID, status := range statusByJobID {
if job, ok := jobs[jobID]; ok {
status.ActionStatus = job.Status
}
}
}

func getCommitStatusEventNameAndCommitID(run *actions_model.ActionRun) (event, commitID string, _ error) {
switch run.Event {
case webhook_module.HookEventPush:
Expand Down Expand Up @@ -172,7 +213,7 @@ func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event,
case actions_model.StatusSkipped:
description = "Has been skipped"
case actions_model.StatusRunning:
description = "Has started running"
description = "In progress"
case actions_model.StatusWaiting:
description = "Waiting to run"
case actions_model.StatusBlocked:
Expand Down
4 changes: 4 additions & 0 deletions services/git/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
actions_service "code.gitea.io/gitea/services/actions"
asymkey_service "code.gitea.io/gitea/services/asymkey"
)

Expand Down Expand Up @@ -72,6 +73,7 @@ func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo
// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state
func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.SignCommit, repo *repo_model.Repository) ([]*git_model.SignCommitWithStatuses, error) {
newCommits := make([]*git_model.SignCommitWithStatuses, 0, len(oldCommits))
var flatStatuses []*git_model.CommitStatus

for _, c := range oldCommits {
commit := &git_model.SignCommitWithStatuses{
Expand All @@ -85,6 +87,8 @@ func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.Sig
commit.Statuses = statuses
commit.Status = git_model.CalcCommitStatus(statuses)
newCommits = append(newCommits, commit)
flatStatuses = append(flatStatuses, statuses...)
}
actions_service.LoadActionStatuses(ctx, flatStatuses)
return newCommits, nil
}
6 changes: 6 additions & 0 deletions services/pull/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
actions_service "code.gitea.io/gitea/services/actions"
git_service "code.gitea.io/gitea/services/git"
issue_service "code.gitea.io/gitea/services/issue"
notify_service "code.gitea.io/gitea/services/notify"
Expand Down Expand Up @@ -968,6 +969,11 @@ func GetIssuesAllCommitStatus(ctx context.Context, issues issues_model.IssueList
res[issue.PullRequest.ID] = statuses
lastRes[issue.PullRequest.ID] = lastStatus
}
var flatStatuses []*git_model.CommitStatus
for _, s := range res {
flatStatuses = append(flatStatuses, s...)
}
actions_service.LoadActionStatuses(ctx, flatStatuses)
return res, lastRes, nil
}

Expand Down
4 changes: 3 additions & 1 deletion templates/repo/actions/runs_list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
{{range $run := .Runs}}
<div class="flex-item tw-items-center">
<div class="flex-item-leading">
{{template "repo/actions/status" (dict "status" $run.Status.String)}}
<span data-tooltip-content="{{ctx.Locale.Tr (printf "actions.status.%s" $run.Status.String)}}">
{{template "repo/icons/action_status" (dict "status" $run.Status.String "fill" true)}}
</span>
</div>
<div class="flex-item-main">
<a class="flex-item-title" title="{{$run.Title}}" href="{{$run.Link}}">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<!-- This template should be kept the same as web_src/js/components/ActionRunStatus.vue
Please also update the vue file above if this template is modified.
action status accepted: success, skipped, waiting, blocked, running, failure, cancelled, unknown
-->
{{/* Template Attributes:
Comment thread
silverwind marked this conversation as resolved.
Outdated
* status: one of success, skipped, waiting, blocked, running, failure, cancelled, unknown
* size: icon size in pixels (default 16)
* className: additional CSS classes
Comment thread
silverwind marked this conversation as resolved.
Outdated
* fill: use filled-circle icons for success/failure (default: bare icons matching repo/commit_status)

Keep this template in sync with web_src/js/components/ActionRunStatus.vue.
*/}}
{{- $size := Iif .size .size 16 -}}
{{- $className := Iif .className .className "" -}}
<span data-tooltip-content="{{ctx.Locale.Tr (printf "actions.status.%s" .status)}}">
{{if eq .status "success"}}
{{svg "octicon-check-circle-fill" $size (printf "tw-text-green %s" $className)}}
{{svg (Iif .fill "octicon-check-circle-fill" "octicon-check") $size (printf "tw-text-green %s" $className)}}
{{else if eq .status "skipped"}}
{{svg "octicon-skip" $size (printf "tw-text-text-light %s" $className)}}
{{else if eq .status "cancelled"}}
Expand All @@ -18,6 +21,5 @@
{{else if eq .status "running"}}
{{svg "gitea-running" $size (printf "tw-text-yellow rotate-clockwise %s" $className)}}
{{else}}{{/*failure, unknown*/}}
{{svg "octicon-x-circle-fill" $size (printf "tw-text-red %s" $className)}}
{{svg (Iif .fill "octicon-x-circle-fill" "octicon-x") $size (printf "tw-text-red %s" $className)}}
{{end}}
</span>
8 changes: 6 additions & 2 deletions templates/repo/pulls/status.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@
<div class="commit-status-list">
{{range .CommitStatuses}}
<div class="commit-status-item">
{{template "repo/commit_status" .}}
<div class="status-context gt-ellipsis">{{.Context}} <span class="tw-text-text-light-2">{{.Description}}</span></div>
{{if .ActionStatus.IsUnknown}}
{{template "repo/commit_status" .}}
{{else}}
{{template "repo/icons/action_status" (dict "status" .ActionStatus.String "size" 18 "className" "commit-status icon")}}
{{end}}
<div class="status-context gt-ellipsis">{{.Context}} <span class="tw-text-text-light-2">{{.GetDescription}}</span></div>
<div class="ui status-details">
{{if and $statusCheckData $statusCheckData.IsContextRequired}}
{{if (call $statusCheckData.IsContextRequired .Context)}}<div class="ui label">{{ctx.Locale.Tr "repo.pulls.status_checks_requested"}}</div>{{end}}
Expand Down
48 changes: 48 additions & 0 deletions tests/integration/actions_trigger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/timeutil"
webhook_module "code.gitea.io/gitea/modules/webhook"
actions_service "code.gitea.io/gitea/services/actions"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
release_service "code.gitea.io/gitea/services/release"
Expand Down Expand Up @@ -1806,3 +1807,50 @@ jobs:
runner.fetchNoTask(t)
})
}

// Verify LoadActionStatuses surfaces the live ActionRunJob.Status after a
// Waiting→Running transition (the dedup on State keeps the initial row).
func TestActionsCommitStatusRunning(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo, err := repo_service.CreateRepository(t.Context(), user2, user2, repo_service.CreateRepoOptions{
Name: "repo-cs-running", AutoInit: true, Readme: "Default", DefaultBranch: "main",
})
require.NoError(t, err)

gitRepo, err := gitrepo.OpenRepository(t.Context(), repo)
require.NoError(t, err)
defer gitRepo.Close()
commit, err := gitRepo.GetBranchCommit("main")
require.NoError(t, err)
sha := commit.ID.String()

payload, err := json.Marshal(&api.PushPayload{HeadCommit: &api.PayloadCommit{ID: sha}})
require.NoError(t, err)
run := &actions_model.ActionRun{
RepoID: repo.ID, OwnerID: user2.ID, WorkflowID: "test.yml",
CommitSHA: sha, Event: webhook_module.HookEventPush, TriggerEvent: "push",
EventPayload: string(payload),
}
require.NoError(t, db.Insert(t.Context(), run))
job := &actions_model.ActionRunJob{
RunID: run.ID, RepoID: repo.ID, OwnerID: user2.ID, Name: "test",
Status: actions_model.StatusWaiting,
}
require.NoError(t, db.Insert(t.Context(), job))

actions_service.CreateCommitStatusForRunJobs(t.Context(), run, job)
job.Status = actions_model.StatusRunning
actions_service.CreateCommitStatusForRunJobs(t.Context(), run, job)

statuses, err := git_model.GetLatestCommitStatus(t.Context(), repo.ID, sha, db.ListOptionsAll)
require.NoError(t, err)
require.Len(t, statuses, 1)
assert.Equal(t, commitstatus.CommitStatusPending, statuses[0].State)
// Dedup on State only → stored Description stays at the Waiting text.
assert.Equal(t, "Waiting to run", statuses[0].Description)

actions_service.LoadActionStatuses(t.Context(), statuses)
assert.Equal(t, actions_model.StatusRunning, statuses[0].ActionStatus)
})
}
2 changes: 1 addition & 1 deletion web_src/js/components/ActionRunStatus.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- This vue should be kept the same as templates/repo/actions/status.tmpl
<!-- This vue should be kept the same as templates/repo/icons/action_status.tmpl
Please also update the template file above if this vue is modified.
action status accepted: success, skipped, waiting, blocked, running, failure, cancelled, unknown
-->
Expand Down
Loading