Skip to content

Commit 1f2dbc2

Browse files
committed
Fix workflow run logs to download all jobs in zip archive
- GetWorkflowRunLogs was only downloading logs for first job (index 0) - GitHub Actions API expects workflow run logs to be a zip archive of all jobs - Added DownloadActionsRunAllJobLogs function to create zip archive - Each job's logs included as separate file: {workflow}-{jobname}-{taskid}.log - Properly handles missing jobs, expired logs, and different repos Fixes workflow run logs endpoint to match expected API behavior.
1 parent 628dd68 commit 1f2dbc2

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

routers/api/v1/repo/actions_run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ func GetWorkflowRunLogs(ctx *context.APIContext) {
628628
return
629629
}
630630

631-
if err = common.DownloadActionsRunJobLogsWithIndex(ctx.Base, ctx.Repo.Repository, run.ID, 0); err != nil {
631+
if err = common.DownloadActionsRunAllJobLogs(ctx.Base, ctx.Repo.Repository, run.ID); err != nil {
632632
if errors.Is(err, util.ErrNotExist) {
633633
ctx.APIError(404, "Logs not found")
634634
} else {

routers/common/actions.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
package common
55

66
import (
7+
"archive/zip"
78
"fmt"
9+
"io"
810
"strings"
911

1012
actions_model "code.gitea.io/gitea/models/actions"
@@ -28,6 +30,81 @@ func DownloadActionsRunJobLogsWithIndex(ctx *context.Base, ctxRepo *repo_model.R
2830
return DownloadActionsRunJobLogs(ctx, ctxRepo, runJobs[jobIndex])
2931
}
3032

33+
func DownloadActionsRunAllJobLogs(ctx *context.Base, ctxRepo *repo_model.Repository, runID int64) error {
34+
runJobs, err := actions_model.GetRunJobsByRunID(ctx, runID)
35+
if err != nil {
36+
return fmt.Errorf("GetRunJobsByRunID: %w", err)
37+
}
38+
if err = runJobs.LoadRepos(ctx); err != nil {
39+
return fmt.Errorf("LoadRepos: %w", err)
40+
}
41+
42+
if len(runJobs) == 0 {
43+
return util.NewNotExistErrorf("no jobs found for run %d", runID)
44+
}
45+
46+
// Load run for workflow name
47+
if err := runJobs[0].LoadRun(ctx); err != nil {
48+
return fmt.Errorf("LoadRun: %w", err)
49+
}
50+
51+
workflowName := runJobs[0].Run.WorkflowID
52+
if p := strings.Index(workflowName, "."); p > 0 {
53+
workflowName = workflowName[0:p]
54+
}
55+
56+
// Set headers for zip download
57+
ctx.Resp.Header().Set("Content-Type", "application/zip")
58+
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s-run-%d-logs.zip\"", workflowName, runID))
59+
60+
// Create zip writer
61+
zipWriter := zip.NewWriter(ctx.Resp)
62+
defer zipWriter.Close()
63+
64+
// Add each job's logs to the zip
65+
for _, job := range runJobs {
66+
if job.Repo.ID != ctxRepo.ID {
67+
continue // Skip jobs from other repos
68+
}
69+
70+
if job.TaskID == 0 {
71+
continue // Skip jobs that haven't started
72+
}
73+
74+
task, err := actions_model.GetTaskByID(ctx, job.TaskID)
75+
if err != nil {
76+
return fmt.Errorf("GetTaskByID for job %d: %w", job.ID, err)
77+
}
78+
79+
if task.LogExpired {
80+
continue // Skip expired logs
81+
}
82+
83+
reader, err := actions.OpenLogs(ctx, task.LogInStorage, task.LogFilename)
84+
if err != nil {
85+
return fmt.Errorf("OpenLogs for job %d: %w", job.ID, err)
86+
}
87+
88+
// Create file in zip with job name and task ID
89+
fileName := fmt.Sprintf("%s-%s-%d.log", workflowName, job.Name, task.ID)
90+
zipFile, err := zipWriter.Create(fileName)
91+
if err != nil {
92+
reader.Close()
93+
return fmt.Errorf("Create zip file %s: %w", fileName, err)
94+
}
95+
96+
// Copy log content to zip file
97+
if _, err := io.Copy(zipFile, reader); err != nil {
98+
reader.Close()
99+
return fmt.Errorf("Copy logs for job %d: %w", job.ID, err)
100+
}
101+
102+
reader.Close()
103+
}
104+
105+
return nil
106+
}
107+
31108
func DownloadActionsRunJobLogs(ctx *context.Base, ctxRepo *repo_model.Repository, curJob *actions_model.ActionRunJob) error {
32109
if curJob.Repo.ID != ctxRepo.ID {
33110
return util.NewNotExistErrorf("job not found")

0 commit comments

Comments
 (0)