code.gitea.io/gitea@v1.21.7/models/actions/artifact.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 // This artifact server is inspired by https://github.com/nektos/act/blob/master/pkg/artifacts/server.go. 5 // It updates url setting and uses ObjectStore to handle artifacts persistence. 6 7 package actions 8 9 import ( 10 "context" 11 "errors" 12 "time" 13 14 "code.gitea.io/gitea/models/db" 15 "code.gitea.io/gitea/modules/timeutil" 16 "code.gitea.io/gitea/modules/util" 17 ) 18 19 // ArtifactStatus is the status of an artifact, uploading, expired or need-delete 20 type ArtifactStatus int64 21 22 const ( 23 ArtifactStatusUploadPending ArtifactStatus = iota + 1 // 1, ArtifactStatusUploadPending is the status of an artifact upload that is pending 24 ArtifactStatusUploadConfirmed // 2, ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed 25 ArtifactStatusUploadError // 3, ArtifactStatusUploadError is the status of an artifact upload that is errored 26 ArtifactStatusExpired // 4, ArtifactStatusExpired is the status of an artifact that is expired 27 ) 28 29 func init() { 30 db.RegisterModel(new(ActionArtifact)) 31 } 32 33 // ActionArtifact is a file that is stored in the artifact storage. 34 type ActionArtifact struct { 35 ID int64 `xorm:"pk autoincr"` 36 RunID int64 `xorm:"index unique(runid_name_path)"` // The run id of the artifact 37 RunnerID int64 38 RepoID int64 `xorm:"index"` 39 OwnerID int64 40 CommitSHA string 41 StoragePath string // The path to the artifact in the storage 42 FileSize int64 // The size of the artifact in bytes 43 FileCompressedSize int64 // The size of the artifact in bytes after gzip compression 44 ContentEncoding string // The content encoding of the artifact 45 ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it 46 ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it 47 Status int64 `xorm:"index"` // The status of the artifact, uploading, expired or need-delete 48 CreatedUnix timeutil.TimeStamp `xorm:"created"` 49 UpdatedUnix timeutil.TimeStamp `xorm:"updated index"` 50 ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired 51 } 52 53 func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPath string, expiredDays int64) (*ActionArtifact, error) { 54 if err := t.LoadJob(ctx); err != nil { 55 return nil, err 56 } 57 artifact, err := getArtifactByNameAndPath(ctx, t.Job.RunID, artifactName, artifactPath) 58 if errors.Is(err, util.ErrNotExist) { 59 artifact := &ActionArtifact{ 60 ArtifactName: artifactName, 61 ArtifactPath: artifactPath, 62 RunID: t.Job.RunID, 63 RunnerID: t.RunnerID, 64 RepoID: t.RepoID, 65 OwnerID: t.OwnerID, 66 CommitSHA: t.CommitSHA, 67 Status: int64(ArtifactStatusUploadPending), 68 ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + 3600*24*expiredDays), 69 } 70 if _, err := db.GetEngine(ctx).Insert(artifact); err != nil { 71 return nil, err 72 } 73 return artifact, nil 74 } else if err != nil { 75 return nil, err 76 } 77 return artifact, nil 78 } 79 80 func getArtifactByNameAndPath(ctx context.Context, runID int64, name, fpath string) (*ActionArtifact, error) { 81 var art ActionArtifact 82 has, err := db.GetEngine(ctx).Where("run_id = ? AND artifact_name = ? AND artifact_path = ?", runID, name, fpath).Get(&art) 83 if err != nil { 84 return nil, err 85 } else if !has { 86 return nil, util.ErrNotExist 87 } 88 return &art, nil 89 } 90 91 // GetArtifactByID returns an artifact by id 92 func GetArtifactByID(ctx context.Context, id int64) (*ActionArtifact, error) { 93 var art ActionArtifact 94 has, err := db.GetEngine(ctx).ID(id).Get(&art) 95 if err != nil { 96 return nil, err 97 } else if !has { 98 return nil, util.ErrNotExist 99 } 100 101 return &art, nil 102 } 103 104 // UpdateArtifactByID updates an artifact by id 105 func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) error { 106 art.ID = id 107 _, err := db.GetEngine(ctx).ID(id).AllCols().Update(art) 108 return err 109 } 110 111 // ListArtifactsByRunID returns all artifacts of a run 112 func ListArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) { 113 arts := make([]*ActionArtifact, 0, 10) 114 return arts, db.GetEngine(ctx).Where("run_id=?", runID).Find(&arts) 115 } 116 117 // ListArtifactsByRunIDAndArtifactName returns an artifacts of a run by artifact name 118 func ListArtifactsByRunIDAndArtifactName(ctx context.Context, runID int64, artifactName string) ([]*ActionArtifact, error) { 119 arts := make([]*ActionArtifact, 0, 10) 120 return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, artifactName).Find(&arts) 121 } 122 123 // ListUploadedArtifactsByRunID returns all uploaded artifacts of a run 124 func ListUploadedArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) { 125 arts := make([]*ActionArtifact, 0, 10) 126 return arts, db.GetEngine(ctx).Where("run_id=? AND status=?", runID, ArtifactStatusUploadConfirmed).Find(&arts) 127 } 128 129 // ActionArtifactMeta is the meta data of an artifact 130 type ActionArtifactMeta struct { 131 ArtifactName string 132 FileSize int64 133 Status int64 134 } 135 136 // ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run 137 func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtifactMeta, error) { 138 arts := make([]*ActionArtifactMeta, 0, 10) 139 return arts, db.GetEngine(ctx).Table("action_artifact"). 140 Where("run_id=? AND (status=? OR status=?)", runID, ArtifactStatusUploadConfirmed, ArtifactStatusExpired). 141 GroupBy("artifact_name"). 142 Select("artifact_name, sum(file_size) as file_size, max(status) as status"). 143 Find(&arts) 144 } 145 146 // ListArtifactsByRepoID returns all artifacts of a repo 147 func ListArtifactsByRepoID(ctx context.Context, repoID int64) ([]*ActionArtifact, error) { 148 arts := make([]*ActionArtifact, 0, 10) 149 return arts, db.GetEngine(ctx).Where("repo_id=?", repoID).Find(&arts) 150 } 151 152 // ListArtifactsByRunIDAndName returns artifacts by name of a run 153 func ListArtifactsByRunIDAndName(ctx context.Context, runID int64, name string) ([]*ActionArtifact, error) { 154 arts := make([]*ActionArtifact, 0, 10) 155 return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, name).Find(&arts) 156 } 157 158 // ListNeedExpiredArtifacts returns all need expired artifacts but not deleted 159 func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { 160 arts := make([]*ActionArtifact, 0, 10) 161 return arts, db.GetEngine(ctx). 162 Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts) 163 } 164 165 // SetArtifactExpired sets an artifact to expired 166 func SetArtifactExpired(ctx context.Context, artifactID int64) error { 167 _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)}) 168 return err 169 }