0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-11 04:55:34 +02:00
This commit is contained in:
Nicolas 2026-05-09 14:31:41 +02:00
parent 015eb7805c
commit f4bbd1a2e0
6 changed files with 58 additions and 30 deletions

View File

@ -3790,6 +3790,8 @@
"actions.workflow.from_ref": "Use workflow from",
"actions.workflow.has_workflow_dispatch": "This workflow has a workflow_dispatch event trigger.",
"actions.workflow.has_no_workflow_dispatch": "Workflow '%s' has no workflow_dispatch event trigger.",
"actions.artifacts.preview_file_not_found": "The requested file is not present in this artifact.",
"actions.artifacts.preview_file_too_large": "This file is too large to preview. Please download the artifact to view it.",
"actions.need_approval_desc": "Need approval to run workflows for fork pull request.",
"actions.approve_all_success": "All workflow runs are approved successfully.",
"actions.variables": "Variables",

View File

@ -505,7 +505,8 @@ func MockActionsArtifactPreview(ctx *context.Context) {
return
}
selectedPath := actions.ChoosePreviewPath(mockArtifactFilePaths(files), actions.GetRequestedPreviewPath(ctx))
requested := actions.GetRequestedPreviewPath(ctx)
selectedPath := actions.ChoosePreviewPath(mockArtifactFilePaths(files), requested)
previewFiles := make([]actions.ArtifactPreviewFile, 0, len(files))
for _, file := range files {
previewFiles = append(previewFiles, actions.ArtifactPreviewFile{
@ -524,6 +525,9 @@ func MockActionsArtifactPreview(ctx *context.Context) {
ctx.Data["PreviewRawURL"] = previewURL + "/raw"
ctx.Data["DownloadURL"] = runURL + "/artifacts/" + url.PathEscape(artifactName)
ctx.Data["SelectedPath"] = selectedPath
ctx.Data["RequestedPathMissing"] = requested != "" && selectedPath == ""
ctx.Data["AttemptQuery"] = ""
ctx.Data["AttemptAmpQuery"] = ""
ctx.HTML(http.StatusOK, "devtest/repo-action-artifact-preview")
}

View File

@ -19,7 +19,6 @@ import (
"time"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
@ -104,12 +103,17 @@ func getCurrentRunAndUploadedArtifacts(ctx *context_module.Context, artifactName
return nil, nil, false
}
artifacts, err := db.Find[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{
RunID: run.ID,
ArtifactName: artifactName,
})
resolvedAttemptID, err := resolveArtifactAttemptIDFromQuery(ctx, run)
if err != nil {
ctx.ServerError("FindArtifacts", err)
ctx.NotFoundOrServerError("resolveArtifactAttemptIDFromQuery", func(err error) bool {
return errors.Is(err, util.ErrNotExist)
}, err)
return nil, nil, false
}
artifacts, err := actions_model.GetArtifactsByRunAttemptAndName(ctx, run.ID, resolvedAttemptID, artifactName)
if err != nil {
ctx.ServerError("GetArtifactsByRunAttemptAndName", err)
return nil, nil, false
}
if len(artifacts) == 0 {
@ -330,13 +334,21 @@ func WritePreviewRawError(ctx *context_module.Context, status int, msg string) {
}
func previewArtifactByReader(ctx *context_module.Context, path string, reader io.Reader) {
buf := filebuffer.New(int(setting.UI.MaxDisplayFileSize), "")
maxSize := setting.UI.MaxDisplayFileSize
buf := filebuffer.New(int(maxSize), "")
defer buf.Close()
if _, err := io.Copy(buf, io.LimitReader(reader, setting.UI.MaxDisplayFileSize)); err != nil {
// Copy maxSize+1 bytes so we can detect truncation: if the reader still has
// data after the limit, the file is too large to render in the preview.
n, err := io.Copy(buf, io.LimitReader(reader, maxSize+1))
if err != nil {
log.Error("artifact preview io.Copy: %v", err)
WritePreviewRawError(ctx, http.StatusInternalServerError, "failed to read artifact")
return
}
if n > maxSize {
WritePreviewRawError(ctx, http.StatusRequestEntityTooLarge, "file is too large to preview, please download the artifact instead")
return
}
if _, err := buf.Seek(0, io.SeekStart); err != nil {
log.Error("artifact preview Seek: %v", err)
WritePreviewRawError(ctx, http.StatusInternalServerError, "failed to read artifact")
@ -387,7 +399,8 @@ func ArtifactsPreviewView(ctx *context_module.Context) {
ctx.ServerError("listPreviewPaths", err)
return
}
selectedPath := ChoosePreviewPath(paths, GetRequestedPreviewPath(ctx))
requested := GetRequestedPreviewPath(ctx)
selectedPath := ChoosePreviewPath(paths, requested)
previewFiles := make([]ArtifactPreviewFile, 0, len(paths))
for _, path := range paths {
@ -400,6 +413,12 @@ func ArtifactsPreviewView(ctx *context_module.Context) {
runURL := run.Link()
artifactPath := url.PathEscape(artifactName)
previewURL := runURL + "/artifacts/" + artifactPath + "/preview"
downloadURL := runURL + "/artifacts/" + artifactPath
attemptQuery, attemptAmpQuery := "", ""
if attempt := ctx.FormString("attempt"); attempt != "" {
attemptQuery = "?attempt=" + url.QueryEscape(attempt)
attemptAmpQuery = "&attempt=" + url.QueryEscape(attempt)
}
ctx.Data["Title"] = ctx.Tr("preview")
ctx.Data["PageIsActions"] = true
@ -407,8 +426,11 @@ func ArtifactsPreviewView(ctx *context_module.Context) {
ctx.Data["ArtifactName"] = artifactName
ctx.Data["PreviewURL"] = previewURL
ctx.Data["PreviewRawURL"] = previewURL + "/raw"
ctx.Data["DownloadURL"] = runURL + "/artifacts/" + artifactPath
ctx.Data["DownloadURL"] = downloadURL + attemptQuery
ctx.Data["AttemptQuery"] = attemptQuery
ctx.Data["AttemptAmpQuery"] = attemptAmpQuery
ctx.Data["SelectedPath"] = selectedPath
ctx.Data["RequestedPathMissing"] = requested != "" && selectedPath == ""
ctx.Data["PreviewFiles"] = previewFiles
ctx.HTML(http.StatusOK, tplArtifactPreviewAction)

View File

@ -1,20 +1,3 @@
<style>
.artifact-preview-page {
margin-top: 1rem;
}
.artifact-preview-title {
min-width: 0;
}
.artifact-preview-frame {
width: 100%;
min-height: 70vh;
border: 1px solid var(--color-secondary);
border-radius: var(--border-radius);
}
</style>
<div class="ui top attached header tw-flex tw-items-center tw-justify-between">
<div class="artifact-preview-title">
<span class="tw-text-base tw-font-semibold">{{ctx.Locale.Tr "preview"}}: <span class="gt-ellipsis">{{.ArtifactName}}</span></span>
@ -39,7 +22,7 @@
<b>{{ctx.Locale.Tr "files"}}</b>
</div>
{{range .PreviewFiles}}
<a class="item gt-ellipsis {{if .Selected}}active{{end}}" href="{{$.PreviewURL}}?path={{QueryEscape .Path}}" title="{{.Path}}">
<a class="item gt-ellipsis {{if .Selected}}active{{end}}" href="{{$.PreviewURL}}?path={{QueryEscape .Path}}{{$.AttemptAmpQuery}}" title="{{.Path}}">
{{.Path}}
</a>
{{else}}
@ -51,7 +34,9 @@
{{if .SelectedPath}}
{{/* sandbox="allow-scripts": scripts execute but in a null/opaque origin (no allow-same-origin), so they cannot
access Gitea cookies or the parent frame. The response CSP reinforces this and blocks connect-src. */}}
<iframe class="artifact-preview-frame" src="{{.PreviewRawURL}}/{{PathEscapeSegments .SelectedPath}}" sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe>
<iframe class="artifact-preview-frame" src="{{.PreviewRawURL}}/{{PathEscapeSegments .SelectedPath}}{{.AttemptQuery}}" sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe>
{{else if .RequestedPathMissing}}
<div class="ui attached warning message">{{ctx.Locale.Tr "actions.artifacts.preview_file_not_found"}}</div>
{{else}}
<div class="ui attached segment">{{ctx.Locale.Tr "none"}}</div>
{{end}}

View File

@ -0,0 +1,14 @@
.artifact-preview-page {
margin-top: 1rem;
}
.artifact-preview-title {
min-width: 0;
}
.artifact-preview-frame {
width: 100%;
min-height: 70vh;
border: 1px solid var(--color-secondary);
border-radius: var(--border-radius);
}

View File

@ -48,6 +48,7 @@
@import "./features/cropper.css";
@import "./features/console.css";
@import "./features/captcha.css";
@import "./features/actions-artifact-preview.css";
@import "./markup/content.css";
@import "./markup/codeblock.css";