gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright 2025 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package shared
  4. import (
  5. "fmt"
  6. "net/http"
  7. actions_model "code.gitea.io/gitea/models/actions"
  8. "code.gitea.io/gitea/models/db"
  9. repo_model "code.gitea.io/gitea/models/repo"
  10. user_model "code.gitea.io/gitea/models/user"
  11. "code.gitea.io/gitea/modules/git"
  12. "code.gitea.io/gitea/modules/setting"
  13. api "code.gitea.io/gitea/modules/structs"
  14. "code.gitea.io/gitea/modules/webhook"
  15. "code.gitea.io/gitea/routers/api/v1/utils"
  16. "code.gitea.io/gitea/services/context"
  17. "code.gitea.io/gitea/services/convert"
  18. )
  19. // ListJobs lists jobs for api route validated ownerID and repoID
  20. // ownerID == 0 and repoID == 0 means all jobs
  21. // ownerID == 0 and repoID != 0 means all jobs for the given repo
  22. // ownerID != 0 and repoID == 0 means all jobs for the given user/org
  23. // ownerID != 0 and repoID != 0 undefined behavior
  24. // runID == 0 means all jobs
  25. // runID is used as an additional filter together with ownerID and repoID to only return jobs for the given run
  26. // Access rights are checked at the API route level
  27. func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64) {
  28. if ownerID != 0 && repoID != 0 {
  29. setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
  30. }
  31. opts := actions_model.FindRunJobOptions{
  32. OwnerID: ownerID,
  33. RepoID: repoID,
  34. RunID: runID,
  35. ListOptions: utils.GetListOptions(ctx),
  36. }
  37. for _, status := range ctx.FormStrings("status") {
  38. values, err := convertToInternal(status)
  39. if err != nil {
  40. ctx.APIError(http.StatusBadRequest, fmt.Errorf("Invalid status %s", status))
  41. return
  42. }
  43. opts.Statuses = append(opts.Statuses, values...)
  44. }
  45. jobs, total, err := db.FindAndCount[actions_model.ActionRunJob](ctx, opts)
  46. if err != nil {
  47. ctx.APIErrorInternal(err)
  48. return
  49. }
  50. res := new(api.ActionWorkflowJobsResponse)
  51. res.TotalCount = total
  52. res.Entries = make([]*api.ActionWorkflowJob, len(jobs))
  53. isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID
  54. for i := range jobs {
  55. var repository *repo_model.Repository
  56. if isRepoLevel {
  57. repository = ctx.Repo.Repository
  58. } else {
  59. repository, err = repo_model.GetRepositoryByID(ctx, jobs[i].RepoID)
  60. if err != nil {
  61. ctx.APIErrorInternal(err)
  62. return
  63. }
  64. }
  65. convertedWorkflowJob, err := convert.ToActionWorkflowJob(ctx, repository, nil, jobs[i])
  66. if err != nil {
  67. ctx.APIErrorInternal(err)
  68. return
  69. }
  70. res.Entries[i] = convertedWorkflowJob
  71. }
  72. ctx.JSON(http.StatusOK, &res)
  73. }
  74. func convertToInternal(s string) ([]actions_model.Status, error) {
  75. switch s {
  76. case "pending", "waiting", "requested", "action_required":
  77. return []actions_model.Status{actions_model.StatusBlocked}, nil
  78. case "queued":
  79. return []actions_model.Status{actions_model.StatusWaiting}, nil
  80. case "in_progress":
  81. return []actions_model.Status{actions_model.StatusRunning}, nil
  82. case "completed":
  83. return []actions_model.Status{
  84. actions_model.StatusSuccess,
  85. actions_model.StatusFailure,
  86. actions_model.StatusSkipped,
  87. actions_model.StatusCancelled,
  88. }, nil
  89. case "failure":
  90. return []actions_model.Status{actions_model.StatusFailure}, nil
  91. case "success":
  92. return []actions_model.Status{actions_model.StatusSuccess}, nil
  93. case "skipped", "neutral":
  94. return []actions_model.Status{actions_model.StatusSkipped}, nil
  95. case "cancelled", "timed_out":
  96. return []actions_model.Status{actions_model.StatusCancelled}, nil
  97. default:
  98. return nil, fmt.Errorf("invalid status %s", s)
  99. }
  100. }
  101. // ListRuns lists jobs for api route validated ownerID and repoID
  102. // ownerID == 0 and repoID == 0 means all runs
  103. // ownerID == 0 and repoID != 0 means all runs for the given repo
  104. // ownerID != 0 and repoID == 0 means all runs for the given user/org
  105. // ownerID != 0 and repoID != 0 undefined behavior
  106. // Access rights are checked at the API route level
  107. func ListRuns(ctx *context.APIContext, ownerID, repoID int64) {
  108. if ownerID != 0 && repoID != 0 {
  109. setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
  110. }
  111. opts := actions_model.FindRunOptions{
  112. OwnerID: ownerID,
  113. RepoID: repoID,
  114. ListOptions: utils.GetListOptions(ctx),
  115. }
  116. if event := ctx.FormString("event"); event != "" {
  117. opts.TriggerEvent = webhook.HookEventType(event)
  118. }
  119. if branch := ctx.FormString("branch"); branch != "" {
  120. opts.Ref = string(git.RefNameFromBranch(branch))
  121. }
  122. for _, status := range ctx.FormStrings("status") {
  123. values, err := convertToInternal(status)
  124. if err != nil {
  125. ctx.APIError(http.StatusBadRequest, fmt.Errorf("Invalid status %s", status))
  126. return
  127. }
  128. opts.Status = append(opts.Status, values...)
  129. }
  130. if actor := ctx.FormString("actor"); actor != "" {
  131. user, err := user_model.GetUserByName(ctx, actor)
  132. if err != nil {
  133. ctx.APIErrorInternal(err)
  134. return
  135. }
  136. opts.TriggerUserID = user.ID
  137. }
  138. if headSHA := ctx.FormString("head_sha"); headSHA != "" {
  139. opts.CommitSHA = headSHA
  140. }
  141. runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts)
  142. if err != nil {
  143. ctx.APIErrorInternal(err)
  144. return
  145. }
  146. res := new(api.ActionWorkflowRunsResponse)
  147. res.TotalCount = total
  148. res.Entries = make([]*api.ActionWorkflowRun, len(runs))
  149. isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID
  150. for i := range runs {
  151. var repository *repo_model.Repository
  152. if isRepoLevel {
  153. repository = ctx.Repo.Repository
  154. } else {
  155. repository, err = repo_model.GetRepositoryByID(ctx, runs[i].RepoID)
  156. if err != nil {
  157. ctx.APIErrorInternal(err)
  158. return
  159. }
  160. }
  161. convertedRun, err := convert.ToActionWorkflowRun(ctx, repository, runs[i])
  162. if err != nil {
  163. ctx.APIErrorInternal(err)
  164. return
  165. }
  166. res.Entries[i] = convertedRun
  167. }
  168. ctx.JSON(http.StatusOK, &res)
  169. }