Artifacts download api for artifact actions v4 (#33510)
* download endpoint has to use 302 redirect * fake blob download used if direct download not possible * downloading v3 artifacts not possible New repo apis based on GitHub Rest V3 - GET /runs/{run}/artifacts (Cannot use run index of url due to not being unique) - GET /artifacts - GET + DELETE /artifacts/{artifact_id} - GET /artifacts/{artifact_id}/zip - (GET /artifacts/{artifact_id}/zip/raw this is a workaround for a http 302 assertion in actions/toolkit) - api docs removed this is protected by a signed url like the internal artifacts api and no longer usable with any token or swagger - returns http 401 if the signature is invalid - or change the artifact id - or expired after 1 hour Closes #33353 Closes #32124 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
01bf8da02e
commit
2b8cfb557d
14 changed files with 1146 additions and 27 deletions
48
modules/actions/artifacts.go
Normal file
48
modules/actions/artifacts.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
// Artifacts using the v4 backend are stored as a single combined zip file per artifact on the backend
|
||||
// The v4 backend ensures ContentEncoding is set to "application/zip", which is not the case for the old backend
|
||||
func IsArtifactV4(art *actions_model.ActionArtifact) bool {
|
||||
return art.ArtifactName+".zip" == art.ArtifactPath && art.ContentEncoding == "application/zip"
|
||||
}
|
||||
|
||||
func DownloadArtifactV4ServeDirectOnly(ctx *context.Base, art *actions_model.ActionArtifact) (bool, error) {
|
||||
if setting.Actions.ArtifactStorage.ServeDirect() {
|
||||
u, err := storage.ActionsArtifacts.URL(art.StoragePath, art.ArtifactPath, nil)
|
||||
if u != nil && err == nil {
|
||||
ctx.Redirect(u.String(), http.StatusFound)
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func DownloadArtifactV4Fallback(ctx *context.Base, art *actions_model.ActionArtifact) error {
|
||||
f, err := storage.ActionsArtifacts.Open(art.StoragePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
http.ServeContent(ctx.Resp, ctx.Req, art.ArtifactName+".zip", art.CreatedUnix.AsLocalTime(), f)
|
||||
return nil
|
||||
}
|
||||
|
||||
func DownloadArtifactV4(ctx *context.Base, art *actions_model.ActionArtifact) error {
|
||||
ok, err := DownloadArtifactV4ServeDirectOnly(ctx, art)
|
||||
if ok || err != nil {
|
||||
return err
|
||||
}
|
||||
return DownloadArtifactV4Fallback(ctx, art)
|
||||
}
|
|
@ -65,3 +65,34 @@ type ActionWorkflowResponse struct {
|
|||
Workflows []*ActionWorkflow `json:"workflows"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
}
|
||||
|
||||
// ActionArtifact represents a ActionArtifact
|
||||
type ActionArtifact struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
SizeInBytes int64 `json:"size_in_bytes"`
|
||||
URL string `json:"url"`
|
||||
ArchiveDownloadURL string `json:"archive_download_url"`
|
||||
Expired bool `json:"expired"`
|
||||
WorkflowRun *ActionWorkflowRun `json:"workflow_run"`
|
||||
|
||||
// swagger:strfmt date-time
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
// swagger:strfmt date-time
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
// swagger:strfmt date-time
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
// ActionWorkflowRun represents a WorkflowRun
|
||||
type ActionWorkflowRun struct {
|
||||
ID int64 `json:"id"`
|
||||
RepositoryID int64 `json:"repository_id"`
|
||||
HeadSha string `json:"head_sha"`
|
||||
}
|
||||
|
||||
// ActionArtifactsResponse returns ActionArtifacts
|
||||
type ActionArtifactsResponse struct {
|
||||
Entries []*ActionArtifact `json:"artifacts"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue