gitea源码

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