Skip to content

Commit

Permalink
Refactor RepoActionView.vue, add ::group:: support (#32713)
Browse files Browse the repository at this point in the history
1. make it able to "force reload", then the previous pending request
won't block the new request
2. make it support `::group::`
3. add some TS types (but there are still many variables untyped, this
PR is large enough, the remaining types could be added in the future)
  • Loading branch information
wxiaoguang authored Dec 6, 2024
1 parent ff14ada commit f7f68e4
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 156 deletions.
108 changes: 108 additions & 0 deletions routers/web/devtest/mock_actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package devtest

import (
"fmt"
mathRand "math/rand/v2"
"net/http"
"strings"
"time"

actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/repo/actions"
"code.gitea.io/gitea/services/context"
)

func generateMockStepsLog(logCur actions.LogCursor) (stepsLog []*actions.ViewStepLog) {
mockedLogs := []string{
"::group::test group for: step={step}, cursor={cursor}",
"in group msg for: step={step}, cursor={cursor}",
"in group msg for: step={step}, cursor={cursor}",
"in group msg for: step={step}, cursor={cursor}",
"::endgroup::",
"message for: step={step}, cursor={cursor}",
"message for: step={step}, cursor={cursor}",
"message for: step={step}, cursor={cursor}",
"message for: step={step}, cursor={cursor}",
"message for: step={step}, cursor={cursor}",
}
cur := logCur.Cursor // usually the cursor is the "file offset", but here we abuse it as "line number" to make the mock easier, intentionally
for i := 0; i < util.Iif(logCur.Step == 0, 3, 1); i++ {
logStr := mockedLogs[int(cur)%len(mockedLogs)]
cur++
logStr = strings.ReplaceAll(logStr, "{step}", fmt.Sprintf("%d", logCur.Step))
logStr = strings.ReplaceAll(logStr, "{cursor}", fmt.Sprintf("%d", cur))
stepsLog = append(stepsLog, &actions.ViewStepLog{
Step: logCur.Step,
Cursor: cur,
Started: time.Now().Unix() - 1,
Lines: []*actions.ViewStepLogLine{
{Index: cur, Message: logStr, Timestamp: float64(time.Now().UnixNano()) / float64(time.Second)},
},
})
}
return stepsLog
}

func MockActionsRunsJobs(ctx *context.Context) {
req := web.GetForm(ctx).(*actions.ViewRequest)

resp := &actions.ViewResponse{}
resp.Artifacts = append(resp.Artifacts, &actions.ArtifactsViewItem{
Name: "artifact-a",
Size: 100 * 1024,
Status: "expired",
})
resp.Artifacts = append(resp.Artifacts, &actions.ArtifactsViewItem{
Name: "artifact-b",
Size: 1024 * 1024,
Status: "completed",
})
resp.State.CurrentJob.Steps = append(resp.State.CurrentJob.Steps, &actions.ViewJobStep{
Summary: "step 0 (mock slow)",
Duration: time.Hour.String(),
Status: actions_model.StatusRunning.String(),
})
resp.State.CurrentJob.Steps = append(resp.State.CurrentJob.Steps, &actions.ViewJobStep{
Summary: "step 1 (mock fast)",
Duration: time.Hour.String(),
Status: actions_model.StatusRunning.String(),
})
resp.State.CurrentJob.Steps = append(resp.State.CurrentJob.Steps, &actions.ViewJobStep{
Summary: "step 2 (mock error)",
Duration: time.Hour.String(),
Status: actions_model.StatusRunning.String(),
})
if len(req.LogCursors) == 0 {
ctx.JSON(http.StatusOK, resp)
return
}

resp.Logs.StepsLog = []*actions.ViewStepLog{}
doSlowResponse := false
doErrorResponse := false
for _, logCur := range req.LogCursors {
if !logCur.Expanded {
continue
}
doSlowResponse = doSlowResponse || logCur.Step == 0
doErrorResponse = doErrorResponse || logCur.Step == 2
resp.Logs.StepsLog = append(resp.Logs.StepsLog, generateMockStepsLog(logCur)...)
}
if doErrorResponse {
if mathRand.Float64() > 0.5 {
ctx.Error(http.StatusInternalServerError, "devtest mock error response")
return
}
}
if doSlowResponse {
time.Sleep(time.Duration(3000) * time.Millisecond)
} else {
time.Sleep(time.Duration(100) * time.Millisecond) // actually, frontend reload every 1 second, any smaller delay is fine
}
ctx.JSON(http.StatusOK, resp)
}
98 changes: 46 additions & 52 deletions routers/web/repo/actions/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,25 @@ func View(ctx *context_module.Context) {
ctx.HTML(http.StatusOK, tplViewActions)
}

type LogCursor struct {
Step int `json:"step"`
Cursor int64 `json:"cursor"`
Expanded bool `json:"expanded"`
}

type ViewRequest struct {
LogCursors []struct {
Step int `json:"step"`
Cursor int64 `json:"cursor"`
Expanded bool `json:"expanded"`
} `json:"logCursors"`
LogCursors []LogCursor `json:"logCursors"`
}

type ArtifactsViewItem struct {
Name string `json:"name"`
Size int64 `json:"size"`
Status string `json:"status"`
}

type ViewResponse struct {
Artifacts []*ArtifactsViewItem `json:"artifacts"`

State struct {
Run struct {
Link string `json:"link"`
Expand Down Expand Up @@ -146,6 +156,25 @@ type ViewStepLogLine struct {
Timestamp float64 `json:"timestamp"`
}

func getActionsViewArtifacts(ctx context.Context, repoID, runIndex int64) (artifactsViewItems []*ArtifactsViewItem, err error) {
run, err := actions_model.GetRunByIndex(ctx, repoID, runIndex)
if err != nil {
return nil, err
}
artifacts, err := actions_model.ListUploadedArtifactsMeta(ctx, run.ID)
if err != nil {
return nil, err
}
for _, art := range artifacts {
artifactsViewItems = append(artifactsViewItems, &ArtifactsViewItem{
Name: art.ArtifactName,
Size: art.FileSize,
Status: util.Iif(art.Status == actions_model.ArtifactStatusExpired, "expired", "completed"),
})
}
return artifactsViewItems, nil
}

func ViewPost(ctx *context_module.Context) {
req := web.GetForm(ctx).(*ViewRequest)
runIndex := getRunIndex(ctx)
Expand All @@ -157,11 +186,19 @@ func ViewPost(ctx *context_module.Context) {
}
run := current.Run
if err := run.LoadAttributes(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
ctx.ServerError("run.LoadAttributes", err)
return
}

var err error
resp := &ViewResponse{}
resp.Artifacts, err = getActionsViewArtifacts(ctx, ctx.Repo.Repository.ID, runIndex)
if err != nil {
if !errors.Is(err, util.ErrNotExist) {
ctx.ServerError("getActionsViewArtifacts", err)
return
}
}

resp.State.Run.Title = run.Title
resp.State.Run.Link = run.Link()
Expand Down Expand Up @@ -205,12 +242,12 @@ func ViewPost(ctx *context_module.Context) {
var err error
task, err = actions_model.GetTaskByID(ctx, current.TaskID)
if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
ctx.ServerError("actions_model.GetTaskByID", err)
return
}
task.Job = current
if err := task.LoadAttributes(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
ctx.ServerError("task.LoadAttributes", err)
return
}
}
Expand Down Expand Up @@ -278,7 +315,7 @@ func ViewPost(ctx *context_module.Context) {
offset := task.LogIndexes[index]
logRows, err := actions.ReadLogs(ctx, task.LogInStorage, task.LogFilename, offset, length)
if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
ctx.ServerError("actions.ReadLogs", err)
return
}

Expand Down Expand Up @@ -555,49 +592,6 @@ func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (*actions
return jobs[0], jobs
}

type ArtifactsViewResponse struct {
Artifacts []*ArtifactsViewItem `json:"artifacts"`
}

type ArtifactsViewItem struct {
Name string `json:"name"`
Size int64 `json:"size"`
Status string `json:"status"`
}

func ArtifactsView(ctx *context_module.Context) {
runIndex := getRunIndex(ctx)
run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
ctx.Error(http.StatusNotFound, err.Error())
return
}
ctx.Error(http.StatusInternalServerError, err.Error())
return
}
artifacts, err := actions_model.ListUploadedArtifactsMeta(ctx, run.ID)
if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
return
}
artifactsResponse := ArtifactsViewResponse{
Artifacts: make([]*ArtifactsViewItem, 0, len(artifacts)),
}
for _, art := range artifacts {
status := "completed"
if art.Status == actions_model.ArtifactStatusExpired {
status = "expired"
}
artifactsResponse.Artifacts = append(artifactsResponse.Artifacts, &ArtifactsViewItem{
Name: art.ArtifactName,
Size: art.FileSize,
Status: status,
})
}
ctx.JSON(http.StatusOK, artifactsResponse)
}

func ArtifactsDeleteView(ctx *context_module.Context) {
if !ctx.Repo.CanWrite(unit.TypeActions) {
ctx.Error(http.StatusForbidden, "no permission")
Expand Down
10 changes: 6 additions & 4 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1424,7 +1424,6 @@ func registerRoutes(m *web.Router) {
})
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
m.Get("/artifacts", actions.ArtifactsView)
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
Expand Down Expand Up @@ -1626,9 +1625,12 @@ func registerRoutes(m *web.Router) {
}

if !setting.IsProd {
m.Any("/devtest", devtest.List)
m.Any("/devtest/fetch-action-test", devtest.FetchActionTest)
m.Any("/devtest/{sub}", devtest.Tmpl)
m.Group("/devtest", func() {
m.Any("", devtest.List)
m.Any("/fetch-action-test", devtest.FetchActionTest)
m.Any("/{sub}", devtest.Tmpl)
m.Post("/actions-mock/runs/{run}/jobs/{job}", web.Bind(actions.ViewRequest{}), devtest.MockActionsRunsJobs)
})
}

m.NotFound(func(w http.ResponseWriter, req *http.Request) {
Expand Down
30 changes: 30 additions & 0 deletions templates/devtest/repo-action-view.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{{template "base/head" .}}
<div class="page-content">
<div id="repo-action-view"
data-run-index="1"
data-job-index="2"
data-actions-url="{{AppSubUrl}}/devtest/actions-mock"
data-locale-approve="approve"
data-locale-cancel="cancel"
data-locale-rerun="re-run"
data-locale-rerun-all="re-run all"
data-locale-runs-scheduled="scheduled"
data-locale-runs-commit="commit"
data-locale-runs-pushed-by="pushed by"
data-locale-status-unknown="unknown"
data-locale-status-waiting="waiting"
data-locale-status-running="running"
data-locale-status-success="success"
data-locale-status-failure="failure"
data-locale-status-cancelled="cancelled"
data-locale-status-skipped="skipped"
data-locale-status-blocked="blocked"
data-locale-artifacts-title="artifacts"
data-locale-confirm-delete-artifact="confirm delete artifact"
data-locale-show-timestamps="show timestamps"
data-locale-show-log-seconds="show log seconds"
data-locale-show-full-screen="show full screen"
data-locale-download-logs="download logs"
></div>
</div>
{{template "base/footer" .}}
Loading

0 comments on commit f7f68e4

Please sign in to comment.