mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-11 13:35:24 +02:00
test
This commit is contained in:
parent
015eb7805c
commit
f4bbd1a2e0
@ -3790,6 +3790,8 @@
|
|||||||
"actions.workflow.from_ref": "Use workflow from",
|
"actions.workflow.from_ref": "Use workflow from",
|
||||||
"actions.workflow.has_workflow_dispatch": "This workflow has a workflow_dispatch event trigger.",
|
"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.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.need_approval_desc": "Need approval to run workflows for fork pull request.",
|
||||||
"actions.approve_all_success": "All workflow runs are approved successfully.",
|
"actions.approve_all_success": "All workflow runs are approved successfully.",
|
||||||
"actions.variables": "Variables",
|
"actions.variables": "Variables",
|
||||||
|
|||||||
@ -505,7 +505,8 @@ func MockActionsArtifactPreview(ctx *context.Context) {
|
|||||||
return
|
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))
|
previewFiles := make([]actions.ArtifactPreviewFile, 0, len(files))
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
previewFiles = append(previewFiles, actions.ArtifactPreviewFile{
|
previewFiles = append(previewFiles, actions.ArtifactPreviewFile{
|
||||||
@ -524,6 +525,9 @@ func MockActionsArtifactPreview(ctx *context.Context) {
|
|||||||
ctx.Data["PreviewRawURL"] = previewURL + "/raw"
|
ctx.Data["PreviewRawURL"] = previewURL + "/raw"
|
||||||
ctx.Data["DownloadURL"] = runURL + "/artifacts/" + url.PathEscape(artifactName)
|
ctx.Data["DownloadURL"] = runURL + "/artifacts/" + url.PathEscape(artifactName)
|
||||||
ctx.Data["SelectedPath"] = selectedPath
|
ctx.Data["SelectedPath"] = selectedPath
|
||||||
|
ctx.Data["RequestedPathMissing"] = requested != "" && selectedPath == ""
|
||||||
|
ctx.Data["AttemptQuery"] = ""
|
||||||
|
ctx.Data["AttemptAmpQuery"] = ""
|
||||||
ctx.HTML(http.StatusOK, "devtest/repo-action-artifact-preview")
|
ctx.HTML(http.StatusOK, "devtest/repo-action-artifact-preview")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
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/actions"
|
||||||
"code.gitea.io/gitea/modules/httplib"
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
@ -104,12 +103,17 @@ func getCurrentRunAndUploadedArtifacts(ctx *context_module.Context, artifactName
|
|||||||
return nil, nil, false
|
return nil, nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts, err := db.Find[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{
|
resolvedAttemptID, err := resolveArtifactAttemptIDFromQuery(ctx, run)
|
||||||
RunID: run.ID,
|
|
||||||
ArtifactName: artifactName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
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
|
return nil, nil, false
|
||||||
}
|
}
|
||||||
if len(artifacts) == 0 {
|
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) {
|
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()
|
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)
|
log.Error("artifact preview io.Copy: %v", err)
|
||||||
WritePreviewRawError(ctx, http.StatusInternalServerError, "failed to read artifact")
|
WritePreviewRawError(ctx, http.StatusInternalServerError, "failed to read artifact")
|
||||||
return
|
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 {
|
if _, err := buf.Seek(0, io.SeekStart); err != nil {
|
||||||
log.Error("artifact preview Seek: %v", err)
|
log.Error("artifact preview Seek: %v", err)
|
||||||
WritePreviewRawError(ctx, http.StatusInternalServerError, "failed to read artifact")
|
WritePreviewRawError(ctx, http.StatusInternalServerError, "failed to read artifact")
|
||||||
@ -387,7 +399,8 @@ func ArtifactsPreviewView(ctx *context_module.Context) {
|
|||||||
ctx.ServerError("listPreviewPaths", err)
|
ctx.ServerError("listPreviewPaths", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectedPath := ChoosePreviewPath(paths, GetRequestedPreviewPath(ctx))
|
requested := GetRequestedPreviewPath(ctx)
|
||||||
|
selectedPath := ChoosePreviewPath(paths, requested)
|
||||||
|
|
||||||
previewFiles := make([]ArtifactPreviewFile, 0, len(paths))
|
previewFiles := make([]ArtifactPreviewFile, 0, len(paths))
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
@ -400,6 +413,12 @@ func ArtifactsPreviewView(ctx *context_module.Context) {
|
|||||||
runURL := run.Link()
|
runURL := run.Link()
|
||||||
artifactPath := url.PathEscape(artifactName)
|
artifactPath := url.PathEscape(artifactName)
|
||||||
previewURL := runURL + "/artifacts/" + artifactPath + "/preview"
|
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["Title"] = ctx.Tr("preview")
|
||||||
ctx.Data["PageIsActions"] = true
|
ctx.Data["PageIsActions"] = true
|
||||||
@ -407,8 +426,11 @@ func ArtifactsPreviewView(ctx *context_module.Context) {
|
|||||||
ctx.Data["ArtifactName"] = artifactName
|
ctx.Data["ArtifactName"] = artifactName
|
||||||
ctx.Data["PreviewURL"] = previewURL
|
ctx.Data["PreviewURL"] = previewURL
|
||||||
ctx.Data["PreviewRawURL"] = previewURL + "/raw"
|
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["SelectedPath"] = selectedPath
|
||||||
|
ctx.Data["RequestedPathMissing"] = requested != "" && selectedPath == ""
|
||||||
ctx.Data["PreviewFiles"] = previewFiles
|
ctx.Data["PreviewFiles"] = previewFiles
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplArtifactPreviewAction)
|
ctx.HTML(http.StatusOK, tplArtifactPreviewAction)
|
||||||
|
|||||||
@ -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="ui top attached header tw-flex tw-items-center tw-justify-between">
|
||||||
<div class="artifact-preview-title">
|
<div class="artifact-preview-title">
|
||||||
<span class="tw-text-base tw-font-semibold">{{ctx.Locale.Tr "preview"}}: <span class="gt-ellipsis">{{.ArtifactName}}</span></span>
|
<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>
|
<b>{{ctx.Locale.Tr "files"}}</b>
|
||||||
</div>
|
</div>
|
||||||
{{range .PreviewFiles}}
|
{{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}}
|
{{.Path}}
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
@ -51,7 +34,9 @@
|
|||||||
{{if .SelectedPath}}
|
{{if .SelectedPath}}
|
||||||
{{/* sandbox="allow-scripts": scripts execute but in a null/opaque origin (no allow-same-origin), so they cannot
|
{{/* 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. */}}
|
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}}
|
{{else}}
|
||||||
<div class="ui attached segment">{{ctx.Locale.Tr "none"}}</div>
|
<div class="ui attached segment">{{ctx.Locale.Tr "none"}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
14
web_src/css/features/actions-artifact-preview.css
Normal file
14
web_src/css/features/actions-artifact-preview.css
Normal 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);
|
||||||
|
}
|
||||||
@ -48,6 +48,7 @@
|
|||||||
@import "./features/cropper.css";
|
@import "./features/cropper.css";
|
||||||
@import "./features/console.css";
|
@import "./features/console.css";
|
||||||
@import "./features/captcha.css";
|
@import "./features/captcha.css";
|
||||||
|
@import "./features/actions-artifact-preview.css";
|
||||||
|
|
||||||
@import "./markup/content.css";
|
@import "./markup/content.css";
|
||||||
@import "./markup/codeblock.css";
|
@import "./markup/codeblock.css";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user