Skip to content

Commit 5628233

Browse files
committed
Extract actions log download function
1 parent 9ec87c7 commit 5628233

File tree

6 files changed

+147
-150
lines changed

6 files changed

+147
-150
lines changed

models/actions/run_job.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"code.gitea.io/gitea/models/db"
13+
repo_model "code.gitea.io/gitea/models/repo"
1314
"code.gitea.io/gitea/modules/timeutil"
1415
"code.gitea.io/gitea/modules/util"
1516

@@ -19,11 +20,12 @@ import (
1920
// ActionRunJob represents a job of a run
2021
type ActionRunJob struct {
2122
ID int64
22-
RunID int64 `xorm:"index"`
23-
Run *ActionRun `xorm:"-"`
24-
RepoID int64 `xorm:"index"`
25-
OwnerID int64 `xorm:"index"`
26-
CommitSHA string `xorm:"index"`
23+
RunID int64 `xorm:"index"`
24+
Run *ActionRun `xorm:"-"`
25+
RepoID int64 `xorm:"index"`
26+
Repo *repo_model.Repository `xorm:"-"`
27+
OwnerID int64 `xorm:"index"`
28+
CommitSHA string `xorm:"index"`
2729
IsForkPullRequest bool
2830
Name string `xorm:"VARCHAR(255)"`
2931
Attempt int64
@@ -83,7 +85,7 @@ func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
8385
return &job, nil
8486
}
8587

86-
func GetRunJobsByRunID(ctx context.Context, runID int64) ([]*ActionRunJob, error) {
88+
func GetRunJobsByRunID(ctx context.Context, runID int64) (ActionJobList, error) {
8789
var jobs []*ActionRunJob
8890
if err := db.GetEngine(ctx).Where("run_id=?", runID).OrderBy("id").Find(&jobs); err != nil {
8991
return nil, err

models/actions/run_job_list.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88

99
"code.gitea.io/gitea/models/db"
10+
repo_model "code.gitea.io/gitea/models/repo"
1011
"code.gitea.io/gitea/modules/container"
1112
"code.gitea.io/gitea/modules/timeutil"
1213

@@ -21,7 +22,33 @@ func (jobs ActionJobList) GetRunIDs() []int64 {
2122
})
2223
}
2324

25+
func (jobs ActionJobList) LoadRepos(ctx context.Context) error {
26+
repoIDs := container.FilterSlice(jobs, func(j *ActionRunJob) (int64, bool) {
27+
return j.RepoID, j.RepoID != 0
28+
})
29+
if len(repoIDs) == 0 {
30+
return nil
31+
}
32+
33+
repos := make(map[int64]*repo_model.Repository, len(repoIDs))
34+
if err := db.GetEngine(ctx).In("id", repoIDs).Find(&repos); err != nil {
35+
return err
36+
}
37+
for _, j := range jobs {
38+
if j.RepoID > 0 && j.Repo == nil {
39+
j.Repo = repos[j.RepoID]
40+
}
41+
}
42+
return nil
43+
}
44+
2445
func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
46+
if withRepo {
47+
if err := jobs.LoadRepos(ctx); err != nil {
48+
return err
49+
}
50+
}
51+
2552
runIDs := jobs.GetRunIDs()
2653
runs := make(map[int64]*ActionRun, len(runIDs))
2754
if err := db.GetEngine(ctx).In("id", runIDs).Find(&runs); err != nil {
@@ -30,15 +57,9 @@ func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
3057
for _, j := range jobs {
3158
if j.RunID > 0 && j.Run == nil {
3259
j.Run = runs[j.RunID]
60+
j.Run.Repo = j.Repo
3361
}
3462
}
35-
if withRepo {
36-
var runsList RunList = make([]*ActionRun, 0, len(runs))
37-
for _, r := range runs {
38-
runsList = append(runsList, r)
39-
}
40-
return runsList.LoadRepos(ctx)
41-
}
4263
return nil
4364
}
4465

routers/api/v1/api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,8 +1169,8 @@ func Routes() *web.Router {
11691169
}, context.ReferencesGitRepo(), reqToken(), reqRepoReader(unit.TypeActions))
11701170

11711171
m.Group("/actions/runs", func() {
1172-
m.Get("/{run_id}/jobs/{job}/logs", repo.DownloadActionsRunLogs)
1173-
}, context.ReferencesGitRepo(), reqToken(), reqRepoReader(unit.TypeActions))
1172+
m.Get("/{run}/jobs/{job}/logs", repo.DownloadActionsRunJobLogs)
1173+
}, reqToken(), reqRepoReader(unit.TypeActions))
11741174

11751175
m.Group("/hooks/git", func() {
11761176
m.Combo("").Get(repo.ListGitHooks)

routers/api/v1/repo/actions_run.go

Lines changed: 12 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,23 @@
44
package repo
55

66
import (
7-
"errors"
8-
"fmt"
9-
"net/http"
10-
"strings"
11-
127
actions_model "code.gitea.io/gitea/models/actions"
13-
"code.gitea.io/gitea/modules/actions"
14-
"code.gitea.io/gitea/modules/util"
8+
"code.gitea.io/gitea/routers/common"
159
"code.gitea.io/gitea/services/context"
1610
)
1711

18-
func getRunIndex(ctx *context.APIContext) int64 {
12+
func getRunID(ctx *context.APIContext) int64 {
1913
// if run param is "latest", get the latest run index
20-
if ctx.PathParam("run_id") == "latest" {
14+
if ctx.PathParam("run") == "latest" {
2115
if run, _ := actions_model.GetLatestRun(ctx, ctx.Repo.Repository.ID); run != nil {
22-
return run.Index
23-
}
24-
}
25-
return ctx.PathParamInt64("run_id")
26-
}
27-
28-
// getRunJobs gets the jobs of runIndex, and returns jobs[jobIndex], jobs.
29-
// Any error will be written to the ctx.
30-
// It never returns a nil job of an empty jobs, if the jobIndex is out of range, it will be treated as 0.
31-
func getRunJobs(ctx *context.APIContext, runIndex, jobIndex int64) (*actions_model.ActionRunJob, []*actions_model.ActionRunJob) {
32-
run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
33-
if err != nil {
34-
if errors.Is(err, util.ErrNotExist) {
35-
ctx.HTTPError(http.StatusNotFound, err.Error())
36-
return nil, nil
16+
return run.ID
3717
}
38-
ctx.HTTPError(http.StatusInternalServerError, err.Error())
39-
return nil, nil
40-
}
41-
run.Repo = ctx.Repo.Repository
42-
jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID)
43-
if err != nil {
44-
ctx.HTTPError(http.StatusInternalServerError, err.Error())
45-
return nil, nil
4618
}
47-
if len(jobs) == 0 {
48-
ctx.HTTPError(http.StatusNotFound)
49-
return nil, nil
50-
}
51-
52-
for _, v := range jobs {
53-
v.Run = run
54-
}
55-
56-
if jobIndex >= 0 && jobIndex < int64(len(jobs)) {
57-
return jobs[jobIndex], jobs
58-
}
59-
return jobs[0], jobs
19+
return ctx.PathParamInt64("run")
6020
}
6121

62-
func DownloadActionsRunLogs(ctx *context.APIContext) {
63-
// swagger:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs/{job}/logs repository downloadActionsRunLogs
22+
func DownloadActionsRunJobLogs(ctx *context.APIContext) {
23+
// swagger:operation GET /repos/{owner}/{repo}/actions/runs/{run}/jobs/{job}/logs repository downloadActionsRunJobLogs
6424
// ---
6525
// summary: Downloads the logs for a workflow run redirects to blob url
6626
// produces:
@@ -76,7 +36,7 @@ func DownloadActionsRunLogs(ctx *context.APIContext) {
7636
// description: name of the repository
7737
// type: string
7838
// required: true
79-
// - name: run_id
39+
// - name: run
8040
// in: path
8141
// description: id of the run, this could be latest
8242
// type: integer
@@ -87,57 +47,14 @@ func DownloadActionsRunLogs(ctx *context.APIContext) {
8747
// type: integer
8848
// required: true
8949
// responses:
90-
// "302":
91-
// description: redirect to the blob download
50+
// "200":
51+
// description: output blob content
9252
// "400":
9353
// "$ref": "#/responses/error"
9454
// "404":
9555
// "$ref": "#/responses/notFound"
9656

97-
runIndex := getRunIndex(ctx)
57+
runID := getRunID(ctx)
9858
jobIndex := ctx.PathParamInt64("job")
99-
100-
job, _ := getRunJobs(ctx, runIndex, jobIndex)
101-
if ctx.Written() {
102-
return
103-
}
104-
if job.TaskID == 0 {
105-
ctx.HTTPError(http.StatusNotFound, "job is not started")
106-
return
107-
}
108-
109-
err := job.LoadRun(ctx)
110-
if err != nil {
111-
ctx.HTTPError(http.StatusInternalServerError, err.Error())
112-
return
113-
}
114-
115-
task, err := actions_model.GetTaskByID(ctx, job.TaskID)
116-
if err != nil {
117-
ctx.HTTPError(http.StatusInternalServerError, err.Error())
118-
return
119-
}
120-
if task.LogExpired {
121-
ctx.HTTPError(http.StatusNotFound, "logs have been cleaned up")
122-
return
123-
}
124-
125-
reader, err := actions.OpenLogs(ctx, task.LogInStorage, task.LogFilename)
126-
if err != nil {
127-
ctx.HTTPError(http.StatusInternalServerError, err.Error())
128-
return
129-
}
130-
defer reader.Close()
131-
132-
workflowName := job.Run.WorkflowID
133-
if p := strings.Index(workflowName, "."); p > 0 {
134-
workflowName = workflowName[0:p]
135-
}
136-
ctx.ServeContent(reader, &context.ServeHeaderOptions{
137-
Filename: fmt.Sprintf("%v-%v-%v.log", workflowName, job.Name, task.ID),
138-
ContentLength: &task.LogSize,
139-
ContentType: "text/plain",
140-
ContentTypeCharset: "utf-8",
141-
Disposition: "attachment",
142-
})
59+
common.DownloadActionsRunJobLogs(ctx.Base, ctx.Repo.Repository, runID, jobIndex)
14360
}

routers/common/actions.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package common
5+
6+
import (
7+
"fmt"
8+
"net/http"
9+
"strings"
10+
11+
actions_model "code.gitea.io/gitea/models/actions"
12+
repo_model "code.gitea.io/gitea/models/repo"
13+
"code.gitea.io/gitea/modules/actions"
14+
"code.gitea.io/gitea/services/context"
15+
)
16+
17+
func DownloadActionsRunJobLogs(ctx *context.Base, ctxRepo *repo_model.Repository, runID, jobIndex int64) {
18+
if runID == 0 {
19+
ctx.HTTPError(http.StatusBadRequest, "invalid run id")
20+
return
21+
}
22+
23+
runJobs, err := actions_model.GetRunJobsByRunID(ctx, runID)
24+
if err != nil {
25+
ctx.HTTPError(http.StatusInternalServerError, err.Error())
26+
return
27+
}
28+
if len(runJobs) == 0 {
29+
ctx.HTTPError(http.StatusNotFound)
30+
return
31+
}
32+
if err := runJobs.LoadRepos(ctx); err != nil {
33+
ctx.HTTPError(http.StatusInternalServerError, err.Error())
34+
return
35+
}
36+
if runJobs[0].Repo.ID != ctxRepo.ID {
37+
ctx.HTTPError(http.StatusNotFound)
38+
return
39+
}
40+
41+
var curJob *actions_model.ActionRunJob
42+
for _, job := range runJobs {
43+
if job.ID == jobIndex {
44+
curJob = job
45+
break
46+
}
47+
}
48+
if curJob == nil {
49+
ctx.HTTPError(http.StatusNotFound)
50+
return
51+
}
52+
53+
if curJob.TaskID == 0 {
54+
ctx.HTTPError(http.StatusNotFound, "job is not started")
55+
return
56+
}
57+
58+
if err := curJob.LoadRun(ctx); err != nil {
59+
ctx.HTTPError(http.StatusInternalServerError, err.Error())
60+
return
61+
}
62+
63+
task, err := actions_model.GetTaskByID(ctx, curJob.TaskID)
64+
if err != nil {
65+
ctx.HTTPError(http.StatusInternalServerError, err.Error())
66+
return
67+
}
68+
if task.LogExpired {
69+
ctx.HTTPError(http.StatusNotFound, "logs have been cleaned up")
70+
return
71+
}
72+
73+
reader, err := actions.OpenLogs(ctx, task.LogInStorage, task.LogFilename)
74+
if err != nil {
75+
ctx.HTTPError(http.StatusInternalServerError, err.Error())
76+
return
77+
}
78+
defer reader.Close()
79+
80+
workflowName := curJob.Run.WorkflowID
81+
if p := strings.Index(workflowName, "."); p > 0 {
82+
workflowName = workflowName[0:p]
83+
}
84+
ctx.ServeContent(reader, &context.ServeHeaderOptions{
85+
Filename: fmt.Sprintf("%v-%v-%v.log", workflowName, curJob.Name, task.ID),
86+
ContentLength: &task.LogSize,
87+
ContentType: "text/plain",
88+
ContentTypeCharset: "utf-8",
89+
Disposition: "attachment",
90+
})
91+
}

0 commit comments

Comments
 (0)