Skip to content

Commit f3d293d

Browse files
authored
Actions Artifacts support uploading multiple files and directories (#24874)
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.
1 parent 3acaaa2 commit f3d293d

File tree

10 files changed

+640
-350
lines changed

10 files changed

+640
-350
lines changed

models/actions/artifact.go

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func init() {
3131
// ActionArtifact is a file that is stored in the artifact storage.
3232
type ActionArtifact struct {
3333
ID int64 `xorm:"pk autoincr"`
34-
RunID int64 `xorm:"index UNIQUE(runid_name)"` // The run id of the artifact
34+
RunID int64 `xorm:"index unique(runid_name_path)"` // The run id of the artifact
3535
RunnerID int64
3636
RepoID int64 `xorm:"index"`
3737
OwnerID int64
@@ -40,27 +40,28 @@ type ActionArtifact struct {
4040
FileSize int64 // The size of the artifact in bytes
4141
FileCompressedSize int64 // The size of the artifact in bytes after gzip compression
4242
ContentEncoding string // The content encoding of the artifact
43-
ArtifactPath string // The path to the artifact when runner uploads it
44-
ArtifactName string `xorm:"UNIQUE(runid_name)"` // The name of the artifact when runner uploads it
45-
Status int64 `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
43+
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
44+
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
45+
Status int64 `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
4646
CreatedUnix timeutil.TimeStamp `xorm:"created"`
4747
UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
4848
}
4949

50-
// CreateArtifact create a new artifact with task info or get same named artifact in the same run
51-
func CreateArtifact(ctx context.Context, t *ActionTask, artifactName string) (*ActionArtifact, error) {
50+
func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPath string) (*ActionArtifact, error) {
5251
if err := t.LoadJob(ctx); err != nil {
5352
return nil, err
5453
}
55-
artifact, err := getArtifactByArtifactName(ctx, t.Job.RunID, artifactName)
54+
artifact, err := getArtifactByNameAndPath(ctx, t.Job.RunID, artifactName, artifactPath)
5655
if errors.Is(err, util.ErrNotExist) {
5756
artifact := &ActionArtifact{
58-
RunID: t.Job.RunID,
59-
RunnerID: t.RunnerID,
60-
RepoID: t.RepoID,
61-
OwnerID: t.OwnerID,
62-
CommitSHA: t.CommitSHA,
63-
Status: ArtifactStatusUploadPending,
57+
ArtifactName: artifactName,
58+
ArtifactPath: artifactPath,
59+
RunID: t.Job.RunID,
60+
RunnerID: t.RunnerID,
61+
RepoID: t.RepoID,
62+
OwnerID: t.OwnerID,
63+
CommitSHA: t.CommitSHA,
64+
Status: ArtifactStatusUploadPending,
6465
}
6566
if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
6667
return nil, err
@@ -72,9 +73,9 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName string) (*A
7273
return artifact, nil
7374
}
7475

75-
func getArtifactByArtifactName(ctx context.Context, runID int64, name string) (*ActionArtifact, error) {
76+
func getArtifactByNameAndPath(ctx context.Context, runID int64, name, fpath string) (*ActionArtifact, error) {
7677
var art ActionArtifact
77-
has, err := db.GetEngine(ctx).Where("run_id = ? AND artifact_name = ?", runID, name).Get(&art)
78+
has, err := db.GetEngine(ctx).Where("run_id = ? AND artifact_name = ? AND artifact_path = ?", runID, name, fpath).Get(&art)
7879
if err != nil {
7980
return nil, err
8081
} else if !has {
@@ -109,14 +110,42 @@ func ListArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact,
109110
return arts, db.GetEngine(ctx).Where("run_id=?", runID).Find(&arts)
110111
}
111112

113+
// ListArtifactsByRunIDAndArtifactName returns an artifacts of a run by artifact name
114+
func ListArtifactsByRunIDAndArtifactName(ctx context.Context, runID int64, artifactName string) ([]*ActionArtifact, error) {
115+
arts := make([]*ActionArtifact, 0, 10)
116+
return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, artifactName).Find(&arts)
117+
}
118+
112119
// ListUploadedArtifactsByRunID returns all uploaded artifacts of a run
113120
func ListUploadedArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) {
114121
arts := make([]*ActionArtifact, 0, 10)
115122
return arts, db.GetEngine(ctx).Where("run_id=? AND status=?", runID, ArtifactStatusUploadConfirmed).Find(&arts)
116123
}
117124

125+
// ActionArtifactMeta is the meta data of an artifact
126+
type ActionArtifactMeta struct {
127+
ArtifactName string
128+
FileSize int64
129+
}
130+
131+
// ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run
132+
func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtifactMeta, error) {
133+
arts := make([]*ActionArtifactMeta, 0, 10)
134+
return arts, db.GetEngine(ctx).Table("action_artifact").
135+
Where("run_id=? AND status=?", runID, ArtifactStatusUploadConfirmed).
136+
GroupBy("artifact_name").
137+
Select("artifact_name, sum(file_size) as file_size").
138+
Find(&arts)
139+
}
140+
118141
// ListArtifactsByRepoID returns all artifacts of a repo
119142
func ListArtifactsByRepoID(ctx context.Context, repoID int64) ([]*ActionArtifact, error) {
120143
arts := make([]*ActionArtifact, 0, 10)
121144
return arts, db.GetEngine(ctx).Where("repo_id=?", repoID).Find(&arts)
122145
}
146+
147+
// ListArtifactsByRunIDAndName returns artifacts by name of a run
148+
func ListArtifactsByRunIDAndName(ctx context.Context, runID int64, name string) ([]*ActionArtifact, error) {
149+
arts := make([]*ActionArtifact, 0, 10)
150+
return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, name).Find(&arts)
151+
}

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,8 @@ var migrations = []Migration{
511511
NewMigration("Add git_size and lfs_size columns to repository table", v1_21.AddGitSizeAndLFSSizeToRepositoryTable),
512512
// v264 -> v265
513513
NewMigration("Add branch table", v1_21.AddBranchTable),
514+
// v265 -> v266
515+
NewMigration("Alter Actions Artifact table", v1_21.AlterActionArtifactTable),
514516
}
515517

516518
// GetCurrentDBVersion returns the current db version

models/migrations/v1_21/v265.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_21 //nolint
5+
6+
import (
7+
"xorm.io/xorm"
8+
)
9+
10+
func AlterActionArtifactTable(x *xorm.Engine) error {
11+
// ActionArtifact is a file that is stored in the artifact storage.
12+
type ActionArtifact struct {
13+
RunID int64 `xorm:"index unique(runid_name_path)"` // The run id of the artifact
14+
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
15+
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when
16+
}
17+
18+
return x.Sync(new(ActionArtifact))
19+
}

0 commit comments

Comments
 (0)