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.

release.go 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package repo
  5. import (
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/models/db"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. "code.gitea.io/gitea/models/unit"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/modules/base"
  15. "code.gitea.io/gitea/modules/context"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/markup"
  18. "code.gitea.io/gitea/modules/markup/markdown"
  19. "code.gitea.io/gitea/modules/setting"
  20. "code.gitea.io/gitea/modules/upload"
  21. "code.gitea.io/gitea/modules/util"
  22. "code.gitea.io/gitea/modules/web"
  23. "code.gitea.io/gitea/routers/web/feed"
  24. "code.gitea.io/gitea/services/forms"
  25. releaseservice "code.gitea.io/gitea/services/release"
  26. )
  27. const (
  28. tplReleases base.TplName = "repo/release/list"
  29. tplReleaseNew base.TplName = "repo/release/new"
  30. )
  31. // calReleaseNumCommitsBehind calculates given release has how many commits behind release target.
  32. func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model.Release, countCache map[string]int64) error {
  33. // Fast return if release target is same as default branch.
  34. if repoCtx.BranchName == release.Target {
  35. release.NumCommitsBehind = repoCtx.CommitsCount - release.NumCommits
  36. return nil
  37. }
  38. // Get count if not exists
  39. if _, ok := countCache[release.Target]; !ok {
  40. if repoCtx.GitRepo.IsBranchExist(release.Target) {
  41. commit, err := repoCtx.GitRepo.GetBranchCommit(release.Target)
  42. if err != nil {
  43. return fmt.Errorf("GetBranchCommit: %w", err)
  44. }
  45. countCache[release.Target], err = commit.CommitsCount()
  46. if err != nil {
  47. return fmt.Errorf("CommitsCount: %w", err)
  48. }
  49. } else {
  50. // Use NumCommits of the newest release on that target
  51. countCache[release.Target] = release.NumCommits
  52. }
  53. }
  54. release.NumCommitsBehind = countCache[release.Target] - release.NumCommits
  55. return nil
  56. }
  57. // Releases render releases list page
  58. func Releases(ctx *context.Context) {
  59. releasesOrTags(ctx, false)
  60. }
  61. // TagsList render tags list page
  62. func TagsList(ctx *context.Context) {
  63. releasesOrTags(ctx, true)
  64. }
  65. func releasesOrTags(ctx *context.Context, isTagList bool) {
  66. ctx.Data["PageIsReleaseList"] = true
  67. ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch
  68. ctx.Data["IsViewBranch"] = false
  69. ctx.Data["IsViewTag"] = true
  70. // Disable the showCreateNewBranch form in the dropdown on this page.
  71. ctx.Data["CanCreateBranch"] = false
  72. ctx.Data["HideBranchesInDropdown"] = true
  73. if isTagList {
  74. ctx.Data["Title"] = ctx.Tr("repo.release.tags")
  75. ctx.Data["PageIsTagList"] = true
  76. } else {
  77. ctx.Data["Title"] = ctx.Tr("repo.release.releases")
  78. ctx.Data["PageIsTagList"] = false
  79. }
  80. listOptions := db.ListOptions{
  81. Page: ctx.FormInt("page"),
  82. PageSize: ctx.FormInt("limit"),
  83. }
  84. if listOptions.PageSize == 0 {
  85. listOptions.PageSize = setting.Repository.Release.DefaultPagingNum
  86. }
  87. if listOptions.PageSize > setting.API.MaxResponseItems {
  88. listOptions.PageSize = setting.API.MaxResponseItems
  89. }
  90. // TODO(20073) tags are used for compare feature which needs all tags
  91. // filtering is done on the client-side atm
  92. tagListStart, tagListEnd := 0, 0
  93. if isTagList {
  94. tagListStart, tagListEnd = listOptions.GetStartEnd()
  95. }
  96. tags, err := ctx.Repo.GitRepo.GetTags(tagListStart, tagListEnd)
  97. if err != nil {
  98. ctx.ServerError("GetTags", err)
  99. return
  100. }
  101. ctx.Data["Tags"] = tags
  102. writeAccess := ctx.Repo.CanWrite(unit.TypeReleases)
  103. ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
  104. opts := repo_model.FindReleasesOptions{
  105. ListOptions: listOptions,
  106. }
  107. if isTagList {
  108. // for the tags list page, show all releases with real tags (having real commit-id),
  109. // the drafts should also be included because a real tag might be used as a draft.
  110. opts.IncludeDrafts = true
  111. opts.IncludeTags = true
  112. opts.HasSha1 = util.OptionalBoolTrue
  113. } else {
  114. // only show draft releases for users who can write, read-only users shouldn't see draft releases.
  115. opts.IncludeDrafts = writeAccess
  116. }
  117. releases, err := repo_model.GetReleasesByRepoID(ctx, ctx.Repo.Repository.ID, opts)
  118. if err != nil {
  119. ctx.ServerError("GetReleasesByRepoID", err)
  120. return
  121. }
  122. count, err := repo_model.GetReleaseCountByRepoID(ctx, ctx.Repo.Repository.ID, opts)
  123. if err != nil {
  124. ctx.ServerError("GetReleaseCountByRepoID", err)
  125. return
  126. }
  127. if err = repo_model.GetReleaseAttachments(ctx, releases...); err != nil {
  128. ctx.ServerError("GetReleaseAttachments", err)
  129. return
  130. }
  131. // Temporary cache commits count of used branches to speed up.
  132. countCache := make(map[string]int64)
  133. cacheUsers := make(map[int64]*user_model.User)
  134. if ctx.Doer != nil {
  135. cacheUsers[ctx.Doer.ID] = ctx.Doer
  136. }
  137. var ok bool
  138. for _, r := range releases {
  139. if r.Publisher, ok = cacheUsers[r.PublisherID]; !ok {
  140. r.Publisher, err = user_model.GetUserByID(ctx, r.PublisherID)
  141. if err != nil {
  142. if user_model.IsErrUserNotExist(err) {
  143. r.Publisher = user_model.NewGhostUser()
  144. } else {
  145. ctx.ServerError("GetUserByID", err)
  146. return
  147. }
  148. }
  149. cacheUsers[r.PublisherID] = r.Publisher
  150. }
  151. r.Note, err = markdown.RenderString(&markup.RenderContext{
  152. URLPrefix: ctx.Repo.RepoLink,
  153. Metas: ctx.Repo.Repository.ComposeMetas(),
  154. GitRepo: ctx.Repo.GitRepo,
  155. Ctx: ctx,
  156. }, r.Note)
  157. if err != nil {
  158. ctx.ServerError("RenderString", err)
  159. return
  160. }
  161. if r.IsDraft {
  162. continue
  163. }
  164. if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil {
  165. ctx.ServerError("calReleaseNumCommitsBehind", err)
  166. return
  167. }
  168. }
  169. ctx.Data["Releases"] = releases
  170. ctx.Data["ReleasesNum"] = len(releases)
  171. pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
  172. pager.SetDefaultParams(ctx)
  173. ctx.Data["Page"] = pager
  174. ctx.HTML(http.StatusOK, tplReleases)
  175. }
  176. // ReleasesFeedRSS get feeds for releases in RSS format
  177. func ReleasesFeedRSS(ctx *context.Context) {
  178. releasesOrTagsFeed(ctx, true, "rss")
  179. }
  180. // TagsListFeedRSS get feeds for tags in RSS format
  181. func TagsListFeedRSS(ctx *context.Context) {
  182. releasesOrTagsFeed(ctx, false, "rss")
  183. }
  184. // ReleasesFeedAtom get feeds for releases in Atom format
  185. func ReleasesFeedAtom(ctx *context.Context) {
  186. releasesOrTagsFeed(ctx, true, "atom")
  187. }
  188. // TagsListFeedAtom get feeds for tags in RSS format
  189. func TagsListFeedAtom(ctx *context.Context) {
  190. releasesOrTagsFeed(ctx, false, "atom")
  191. }
  192. func releasesOrTagsFeed(ctx *context.Context, isReleasesOnly bool, formatType string) {
  193. feed.ShowReleaseFeed(ctx, ctx.Repo.Repository, isReleasesOnly, formatType)
  194. }
  195. // SingleRelease renders a single release's page
  196. func SingleRelease(ctx *context.Context) {
  197. ctx.Data["Title"] = ctx.Tr("repo.release.releases")
  198. ctx.Data["PageIsReleaseList"] = true
  199. writeAccess := ctx.Repo.CanWrite(unit.TypeReleases)
  200. ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
  201. release, err := repo_model.GetRelease(ctx.Repo.Repository.ID, ctx.Params("*"))
  202. if err != nil {
  203. if repo_model.IsErrReleaseNotExist(err) {
  204. ctx.NotFound("GetRelease", err)
  205. return
  206. }
  207. ctx.ServerError("GetReleasesByRepoID", err)
  208. return
  209. }
  210. err = repo_model.GetReleaseAttachments(ctx, release)
  211. if err != nil {
  212. ctx.ServerError("GetReleaseAttachments", err)
  213. return
  214. }
  215. release.Publisher, err = user_model.GetUserByID(ctx, release.PublisherID)
  216. if err != nil {
  217. if user_model.IsErrUserNotExist(err) {
  218. release.Publisher = user_model.NewGhostUser()
  219. } else {
  220. ctx.ServerError("GetUserByID", err)
  221. return
  222. }
  223. }
  224. if !release.IsDraft {
  225. if err := calReleaseNumCommitsBehind(ctx.Repo, release, make(map[string]int64)); err != nil {
  226. ctx.ServerError("calReleaseNumCommitsBehind", err)
  227. return
  228. }
  229. }
  230. release.Note, err = markdown.RenderString(&markup.RenderContext{
  231. URLPrefix: ctx.Repo.RepoLink,
  232. Metas: ctx.Repo.Repository.ComposeMetas(),
  233. GitRepo: ctx.Repo.GitRepo,
  234. Ctx: ctx,
  235. }, release.Note)
  236. if err != nil {
  237. ctx.ServerError("RenderString", err)
  238. return
  239. }
  240. ctx.Data["Releases"] = []*repo_model.Release{release}
  241. ctx.HTML(http.StatusOK, tplReleases)
  242. }
  243. // LatestRelease redirects to the latest release
  244. func LatestRelease(ctx *context.Context) {
  245. release, err := repo_model.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID)
  246. if err != nil {
  247. if repo_model.IsErrReleaseNotExist(err) {
  248. ctx.NotFound("LatestRelease", err)
  249. return
  250. }
  251. ctx.ServerError("GetLatestReleaseByRepoID", err)
  252. return
  253. }
  254. if err := release.LoadAttributes(ctx); err != nil {
  255. ctx.ServerError("LoadAttributes", err)
  256. return
  257. }
  258. ctx.Redirect(release.HTMLURL())
  259. }
  260. // NewRelease render creating or edit release page
  261. func NewRelease(ctx *context.Context) {
  262. ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
  263. ctx.Data["PageIsReleaseList"] = true
  264. ctx.Data["RequireTribute"] = true
  265. ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
  266. if tagName := ctx.FormString("tag"); len(tagName) > 0 {
  267. rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
  268. if err != nil && !repo_model.IsErrReleaseNotExist(err) {
  269. ctx.ServerError("GetRelease", err)
  270. return
  271. }
  272. if rel != nil {
  273. rel.Repo = ctx.Repo.Repository
  274. if err := rel.LoadAttributes(ctx); err != nil {
  275. ctx.ServerError("LoadAttributes", err)
  276. return
  277. }
  278. ctx.Data["tag_name"] = rel.TagName
  279. if rel.Target != "" {
  280. ctx.Data["tag_target"] = rel.Target
  281. }
  282. ctx.Data["title"] = rel.Title
  283. ctx.Data["content"] = rel.Note
  284. ctx.Data["attachments"] = rel.Attachments
  285. }
  286. }
  287. ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
  288. var err error
  289. // Get assignees.
  290. ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
  291. if err != nil {
  292. ctx.ServerError("GetAssignees", err)
  293. return
  294. }
  295. upload.AddUploadContext(ctx, "release")
  296. ctx.HTML(http.StatusOK, tplReleaseNew)
  297. }
  298. // NewReleasePost response for creating a release
  299. func NewReleasePost(ctx *context.Context) {
  300. form := web.GetForm(ctx).(*forms.NewReleaseForm)
  301. ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
  302. ctx.Data["PageIsReleaseList"] = true
  303. ctx.Data["RequireTribute"] = true
  304. if ctx.HasError() {
  305. ctx.HTML(http.StatusOK, tplReleaseNew)
  306. return
  307. }
  308. if !ctx.Repo.GitRepo.IsBranchExist(form.Target) {
  309. ctx.RenderWithErr(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form)
  310. return
  311. }
  312. var attachmentUUIDs []string
  313. if setting.Attachment.Enabled {
  314. attachmentUUIDs = form.Files
  315. }
  316. rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, form.TagName)
  317. if err != nil {
  318. if !repo_model.IsErrReleaseNotExist(err) {
  319. ctx.ServerError("GetRelease", err)
  320. return
  321. }
  322. msg := ""
  323. if len(form.Title) > 0 && form.AddTagMsg {
  324. msg = form.Title + "\n\n" + form.Content
  325. }
  326. if len(form.TagOnly) > 0 {
  327. if err = releaseservice.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, msg); err != nil {
  328. if models.IsErrTagAlreadyExists(err) {
  329. e := err.(models.ErrTagAlreadyExists)
  330. ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
  331. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  332. return
  333. }
  334. if models.IsErrInvalidTagName(err) {
  335. ctx.Flash.Error(ctx.Tr("repo.release.tag_name_invalid"))
  336. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  337. return
  338. }
  339. if models.IsErrProtectedTagName(err) {
  340. ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
  341. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  342. return
  343. }
  344. ctx.ServerError("releaseservice.CreateNewTag", err)
  345. return
  346. }
  347. ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
  348. ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
  349. return
  350. }
  351. rel = &repo_model.Release{
  352. RepoID: ctx.Repo.Repository.ID,
  353. Repo: ctx.Repo.Repository,
  354. PublisherID: ctx.Doer.ID,
  355. Publisher: ctx.Doer,
  356. Title: form.Title,
  357. TagName: form.TagName,
  358. Target: form.Target,
  359. Note: form.Content,
  360. IsDraft: len(form.Draft) > 0,
  361. IsPrerelease: form.Prerelease,
  362. IsTag: false,
  363. }
  364. if err = releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil {
  365. ctx.Data["Err_TagName"] = true
  366. switch {
  367. case repo_model.IsErrReleaseAlreadyExist(err):
  368. ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
  369. case models.IsErrInvalidTagName(err):
  370. ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
  371. case models.IsErrProtectedTagName(err):
  372. ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form)
  373. default:
  374. ctx.ServerError("CreateRelease", err)
  375. }
  376. return
  377. }
  378. } else {
  379. if !rel.IsTag {
  380. ctx.Data["Err_TagName"] = true
  381. ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
  382. return
  383. }
  384. rel.Title = form.Title
  385. rel.Note = form.Content
  386. rel.Target = form.Target
  387. rel.IsDraft = len(form.Draft) > 0
  388. rel.IsPrerelease = form.Prerelease
  389. rel.PublisherID = ctx.Doer.ID
  390. rel.IsTag = false
  391. if err = releaseservice.UpdateRelease(ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil {
  392. ctx.Data["Err_TagName"] = true
  393. ctx.ServerError("UpdateRelease", err)
  394. return
  395. }
  396. }
  397. log.Trace("Release created: %s/%s:%s", ctx.Doer.LowerName, ctx.Repo.Repository.Name, form.TagName)
  398. ctx.Redirect(ctx.Repo.RepoLink + "/releases")
  399. }
  400. // EditRelease render release edit page
  401. func EditRelease(ctx *context.Context) {
  402. ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
  403. ctx.Data["PageIsReleaseList"] = true
  404. ctx.Data["PageIsEditRelease"] = true
  405. ctx.Data["RequireTribute"] = true
  406. ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
  407. upload.AddUploadContext(ctx, "release")
  408. tagName := ctx.Params("*")
  409. rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
  410. if err != nil {
  411. if repo_model.IsErrReleaseNotExist(err) {
  412. ctx.NotFound("GetRelease", err)
  413. } else {
  414. ctx.ServerError("GetRelease", err)
  415. }
  416. return
  417. }
  418. ctx.Data["ID"] = rel.ID
  419. ctx.Data["tag_name"] = rel.TagName
  420. ctx.Data["tag_target"] = rel.Target
  421. ctx.Data["title"] = rel.Title
  422. ctx.Data["content"] = rel.Note
  423. ctx.Data["prerelease"] = rel.IsPrerelease
  424. ctx.Data["IsDraft"] = rel.IsDraft
  425. rel.Repo = ctx.Repo.Repository
  426. if err := rel.LoadAttributes(ctx); err != nil {
  427. ctx.ServerError("LoadAttributes", err)
  428. return
  429. }
  430. ctx.Data["attachments"] = rel.Attachments
  431. // Get assignees.
  432. ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, rel.Repo)
  433. if err != nil {
  434. ctx.ServerError("GetAssignees", err)
  435. return
  436. }
  437. ctx.HTML(http.StatusOK, tplReleaseNew)
  438. }
  439. // EditReleasePost response for edit release
  440. func EditReleasePost(ctx *context.Context) {
  441. form := web.GetForm(ctx).(*forms.EditReleaseForm)
  442. ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
  443. ctx.Data["PageIsReleaseList"] = true
  444. ctx.Data["PageIsEditRelease"] = true
  445. ctx.Data["RequireTribute"] = true
  446. tagName := ctx.Params("*")
  447. rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
  448. if err != nil {
  449. if repo_model.IsErrReleaseNotExist(err) {
  450. ctx.NotFound("GetRelease", err)
  451. } else {
  452. ctx.ServerError("GetRelease", err)
  453. }
  454. return
  455. }
  456. if rel.IsTag {
  457. ctx.NotFound("GetRelease", err)
  458. return
  459. }
  460. ctx.Data["tag_name"] = rel.TagName
  461. ctx.Data["tag_target"] = rel.Target
  462. ctx.Data["title"] = rel.Title
  463. ctx.Data["content"] = rel.Note
  464. ctx.Data["prerelease"] = rel.IsPrerelease
  465. if ctx.HasError() {
  466. ctx.HTML(http.StatusOK, tplReleaseNew)
  467. return
  468. }
  469. const delPrefix = "attachment-del-"
  470. const editPrefix = "attachment-edit-"
  471. var addAttachmentUUIDs, delAttachmentUUIDs []string
  472. editAttachments := make(map[string]string) // uuid -> new name
  473. if setting.Attachment.Enabled {
  474. addAttachmentUUIDs = form.Files
  475. for k, v := range ctx.Req.Form {
  476. if strings.HasPrefix(k, delPrefix) && v[0] == "true" {
  477. delAttachmentUUIDs = append(delAttachmentUUIDs, k[len(delPrefix):])
  478. } else if strings.HasPrefix(k, editPrefix) {
  479. editAttachments[k[len(editPrefix):]] = v[0]
  480. }
  481. }
  482. }
  483. rel.Title = form.Title
  484. rel.Note = form.Content
  485. rel.IsDraft = len(form.Draft) > 0
  486. rel.IsPrerelease = form.Prerelease
  487. if err = releaseservice.UpdateRelease(ctx.Doer, ctx.Repo.GitRepo,
  488. rel, addAttachmentUUIDs, delAttachmentUUIDs, editAttachments); err != nil {
  489. ctx.ServerError("UpdateRelease", err)
  490. return
  491. }
  492. ctx.Redirect(ctx.Repo.RepoLink + "/releases")
  493. }
  494. // DeleteRelease deletes a release
  495. func DeleteRelease(ctx *context.Context) {
  496. deleteReleaseOrTag(ctx, false)
  497. }
  498. // DeleteTag deletes a tag
  499. func DeleteTag(ctx *context.Context) {
  500. deleteReleaseOrTag(ctx, true)
  501. }
  502. func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
  503. if err := releaseservice.DeleteReleaseByID(ctx, ctx.FormInt64("id"), ctx.Doer, isDelTag); err != nil {
  504. if models.IsErrProtectedTagName(err) {
  505. ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
  506. } else {
  507. ctx.Flash.Error("DeleteReleaseByID: " + err.Error())
  508. }
  509. } else {
  510. if isDelTag {
  511. ctx.Flash.Success(ctx.Tr("repo.release.deletion_tag_success"))
  512. } else {
  513. ctx.Flash.Success(ctx.Tr("repo.release.deletion_success"))
  514. }
  515. }
  516. if isDelTag {
  517. ctx.JSON(http.StatusOK, map[string]interface{}{
  518. "redirect": ctx.Repo.RepoLink + "/tags",
  519. })
  520. return
  521. }
  522. ctx.JSON(http.StatusOK, map[string]interface{}{
  523. "redirect": ctx.Repo.RepoLink + "/releases",
  524. })
  525. }