feat: Add workflow dependencies visualization (#36248)

Add workflow dependencies visualization

Related to #26062

This PR adds an interactive visualization component that displays job
dependencies in Gitea Actions workflow runs. It helps users understand
complex pipeline structures at a glance, addressing the difficulty of
comprehending dependency chains in current Gitea UI.

---------

Signed-off-by: Semenets V. Pavel <p.semenets@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Semenets V. Pavel
2026-02-23 16:11:33 +03:00
committed by GitHub
parent 427954ba6e
commit a8505269ca
11 changed files with 1064 additions and 64 deletions

View File

@@ -12,6 +12,7 @@ import (
"time"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/repo/actions"
@@ -58,8 +59,8 @@ func generateMockStepsLog(logCur actions.LogCursor, opts generateMockStepsLogOpt
}
func MockActionsView(ctx *context.Context) {
ctx.Data["RunID"] = ctx.PathParam("run")
ctx.Data["JobID"] = ctx.PathParam("job")
ctx.Data["RunIndex"] = ctx.PathParam("run")
ctx.Data["JobIndex"] = ctx.PathParam("job")
ctx.HTML(http.StatusOK, "devtest/repo-action-view")
}
@@ -69,6 +70,7 @@ func MockActionsRunsJobs(ctx *context.Context) {
req := web.GetForm(ctx).(*actions.ViewRequest)
resp := &actions.ViewResponse{}
resp.State.Run.TitleHTML = `mock run title <a href="/">link</a>`
resp.State.Run.Link = setting.AppSubURL + "/devtest/repo-action-view/runs/" + strconv.FormatInt(runID, 10)
resp.State.Run.Status = actions_model.StatusRunning.String()
resp.State.Run.CanCancel = runID == 10
resp.State.Run.CanApprove = runID == 20
@@ -112,6 +114,7 @@ func MockActionsRunsJobs(ctx *context.Context) {
resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
ID: runID * 10,
JobID: "job-100",
Name: "job 100",
Status: actions_model.StatusRunning.String(),
CanRerun: true,
@@ -119,17 +122,21 @@ func MockActionsRunsJobs(ctx *context.Context) {
})
resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
ID: runID*10 + 1,
JobID: "job-101",
Name: "job 101",
Status: actions_model.StatusWaiting.String(),
CanRerun: false,
Duration: "2h",
Needs: []string{"job-100"},
})
resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
ID: runID*10 + 2,
JobID: "job-102",
Name: "job 102",
Status: actions_model.StatusFailure.String(),
CanRerun: false,
Duration: "3h",
Needs: []string{"job-100", "job-101"},
})
var mockLogOptions []generateMockStepsLogOptions

View File

@@ -143,11 +143,13 @@ type ViewResponse struct {
}
type ViewJob struct {
ID int64 `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
CanRerun bool `json:"canRerun"`
Duration string `json:"duration"`
ID int64 `json:"id"`
JobID string `json:"jobId,omitempty"`
Name string `json:"name"`
Status string `json:"status"`
CanRerun bool `json:"canRerun"`
Duration string `json:"duration"`
Needs []string `json:"needs,omitempty"`
}
type ViewCommit struct {
@@ -248,10 +250,12 @@ func ViewPost(ctx *context_module.Context) {
for _, v := range jobs {
resp.State.Run.Jobs = append(resp.State.Run.Jobs, &ViewJob{
ID: v.ID,
JobID: v.JobID,
Name: v.Name,
Status: v.Status.String(),
CanRerun: resp.State.Run.CanRerun,
Duration: v.Duration().String(),
Needs: v.Needs,
})
}

View File

@@ -1675,7 +1675,7 @@ func registerWebRoutes(m *web.Router) {
m.Any("/mail-preview", devtest.MailPreview)
m.Any("/mail-preview/*", devtest.MailPreviewRender)
m.Any("/{sub}", devtest.TmplCommon)
m.Get("/repo-action-view/{run}/{job}", devtest.MockActionsView)
m.Get("/repo-action-view/runs/{run}/jobs/{job}", devtest.MockActionsView)
m.Post("/actions-mock/runs/{run}/jobs/{job}", web.Bind(actions.ViewRequest{}), devtest.MockActionsRunsJobs)
})
}