gitea源码

runners.go 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package actions
  4. import (
  5. "errors"
  6. "net/http"
  7. "net/url"
  8. actions_model "code.gitea.io/gitea/models/actions"
  9. "code.gitea.io/gitea/models/db"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/setting"
  12. "code.gitea.io/gitea/modules/templates"
  13. "code.gitea.io/gitea/modules/util"
  14. "code.gitea.io/gitea/modules/web"
  15. shared_user "code.gitea.io/gitea/routers/web/shared/user"
  16. "code.gitea.io/gitea/services/context"
  17. "code.gitea.io/gitea/services/forms"
  18. )
  19. const (
  20. // TODO: Separate secrets from runners when layout is ready
  21. tplRepoRunners templates.TplName = "repo/settings/actions"
  22. tplOrgRunners templates.TplName = "org/settings/actions"
  23. tplAdminRunners templates.TplName = "admin/actions"
  24. tplUserRunners templates.TplName = "user/settings/actions"
  25. tplRepoRunnerEdit templates.TplName = "repo/settings/runner_edit"
  26. tplOrgRunnerEdit templates.TplName = "org/settings/runners_edit"
  27. tplAdminRunnerEdit templates.TplName = "admin/runners/edit"
  28. tplUserRunnerEdit templates.TplName = "user/settings/runner_edit"
  29. )
  30. type runnersCtx struct {
  31. OwnerID int64
  32. RepoID int64
  33. IsRepo bool
  34. IsOrg bool
  35. IsAdmin bool
  36. IsUser bool
  37. RunnersTemplate templates.TplName
  38. RunnerEditTemplate templates.TplName
  39. RedirectLink string
  40. }
  41. func getRunnersCtx(ctx *context.Context) (*runnersCtx, error) {
  42. if ctx.Data["PageIsRepoSettings"] == true {
  43. return &runnersCtx{
  44. RepoID: ctx.Repo.Repository.ID,
  45. OwnerID: 0,
  46. IsRepo: true,
  47. RunnersTemplate: tplRepoRunners,
  48. RunnerEditTemplate: tplRepoRunnerEdit,
  49. RedirectLink: ctx.Repo.RepoLink + "/settings/actions/runners/",
  50. }, nil
  51. }
  52. if ctx.Data["PageIsOrgSettings"] == true {
  53. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  54. ctx.ServerError("RenderUserOrgHeader", err)
  55. return nil, nil
  56. }
  57. return &runnersCtx{
  58. RepoID: 0,
  59. OwnerID: ctx.Org.Organization.ID,
  60. IsOrg: true,
  61. RunnersTemplate: tplOrgRunners,
  62. RunnerEditTemplate: tplOrgRunnerEdit,
  63. RedirectLink: ctx.Org.OrgLink + "/settings/actions/runners/",
  64. }, nil
  65. }
  66. if ctx.Data["PageIsAdmin"] == true {
  67. return &runnersCtx{
  68. RepoID: 0,
  69. OwnerID: 0,
  70. IsAdmin: true,
  71. RunnersTemplate: tplAdminRunners,
  72. RunnerEditTemplate: tplAdminRunnerEdit,
  73. RedirectLink: setting.AppSubURL + "/-/admin/actions/runners/",
  74. }, nil
  75. }
  76. if ctx.Data["PageIsUserSettings"] == true {
  77. return &runnersCtx{
  78. OwnerID: ctx.Doer.ID,
  79. RepoID: 0,
  80. IsUser: true,
  81. RunnersTemplate: tplUserRunners,
  82. RunnerEditTemplate: tplUserRunnerEdit,
  83. RedirectLink: setting.AppSubURL + "/user/settings/actions/runners/",
  84. }, nil
  85. }
  86. return nil, errors.New("unable to set Runners context")
  87. }
  88. // Runners render settings/actions/runners page for repo level
  89. func Runners(ctx *context.Context) {
  90. ctx.Data["PageIsSharedSettingsRunners"] = true
  91. ctx.Data["Title"] = ctx.Tr("actions.actions")
  92. ctx.Data["PageType"] = "runners"
  93. rCtx, err := getRunnersCtx(ctx)
  94. if err != nil {
  95. ctx.ServerError("getRunnersCtx", err)
  96. return
  97. }
  98. page := max(ctx.FormInt("page"), 1)
  99. opts := actions_model.FindRunnerOptions{
  100. ListOptions: db.ListOptions{
  101. Page: page,
  102. PageSize: 100,
  103. },
  104. Sort: ctx.Req.URL.Query().Get("sort"),
  105. Filter: ctx.Req.URL.Query().Get("q"),
  106. }
  107. if rCtx.IsRepo {
  108. opts.RepoID = rCtx.RepoID
  109. opts.WithAvailable = true
  110. } else if rCtx.IsOrg || rCtx.IsUser {
  111. opts.OwnerID = rCtx.OwnerID
  112. opts.WithAvailable = true
  113. }
  114. runners, count, err := db.FindAndCount[actions_model.ActionRunner](ctx, opts)
  115. if err != nil {
  116. ctx.ServerError("CountRunners", err)
  117. return
  118. }
  119. if err := actions_model.RunnerList(runners).LoadAttributes(ctx); err != nil {
  120. ctx.ServerError("LoadAttributes", err)
  121. return
  122. }
  123. // ownid=0,repo_id=0,means this token is used for global
  124. var token *actions_model.ActionRunnerToken
  125. token, err = actions_model.GetLatestRunnerToken(ctx, opts.OwnerID, opts.RepoID)
  126. if errors.Is(err, util.ErrNotExist) || (token != nil && !token.IsActive) {
  127. token, err = actions_model.NewRunnerToken(ctx, opts.OwnerID, opts.RepoID)
  128. if err != nil {
  129. ctx.ServerError("CreateRunnerToken", err)
  130. return
  131. }
  132. } else if err != nil {
  133. ctx.ServerError("GetLatestRunnerToken", err)
  134. return
  135. }
  136. ctx.Data["Keyword"] = opts.Filter
  137. ctx.Data["Runners"] = runners
  138. ctx.Data["Total"] = count
  139. ctx.Data["RegistrationToken"] = token.Token
  140. ctx.Data["RunnerOwnerID"] = opts.OwnerID
  141. ctx.Data["RunnerRepoID"] = opts.RepoID
  142. ctx.Data["SortType"] = opts.Sort
  143. pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
  144. ctx.Data["Page"] = pager
  145. ctx.HTML(http.StatusOK, rCtx.RunnersTemplate)
  146. }
  147. // RunnersEdit renders runner edit page for repository level
  148. func RunnersEdit(ctx *context.Context) {
  149. ctx.Data["PageIsSharedSettingsRunners"] = true
  150. ctx.Data["Title"] = ctx.Tr("actions.runners.edit_runner")
  151. rCtx, err := getRunnersCtx(ctx)
  152. if err != nil {
  153. ctx.ServerError("getRunnersCtx", err)
  154. return
  155. }
  156. page := max(ctx.FormInt("page"), 1)
  157. runnerID := ctx.PathParamInt64("runnerid")
  158. ownerID := rCtx.OwnerID
  159. repoID := rCtx.RepoID
  160. runner, err := actions_model.GetRunnerByID(ctx, runnerID)
  161. if err != nil {
  162. ctx.ServerError("GetRunnerByID", err)
  163. return
  164. }
  165. if err := runner.LoadAttributes(ctx); err != nil {
  166. ctx.ServerError("LoadAttributes", err)
  167. return
  168. }
  169. if !runner.EditableInContext(ownerID, repoID) {
  170. err = errors.New("no permission to edit this runner")
  171. ctx.NotFound(err)
  172. return
  173. }
  174. ctx.Data["Runner"] = runner
  175. opts := actions_model.FindTaskOptions{
  176. ListOptions: db.ListOptions{
  177. Page: page,
  178. PageSize: 30,
  179. },
  180. Status: actions_model.StatusUnknown, // Unknown means all
  181. RunnerID: runner.ID,
  182. }
  183. tasks, count, err := db.FindAndCount[actions_model.ActionTask](ctx, opts)
  184. if err != nil {
  185. ctx.ServerError("CountTasks", err)
  186. return
  187. }
  188. if err = actions_model.TaskList(tasks).LoadAttributes(ctx); err != nil {
  189. ctx.ServerError("TasksLoadAttributes", err)
  190. return
  191. }
  192. ctx.Data["Tasks"] = tasks
  193. pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
  194. ctx.Data["Page"] = pager
  195. ctx.HTML(http.StatusOK, rCtx.RunnerEditTemplate)
  196. }
  197. func RunnersEditPost(ctx *context.Context) {
  198. rCtx, err := getRunnersCtx(ctx)
  199. if err != nil {
  200. ctx.ServerError("getRunnersCtx", err)
  201. return
  202. }
  203. runnerID := ctx.PathParamInt64("runnerid")
  204. ownerID := rCtx.OwnerID
  205. repoID := rCtx.RepoID
  206. redirectTo := rCtx.RedirectLink
  207. runner, err := actions_model.GetRunnerByID(ctx, runnerID)
  208. if err != nil {
  209. log.Warn("RunnerDetailsEditPost.GetRunnerByID failed: %v, url: %s", err, ctx.Req.URL)
  210. ctx.ServerError("RunnerDetailsEditPost.GetRunnerByID", err)
  211. return
  212. }
  213. if !runner.EditableInContext(ownerID, repoID) {
  214. ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to edit this runner"))
  215. return
  216. }
  217. form := web.GetForm(ctx).(*forms.EditRunnerForm)
  218. runner.Description = form.Description
  219. err = actions_model.UpdateRunner(ctx, runner, "description")
  220. if err != nil {
  221. log.Warn("RunnerDetailsEditPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL)
  222. ctx.Flash.Warning(ctx.Tr("actions.runners.update_runner_failed"))
  223. ctx.Redirect(redirectTo)
  224. return
  225. }
  226. log.Debug("RunnerDetailsEditPost success: %s", ctx.Req.URL)
  227. ctx.Flash.Success(ctx.Tr("actions.runners.update_runner_success"))
  228. ctx.Redirect(redirectTo)
  229. }
  230. func ResetRunnerRegistrationToken(ctx *context.Context) {
  231. rCtx, err := getRunnersCtx(ctx)
  232. if err != nil {
  233. ctx.ServerError("getRunnersCtx", err)
  234. return
  235. }
  236. ownerID := rCtx.OwnerID
  237. repoID := rCtx.RepoID
  238. redirectTo := rCtx.RedirectLink
  239. if _, err := actions_model.NewRunnerToken(ctx, ownerID, repoID); err != nil {
  240. ctx.ServerError("ResetRunnerRegistrationToken", err)
  241. return
  242. }
  243. ctx.Flash.Success(ctx.Tr("actions.runners.reset_registration_token_success"))
  244. ctx.JSONRedirect(redirectTo)
  245. }
  246. // RunnerDeletePost response for deleting runner
  247. func RunnerDeletePost(ctx *context.Context) {
  248. rCtx, err := getRunnersCtx(ctx)
  249. if err != nil {
  250. ctx.ServerError("getRunnersCtx", err)
  251. return
  252. }
  253. runner := findActionsRunner(ctx, rCtx)
  254. if ctx.Written() {
  255. return
  256. }
  257. if !runner.EditableInContext(rCtx.OwnerID, rCtx.RepoID) {
  258. ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to delete this runner"))
  259. return
  260. }
  261. successRedirectTo := rCtx.RedirectLink
  262. failedRedirectTo := rCtx.RedirectLink + url.PathEscape(ctx.PathParam("runnerid"))
  263. if err := actions_model.DeleteRunner(ctx, runner.ID); err != nil {
  264. log.Warn("DeleteRunnerPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL)
  265. ctx.Flash.Warning(ctx.Tr("actions.runners.delete_runner_failed"))
  266. ctx.JSONRedirect(failedRedirectTo)
  267. return
  268. }
  269. log.Info("DeleteRunnerPost success: %s", ctx.Req.URL)
  270. ctx.Flash.Success(ctx.Tr("actions.runners.delete_runner_success"))
  271. ctx.JSONRedirect(successRedirectTo)
  272. }
  273. func RedirectToDefaultSetting(ctx *context.Context) {
  274. ctx.Redirect(ctx.Repo.RepoLink + "/settings/actions/runners")
  275. }
  276. func findActionsRunner(ctx *context.Context, rCtx *runnersCtx) *actions_model.ActionRunner {
  277. runnerID := ctx.PathParamInt64("runnerid")
  278. opts := &actions_model.FindRunnerOptions{
  279. IDs: []int64{runnerID},
  280. }
  281. switch {
  282. case rCtx.IsRepo:
  283. opts.RepoID = rCtx.RepoID
  284. if opts.RepoID == 0 {
  285. panic("repoID is 0")
  286. }
  287. case rCtx.IsOrg, rCtx.IsUser:
  288. opts.OwnerID = rCtx.OwnerID
  289. if opts.OwnerID == 0 {
  290. panic("ownerID is 0")
  291. }
  292. case rCtx.IsAdmin:
  293. // do nothing
  294. default:
  295. panic("invalid actions runner context")
  296. }
  297. got, err := db.Find[actions_model.ActionRunner](ctx, opts)
  298. if err != nil {
  299. ctx.ServerError("FindRunner", err)
  300. return nil
  301. } else if len(got) == 0 {
  302. ctx.NotFound(errors.New("runner not found"))
  303. return nil
  304. }
  305. return got[0]
  306. }