gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package actions
  4. import (
  5. "context"
  6. "fmt"
  7. "slices"
  8. "time"
  9. "code.gitea.io/gitea/models/db"
  10. repo_model "code.gitea.io/gitea/models/repo"
  11. "code.gitea.io/gitea/modules/timeutil"
  12. "code.gitea.io/gitea/modules/util"
  13. "xorm.io/builder"
  14. )
  15. // ActionRunJob represents a job of a run
  16. type ActionRunJob struct {
  17. ID int64
  18. RunID int64 `xorm:"index"`
  19. Run *ActionRun `xorm:"-"`
  20. RepoID int64 `xorm:"index"`
  21. Repo *repo_model.Repository `xorm:"-"`
  22. OwnerID int64 `xorm:"index"`
  23. CommitSHA string `xorm:"index"`
  24. IsForkPullRequest bool
  25. Name string `xorm:"VARCHAR(255)"`
  26. Attempt int64
  27. WorkflowPayload []byte
  28. JobID string `xorm:"VARCHAR(255)"` // job id in workflow, not job's id
  29. Needs []string `xorm:"JSON TEXT"`
  30. RunsOn []string `xorm:"JSON TEXT"`
  31. TaskID int64 // the latest task of the job
  32. Status Status `xorm:"index"`
  33. Started timeutil.TimeStamp
  34. Stopped timeutil.TimeStamp
  35. Created timeutil.TimeStamp `xorm:"created"`
  36. Updated timeutil.TimeStamp `xorm:"updated index"`
  37. }
  38. func init() {
  39. db.RegisterModel(new(ActionRunJob))
  40. }
  41. func (job *ActionRunJob) Duration() time.Duration {
  42. return calculateDuration(job.Started, job.Stopped, job.Status)
  43. }
  44. func (job *ActionRunJob) LoadRun(ctx context.Context) error {
  45. if job.Run == nil {
  46. run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
  47. if err != nil {
  48. return err
  49. }
  50. job.Run = run
  51. }
  52. return nil
  53. }
  54. func (job *ActionRunJob) LoadRepo(ctx context.Context) error {
  55. if job.Repo == nil {
  56. repo, err := repo_model.GetRepositoryByID(ctx, job.RepoID)
  57. if err != nil {
  58. return err
  59. }
  60. job.Repo = repo
  61. }
  62. return nil
  63. }
  64. // LoadAttributes load Run if not loaded
  65. func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
  66. if job == nil {
  67. return nil
  68. }
  69. if err := job.LoadRun(ctx); err != nil {
  70. return err
  71. }
  72. return job.Run.LoadAttributes(ctx)
  73. }
  74. func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
  75. var job ActionRunJob
  76. has, err := db.GetEngine(ctx).Where("id=?", id).Get(&job)
  77. if err != nil {
  78. return nil, err
  79. } else if !has {
  80. return nil, fmt.Errorf("run job with id %d: %w", id, util.ErrNotExist)
  81. }
  82. return &job, nil
  83. }
  84. func GetRunJobsByRunID(ctx context.Context, runID int64) (ActionJobList, error) {
  85. var jobs []*ActionRunJob
  86. if err := db.GetEngine(ctx).Where("run_id=?", runID).OrderBy("id").Find(&jobs); err != nil {
  87. return nil, err
  88. }
  89. return jobs, nil
  90. }
  91. func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, cols ...string) (int64, error) {
  92. e := db.GetEngine(ctx)
  93. sess := e.ID(job.ID)
  94. if len(cols) > 0 {
  95. sess.Cols(cols...)
  96. }
  97. if cond != nil {
  98. sess.Where(cond)
  99. }
  100. affected, err := sess.Update(job)
  101. if err != nil {
  102. return 0, err
  103. }
  104. if affected == 0 || (!slices.Contains(cols, "status") && job.Status == 0) {
  105. return affected, nil
  106. }
  107. if affected != 0 && slices.Contains(cols, "status") && job.Status.IsWaiting() {
  108. // if the status of job changes to waiting again, increase tasks version.
  109. if err := IncreaseTaskVersion(ctx, job.OwnerID, job.RepoID); err != nil {
  110. return 0, err
  111. }
  112. }
  113. if job.RunID == 0 {
  114. var err error
  115. if job, err = GetRunJobByID(ctx, job.ID); err != nil {
  116. return 0, err
  117. }
  118. }
  119. {
  120. // Other goroutines may aggregate the status of the run and update it too.
  121. // So we need load the run and its jobs before updating the run.
  122. run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
  123. if err != nil {
  124. return 0, err
  125. }
  126. jobs, err := GetRunJobsByRunID(ctx, job.RunID)
  127. if err != nil {
  128. return 0, err
  129. }
  130. run.Status = AggregateJobStatus(jobs)
  131. if run.Started.IsZero() && run.Status.IsRunning() {
  132. run.Started = timeutil.TimeStampNow()
  133. }
  134. if run.Stopped.IsZero() && run.Status.IsDone() {
  135. run.Stopped = timeutil.TimeStampNow()
  136. }
  137. if err := UpdateRun(ctx, run, "status", "started", "stopped"); err != nil {
  138. return 0, fmt.Errorf("update run %d: %w", run.ID, err)
  139. }
  140. }
  141. return affected, nil
  142. }
  143. func AggregateJobStatus(jobs []*ActionRunJob) Status {
  144. allSuccessOrSkipped := len(jobs) != 0
  145. allSkipped := len(jobs) != 0
  146. var hasFailure, hasCancelled, hasWaiting, hasRunning, hasBlocked bool
  147. for _, job := range jobs {
  148. allSuccessOrSkipped = allSuccessOrSkipped && (job.Status == StatusSuccess || job.Status == StatusSkipped)
  149. allSkipped = allSkipped && job.Status == StatusSkipped
  150. hasFailure = hasFailure || job.Status == StatusFailure
  151. hasCancelled = hasCancelled || job.Status == StatusCancelled
  152. hasWaiting = hasWaiting || job.Status == StatusWaiting
  153. hasRunning = hasRunning || job.Status == StatusRunning
  154. hasBlocked = hasBlocked || job.Status == StatusBlocked
  155. }
  156. switch {
  157. case allSkipped:
  158. return StatusSkipped
  159. case allSuccessOrSkipped:
  160. return StatusSuccess
  161. case hasCancelled:
  162. return StatusCancelled
  163. case hasRunning:
  164. return StatusRunning
  165. case hasWaiting:
  166. return StatusWaiting
  167. case hasFailure:
  168. return StatusFailure
  169. case hasBlocked:
  170. return StatusBlocked
  171. default:
  172. return StatusUnknown // it shouldn't happen
  173. }
  174. }