fix: improve actions status icons and texts (#37206)

Action runs, jobs and steps have 8 statuses but the UI only showed 5
(from the commit status api) for the latter two. Align all 8 to GitHub
as closely as possible:

  - waiting — `octicon-circle` (hollow circle), gray
  - blocked — `octicon-blocked` (slashed circle), yellow
  - running — `gitea-running` (rotating spinner), yellow
  - cancelled — `octicon-stop` (gray), was `octicon-x` (red)

Descriptions also aligned with GitHub:

  - "Has started running" → "In progress"
  - "Has been cancelled" → "Cancelled after {dur}"
  - "Has been skipped" → "Skipped"

Fixes: https://github.com/go-gitea/gitea/issues/32228

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
This commit is contained in:
silverwind
2026-05-09 09:24:08 +02:00
committed by GitHub
parent a5d81d9ce2
commit ce089f498b
22 changed files with 248 additions and 70 deletions

View File

@@ -1,65 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"net/http"
"strings"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/services/context"
)
// IsArtifactV4 detects whether the artifact is likely from v4.
// V4 backend stores the files as a single combined zip file per artifact, and ensures ContentEncoding contains a slash
// (otherwise this uses application/zip instead of the custom mime type), which is not the case for the old backend.
func IsArtifactV4(art *actions_model.ActionArtifact) bool {
return strings.Contains(art.ContentEncodingOrType, "/")
}
func GetArtifactV4ServeDirectURL(art *actions_model.ActionArtifact, method string) (string, error) {
contentType := art.ContentEncodingOrType
u, err := storage.ActionsArtifacts.ServeDirectURL(art.StoragePath, art.ArtifactPath, method, &storage.ServeDirectOptions{ContentType: contentType})
if err != nil {
return "", err
}
return u.String(), nil
}
func DownloadArtifactV4ServeDirect(ctx *context.Base, art *actions_model.ActionArtifact) bool {
if !setting.Actions.ArtifactStorage.ServeDirect() {
return false
}
u, err := GetArtifactV4ServeDirectURL(art, ctx.Req.Method)
if err != nil {
log.Error("GetArtifactV4ServeDirectURL: %v", err)
return false
}
ctx.Redirect(u, http.StatusFound)
return true
}
func DownloadArtifactV4ReadStorage(ctx *context.Base, art *actions_model.ActionArtifact) error {
f, err := storage.ActionsArtifacts.Open(art.StoragePath)
if err != nil {
return err
}
defer f.Close()
httplib.ServeUserContentByFile(ctx.Req, ctx.Resp, f, httplib.ServeHeaderOptions{
Filename: art.ArtifactPath,
ContentType: art.ContentEncodingOrType, // v4 guarantees that the field is Content-Type
})
return nil
}
func DownloadArtifactV4(ctx *context.Base, art *actions_model.ActionArtifact) error {
if DownloadArtifactV4ServeDirect(ctx, art) {
return nil
}
return DownloadArtifactV4ReadStorage(ctx, art)
}

View File

@@ -0,0 +1,70 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"context"
"maps"
"slices"
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/modules/log"
)
// CommitActionsStatusMap maps CommitStatus.ID to the live ActionRunJob status
// for Gitea Actions rows.
type CommitActionsStatusMap map[int64]actions_model.Status
// IconStatus returns the action status name to route the icon through
// repo/icons/action_status, or "" when the row isn't from Gitea Actions.
func (m CommitActionsStatusMap) IconStatus(s *git_model.CommitStatus) string {
if status, ok := m[s.ID]; ok {
return status.String()
}
return ""
}
// GetCommitActionsStatusMap resolves the live ActionRunJob.Status for every
// CommitStatus row backed by Gitea Actions. Rows from other sources (external
// CIs, API) are left untouched and rendered from their stored State.
func GetCommitActionsStatusMap(ctx context.Context, statuses []*git_model.CommitStatus) CommitActionsStatusMap {
if len(statuses) == 0 {
return nil
}
statusByJobID := make(map[int64]*git_model.CommitStatus)
repoByID := make(map[int64]*repo_model.Repository)
for _, status := range statuses {
if status == nil || status.TargetURL == "" {
continue
}
if status.Repo == nil {
status.Repo = repoByID[status.RepoID]
}
// ParseGiteaActionsTargetURL lazy-loads status.Repo on miss; cache the
// outcome so later entries with the same RepoID skip that load.
_, jobID, ok := status.ParseGiteaActionsTargetURL(ctx)
repoByID[status.RepoID] = status.Repo
if ok {
statusByJobID[jobID] = status
}
}
if len(statusByJobID) == 0 {
return nil
}
jobs := make(map[int64]*actions_model.ActionRunJob, len(statusByJobID))
if err := db.GetEngine(ctx).In("id", slices.Collect(maps.Keys(statusByJobID))).Cols("id", "status").Find(&jobs); err != nil {
log.Error("db.Find: failed to find action run jobs: %v", err)
return nil
}
info := make(CommitActionsStatusMap, len(jobs))
for jobID, status := range statusByJobID {
if job, ok := jobs[jobID]; ok {
info[status.ID] = job.Status
}
}
return info
}

View File

@@ -0,0 +1,23 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package templates
import (
"context"
git_model "code.gitea.io/gitea/models/git"
actions_module "code.gitea.io/gitea/modules/actions"
)
type ActionsUtils struct {
ctx context.Context
}
func NewActionsUtils(ctx context.Context) *ActionsUtils {
return &ActionsUtils{ctx: ctx}
}
func (a *ActionsUtils) CommitStatusesToActionsStatuses(statuses []*git_model.CommitStatus) actions_module.CommitActionsStatusMap {
return actions_module.GetCommitActionsStatusMap(a.ctx, statuses)
}