Skip to content

Commit f0b773e

Browse files
authored
Support downloading raw task logs (#24451)
Hi! This pull request adds support for downloading raw task logs for Gitea Actions, similar to Github Actions It looks like the following: ![image](https://user-images.githubusercontent.com/945339/235376746-405d5019-710b-468b-8113-9e82eab8e752.png)
1 parent b08647f commit f0b773e

File tree

7 files changed

+62
-2
lines changed

7 files changed

+62
-2
lines changed

modules/actions/log.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func WriteLogs(ctx context.Context, filename string, offset int64, rows []*runne
7373
}
7474

7575
func ReadLogs(ctx context.Context, inStorage bool, filename string, offset, limit int64) ([]*runnerv1.LogRow, error) {
76-
f, err := openLogs(ctx, inStorage, filename)
76+
f, err := OpenLogs(ctx, inStorage, filename)
7777
if err != nil {
7878
return nil, err
7979
}
@@ -141,7 +141,7 @@ func RemoveLogs(ctx context.Context, inStorage bool, filename string) error {
141141
return nil
142142
}
143143

144-
func openLogs(ctx context.Context, inStorage bool, filename string) (io.ReadSeekCloser, error) {
144+
func OpenLogs(ctx context.Context, inStorage bool, filename string) (io.ReadSeekCloser, error) {
145145
if !inStorage {
146146
name := DBFSPrefix + filename
147147
f, err := dbfs.Open(ctx, name)

options/locale/locale_en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ concept_user_organization = Organization
129129
show_timestamps = Show timestamps
130130
show_log_seconds = Show seconds
131131
show_full_screen = Show full screen
132+
download_logs = Download logs
132133

133134
confirm_delete_selected = Confirm to delete all selected items?
134135

routers/web/repo/actions/view.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"errors"
99
"fmt"
1010
"net/http"
11+
"strings"
1112
"time"
1213

1314
actions_model "code.gitea.io/gitea/models/actions"
@@ -310,6 +311,55 @@ func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob) erro
310311
return nil
311312
}
312313

314+
func Logs(ctx *context_module.Context) {
315+
runIndex := ctx.ParamsInt64("run")
316+
jobIndex := ctx.ParamsInt64("job")
317+
318+
job, _ := getRunJobs(ctx, runIndex, jobIndex)
319+
if ctx.Written() {
320+
return
321+
}
322+
if job.TaskID == 0 {
323+
ctx.Error(http.StatusNotFound, "job is not started")
324+
return
325+
}
326+
327+
err := job.LoadRun(ctx)
328+
if err != nil {
329+
ctx.Error(http.StatusInternalServerError, err.Error())
330+
return
331+
}
332+
333+
task, err := actions_model.GetTaskByID(ctx, job.TaskID)
334+
if err != nil {
335+
ctx.Error(http.StatusInternalServerError, err.Error())
336+
return
337+
}
338+
if task.LogExpired {
339+
ctx.Error(http.StatusNotFound, "logs have been cleaned up")
340+
return
341+
}
342+
343+
reader, err := actions.OpenLogs(ctx, task.LogInStorage, task.LogFilename)
344+
if err != nil {
345+
ctx.Error(http.StatusInternalServerError, err.Error())
346+
return
347+
}
348+
defer reader.Close()
349+
350+
workflowName := job.Run.WorkflowID
351+
if p := strings.Index(workflowName, "."); p > 0 {
352+
workflowName = workflowName[0:p]
353+
}
354+
ctx.ServeContent(reader, &context_module.ServeHeaderOptions{
355+
Filename: fmt.Sprintf("%v-%v-%v.log", workflowName, job.Name, task.ID),
356+
ContentLength: &task.LogSize,
357+
ContentType: "text/plain",
358+
ContentTypeCharset: "utf-8",
359+
Disposition: "attachment",
360+
})
361+
}
362+
313363
func Cancel(ctx *context_module.Context) {
314364
runIndex := ctx.ParamsInt64("run")
315365

routers/web/web.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,7 @@ func registerRoutes(m *web.Route) {
12071207
Get(actions.View).
12081208
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
12091209
m.Post("/rerun", reqRepoActionsWriter, actions.RerunOne)
1210+
m.Get("/logs", actions.Logs)
12101211
})
12111212
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
12121213
m.Post("/approve", reqRepoActionsWriter, actions.Approve)

templates/repo/actions/view.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
data-locale-show-timestamps="{{.locale.Tr "show_timestamps"}}"
2323
data-locale-show-log-seconds="{{.locale.Tr "show_log_seconds"}}"
2424
data-locale-show-full-screen="{{.locale.Tr "show_full_screen"}}"
25+
data-locale-download-logs="{{.locale.Tr "download_logs"}}"
2526
>
2627
</div>
2728
</div>

web_src/js/components/RepoActionView.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@
7474
<SvgIcon name="octicon-gear" :size="18"/>
7575
</button>
7676
<div class="menu transition action-job-menu" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak>
77+
<a class="item" :href="run.link+'/jobs/'+jobIndex+'/logs'" target="_blank">
78+
<i class="icon"><SvgIcon name="octicon-download"/></i>
79+
{{ locale.downloadLogs }}
80+
</a>
7781
<a class="item" @click="toggleTimeDisplay('seconds')">
7882
<i class="icon"><SvgIcon v-show="timeVisible['log-time-seconds']" name="octicon-check"/></i>
7983
{{ locale.showLogSeconds }}
@@ -453,6 +457,7 @@ export function initRepositoryActionView() {
453457
showTimeStamps: el.getAttribute('data-locale-show-timestamps'),
454458
showLogSeconds: el.getAttribute('data-locale-show-log-seconds'),
455459
showFullScreen: el.getAttribute('data-locale-show-full-screen'),
460+
downloadLogs: el.getAttribute('data-locale-download-logs'),
456461
status: {
457462
unknown: el.getAttribute('data-locale-status-unknown'),
458463
waiting: el.getAttribute('data-locale-status-waiting'),

web_src/js/svg.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import octiconDiffModified from '../../public/img/svg/octicon-diff-modified.svg'
2222
import octiconDiffRemoved from '../../public/img/svg/octicon-diff-removed.svg';
2323
import octiconDiffRenamed from '../../public/img/svg/octicon-diff-renamed.svg';
2424
import octiconDotFill from '../../public/img/svg/octicon-dot-fill.svg';
25+
import octiconDownload from '../../public/img/svg/octicon-download.svg';
2526
import octiconEye from '../../public/img/svg/octicon-eye.svg';
2627
import octiconFile from '../../public/img/svg/octicon-file.svg';
2728
import octiconFileDirectoryFill from '../../public/img/svg/octicon-file-directory-fill.svg';
@@ -91,6 +92,7 @@ const svgs = {
9192
'octicon-diff-removed': octiconDiffRemoved,
9293
'octicon-diff-renamed': octiconDiffRenamed,
9394
'octicon-dot-fill': octiconDotFill,
95+
'octicon-download': octiconDownload,
9496
'octicon-eye': octiconEye,
9597
'octicon-file': octiconFile,
9698
'octicon-file-directory-fill': octiconFileDirectoryFill,

0 commit comments

Comments
 (0)