[GITEA] allow viewing the latest Action Run on the web

Similar to how some other parts of the web UI support a `/latest` path
to directly go to the latest of a certain thing, let the Actions web UI
do the same: `/{owner}/{repo}/actions/runs/latest` will redirect to the
latest run, if there's one available.

Fixes gitea#27991.

Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
(cherry picked from commit f67ccef1dd3146b0b942a94e2482b37595180e91)

Code cleanup in the actions.ViewLatest route handler

Based on feedback received after the feature was merged, use
`ctx.NotFound` and `ctx.ServerError`, and drop the use of the
unnecessary `ctx.Written()`.

Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
(cherry picked from commit 74e42da5630f9148faaf6b03bf1ac5724fa86b25)
(cherry picked from commit f7535a1cef96ce0589f37907f88b024cd095d0ac)
(cherry picked from commit 1a90cd37c31a1b9c770d6d79a4663ed8d67845c0)
(cherry picked from commit d86d71340afd372e5b5083d5563c2f5b48d975e6)
(cherry picked from commit 9e5cce1afccebcd6146e5e0d364bfdbb840b5276)
(cherry picked from commit 2013fb3fab5e23d0088434d835411f26a3fd9905)
(cherry picked from commit 88b9d21d1194abd133c3b4cbaa19792da433cb43)
(cherry picked from commit 72c020298eebcb0c90e23e7ff35e37be867afc44)
(cherry picked from commit 6525f730dfdd7cb412762d9e30348801335d17ee)
This commit is contained in:
Gergely Nagy 2023-12-08 13:41:48 +01:00 committed by Earl Warren
parent 533c87da65
commit fa0759962b
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
4 changed files with 130 additions and 11 deletions

View file

@ -312,6 +312,17 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
return commiter.Commit() return commiter.Commit()
} }
func GetLatestRun(ctx context.Context, repoID int64) (*ActionRun, error) {
var run ActionRun
has, err := db.GetEngine(ctx).Where("repo_id=?", repoID).OrderBy("id DESC").Limit(1).Get(&run)
if err != nil {
return nil, err
} else if !has {
return nil, fmt.Errorf("latest run: %w", util.ErrNotExist)
}
return &run, nil
}
func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) { func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
var run ActionRun var run ActionRun
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run) has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run)

View file

@ -46,6 +46,20 @@ func View(ctx *context_module.Context) {
ctx.HTML(http.StatusOK, tplViewActions) ctx.HTML(http.StatusOK, tplViewActions)
} }
func ViewLatest(ctx *context_module.Context) {
run, err := actions_model.GetLatestRun(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.NotFound("GetLatestRun", err)
return
}
err = run.LoadAttributes(ctx)
if err != nil {
ctx.ServerError("LoadAttributes", err)
return
}
ctx.Redirect(run.HTMLURL(), http.StatusTemporaryRedirect)
}
type ViewRequest struct { type ViewRequest struct {
LogCursors []struct { LogCursors []struct {
Step int `json:"step"` Step int `json:"step"`

View file

@ -1347,22 +1347,25 @@ func registerRoutes(m *web.Route) {
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile) m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile) m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
m.Group("/runs/{run}", func() { m.Group("/runs", func() {
m.Combo(""). m.Get("/latest", actions.ViewLatest)
Get(actions.View). m.Group("/{run}", func() {
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
m.Group("/jobs/{job}", func() {
m.Combo(""). m.Combo("").
Get(actions.View). Get(actions.View).
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost) Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
m.Group("/jobs/{job}", func() {
m.Combo("").
Get(actions.View).
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
m.Get("/logs", actions.Logs)
})
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
m.Post("/artifacts", actions.ArtifactsView)
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
m.Get("/logs", actions.Logs)
}) })
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
m.Post("/artifacts", actions.ArtifactsView)
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
}) })
}, reqRepoActionsReader, actions.MustEnableActions) }, reqRepoActionsReader, actions.MustEnableActions)

View file

@ -0,0 +1,91 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"fmt"
"net/http"
"net/url"
"strings"
"testing"
"time"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
repo_service "code.gitea.io/gitea/services/repository"
files_service "code.gitea.io/gitea/services/repository/files"
"github.com/stretchr/testify/assert"
)
func TestActionsWebRouteLatestRun(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// create the repo
repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
Name: "actions-latest",
Description: "test /actions/runs/latest",
AutoInit: true,
Gitignores: "Go",
License: "MIT",
Readme: "Default",
DefaultBranch: "main",
IsPrivate: false,
})
assert.NoError(t, err)
assert.NotEmpty(t, repo)
// enable actions
err = repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
RepoID: repo.ID,
Type: unit_model.TypeActions,
}}, nil)
assert.NoError(t, err)
// add workflow file to the repo
addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
Files: []*files_service.ChangeRepoFile{
{
Operation: "create",
TreePath: ".gitea/workflows/pr.yml",
ContentReader: strings.NewReader("name: test\non:\n push:\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"),
},
},
Message: "add workflow",
OldBranch: "main",
NewBranch: "main",
Author: &files_service.IdentityOptions{
Name: user2.Name,
Email: user2.Email,
},
Committer: &files_service.IdentityOptions{
Name: user2.Name,
Email: user2.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
Committer: time.Now(),
},
})
assert.NoError(t, err)
assert.NotEmpty(t, addWorkflowToBaseResp)
// a run has been created
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
// Hit the `/actions/runs/latest` route
req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/runs/latest", repo.HTMLURL()))
resp := MakeRequest(t, req, http.StatusTemporaryRedirect)
// Verify that it redirects to the run we just created
expectedURI := fmt.Sprintf("%s/actions/runs/1", repo.HTMLURL())
assert.Equal(t, expectedURI, resp.Header().Get("Location"))
})
}