loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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)

authored by

Vitaliy Filippov and committed by
GitHub
f0b773e0 b08647f0

+62 -2
+2 -2
modules/actions/log.go
··· 73 73 } 74 74 75 75 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) 77 77 if err != nil { 78 78 return nil, err 79 79 } ··· 141 141 return nil 142 142 } 143 143 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) { 145 145 if !inStorage { 146 146 name := DBFSPrefix + filename 147 147 f, err := dbfs.Open(ctx, name)
+1
options/locale/locale_en-US.ini
··· 129 129 show_timestamps = Show timestamps 130 130 show_log_seconds = Show seconds 131 131 show_full_screen = Show full screen 132 + download_logs = Download logs 132 133 133 134 confirm_delete_selected = Confirm to delete all selected items? 134 135
+50
routers/web/repo/actions/view.go
··· 8 8 "errors" 9 9 "fmt" 10 10 "net/http" 11 + "strings" 11 12 "time" 12 13 13 14 actions_model "code.gitea.io/gitea/models/actions" ··· 308 309 309 310 actions_service.CreateCommitStatus(ctx, job) 310 311 return nil 312 + } 313 + 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 + }) 311 361 } 312 362 313 363 func Cancel(ctx *context_module.Context) {
+1
routers/web/web.go
··· 1207 1207 Get(actions.View). 1208 1208 Post(web.Bind(actions.ViewRequest{}), actions.ViewPost) 1209 1209 m.Post("/rerun", reqRepoActionsWriter, actions.RerunOne) 1210 + m.Get("/logs", actions.Logs) 1210 1211 }) 1211 1212 m.Post("/cancel", reqRepoActionsWriter, actions.Cancel) 1212 1213 m.Post("/approve", reqRepoActionsWriter, actions.Approve)
+1
templates/repo/actions/view.tmpl
··· 22 22 data-locale-show-timestamps="{{.locale.Tr "show_timestamps"}}" 23 23 data-locale-show-log-seconds="{{.locale.Tr "show_log_seconds"}}" 24 24 data-locale-show-full-screen="{{.locale.Tr "show_full_screen"}}" 25 + data-locale-download-logs="{{.locale.Tr "download_logs"}}" 25 26 > 26 27 </div> 27 28 </div>
+5
web_src/js/components/RepoActionView.vue
··· 74 74 <SvgIcon name="octicon-gear" :size="18"/> 75 75 </button> 76 76 <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> 77 81 <a class="item" @click="toggleTimeDisplay('seconds')"> 78 82 <i class="icon"><SvgIcon v-show="timeVisible['log-time-seconds']" name="octicon-check"/></i> 79 83 {{ locale.showLogSeconds }} ··· 453 457 showTimeStamps: el.getAttribute('data-locale-show-timestamps'), 454 458 showLogSeconds: el.getAttribute('data-locale-show-log-seconds'), 455 459 showFullScreen: el.getAttribute('data-locale-show-full-screen'), 460 + downloadLogs: el.getAttribute('data-locale-download-logs'), 456 461 status: { 457 462 unknown: el.getAttribute('data-locale-status-unknown'), 458 463 waiting: el.getAttribute('data-locale-status-waiting'),
+2
web_src/js/svg.js
··· 22 22 import octiconDiffRemoved from '../../public/img/svg/octicon-diff-removed.svg'; 23 23 import octiconDiffRenamed from '../../public/img/svg/octicon-diff-renamed.svg'; 24 24 import octiconDotFill from '../../public/img/svg/octicon-dot-fill.svg'; 25 + import octiconDownload from '../../public/img/svg/octicon-download.svg'; 25 26 import octiconEye from '../../public/img/svg/octicon-eye.svg'; 26 27 import octiconFile from '../../public/img/svg/octicon-file.svg'; 27 28 import octiconFileDirectoryFill from '../../public/img/svg/octicon-file-directory-fill.svg'; ··· 91 92 'octicon-diff-removed': octiconDiffRemoved, 92 93 'octicon-diff-renamed': octiconDiffRenamed, 93 94 'octicon-dot-fill': octiconDotFill, 95 + 'octicon-download': octiconDownload, 94 96 'octicon-eye': octiconEye, 95 97 'octicon-file': octiconFile, 96 98 'octicon-file-directory-fill': octiconFileDirectoryFill,