f3d293d2bb
current actions artifacts implementation only support single file artifact. To support multiple files uploading, it needs: - save each file to each db record with same run-id, same artifact-name and proper artifact-path - need change artifact uploading url without artifact-id, multiple files creates multiple artifact-ids - support `path` in download-artifact action. artifact should download to `{path}/{artifact-path}`. - in repo action view, it provides zip download link in artifacts list in summary page, no matter this artifact contains single or multiple files.
82 lines
2.7 KiB
Go
82 lines
2.7 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/models/actions"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/util"
|
|
)
|
|
|
|
const (
|
|
artifactXTfsFileLengthHeader = "x-tfs-filelength"
|
|
artifactXActionsResultsMD5Header = "x-actions-results-md5"
|
|
)
|
|
|
|
// The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32
|
|
var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<", ">", "|", "*", "?", "\r", "\n"}, "")
|
|
|
|
func validateArtifactName(ctx *ArtifactContext, artifactName string) bool {
|
|
if strings.ContainsAny(artifactName, invalidArtifactNameChars) {
|
|
log.Error("Error checking artifact name contains invalid character")
|
|
ctx.Error(http.StatusBadRequest, "Error checking artifact name contains invalid character")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func validateRunID(ctx *ArtifactContext) (*actions.ActionTask, int64, bool) {
|
|
task := ctx.ActionTask
|
|
runID := ctx.ParamsInt64("run_id")
|
|
if task.Job.RunID != runID {
|
|
log.Error("Error runID not match")
|
|
ctx.Error(http.StatusBadRequest, "run-id does not match")
|
|
return nil, 0, false
|
|
}
|
|
return task, runID, true
|
|
}
|
|
|
|
func validateArtifactHash(ctx *ArtifactContext, artifactName string) bool {
|
|
paramHash := ctx.Params("artifact_hash")
|
|
// use artifact name to create upload url
|
|
artifactHash := fmt.Sprintf("%x", md5.Sum([]byte(artifactName)))
|
|
if paramHash == artifactHash {
|
|
return true
|
|
}
|
|
log.Error("Invalid artifact hash: %s", paramHash)
|
|
ctx.Error(http.StatusBadRequest, "Invalid artifact hash")
|
|
return false
|
|
}
|
|
|
|
func parseArtifactItemPath(ctx *ArtifactContext) (string, string, bool) {
|
|
// itemPath is generated from upload-artifact action
|
|
// it's formatted as {artifact_name}/{artfict_path_in_runner}
|
|
itemPath := util.PathJoinRel(ctx.Req.URL.Query().Get("itemPath"))
|
|
artifactName := strings.Split(itemPath, "/")[0]
|
|
artifactPath := strings.TrimPrefix(itemPath, artifactName+"/")
|
|
if !validateArtifactHash(ctx, artifactName) {
|
|
return "", "", false
|
|
}
|
|
if !validateArtifactName(ctx, artifactName) {
|
|
return "", "", false
|
|
}
|
|
return artifactName, artifactPath, true
|
|
}
|
|
|
|
// getUploadFileSize returns the size of the file to be uploaded.
|
|
// The raw size is the size of the file as reported by the header X-TFS-FileLength.
|
|
func getUploadFileSize(ctx *ArtifactContext) (int64, int64, error) {
|
|
contentLength := ctx.Req.ContentLength
|
|
xTfsLength, _ := strconv.ParseInt(ctx.Req.Header.Get(artifactXTfsFileLengthHeader), 10, 64)
|
|
if xTfsLength > 0 {
|
|
return xTfsLength, contentLength, nil
|
|
}
|
|
return contentLength, contentLength, nil
|
|
}
|