You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

artifact.go 7.0KB

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