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 11KB

Add a storage layer for attachments (#11387) * Add a storage layer for attachments * Fix some bug * fix test * Fix copyright head and lint * Fix bug * Add setting for minio and flags for migrate-storage * Add documents * fix lint * Add test for minio store type on attachments * fix test * fix test * Apply suggestions from code review Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> * Add warning when storage migrated successfully * Fix drone * fix test * rebase * Fix test * display the error on console * Move minio test to amd64 since minio docker don't support arm64 * refactor the codes * add trace * Fix test * remove log on xorm * Fi download bug * Add a storage layer for attachments * Add setting for minio and flags for migrate-storage * fix lint * Add test for minio store type on attachments * Apply suggestions from code review Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> * Fix drone * fix test * Fix test * display the error on console * Move minio test to amd64 since minio docker don't support arm64 * refactor the codes * add trace * Fix test * Add URL function to serve attachments directly from S3/Minio * Add ability to enable/disable redirection in attachment configuration * Fix typo * Add a storage layer for attachments * Add setting for minio and flags for migrate-storage * fix lint * Add test for minio store type on attachments * Apply suggestions from code review Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> * Fix drone * fix test * Fix test * display the error on console * Move minio test to amd64 since minio docker don't support arm64 * don't change unrelated files * Fix lint * Fix build * update go.mod and go.sum * Use github.com/minio/minio-go/v6 * Remove unused function * Upgrade minio to v7 and some other improvements * fix lint * Fix go mod Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: Tyler <tystuyfzand@gmail.com>
3 years ago
Use the type RefName for all the needed places and fix pull mirror sync bugs (#24634) This PR replaces all string refName as a type `git.RefName` to make the code more maintainable. Fix #15367 Replaces #23070 It also fixed a bug that tags are not sync because `git remote --prune origin` will not remove local tags if remote removed. We in fact should use `git fetch --prune --tags origin` but not `git remote update origin` to do the sync. Some answer from ChatGPT as ref. > If the git fetch --prune --tags command is not working as expected, there could be a few reasons why. Here are a few things to check: > >Make sure that you have the latest version of Git installed on your system. You can check the version by running git --version in your terminal. If you have an outdated version, try updating Git and see if that resolves the issue. > >Check that your Git repository is properly configured to track the remote repository's tags. You can check this by running git config --get-all remote.origin.fetch and verifying that it includes +refs/tags/*:refs/tags/*. If it does not, you can add it by running git config --add remote.origin.fetch "+refs/tags/*:refs/tags/*". > >Verify that the tags you are trying to prune actually exist on the remote repository. You can do this by running git ls-remote --tags origin to list all the tags on the remote repository. > >Check if any local tags have been created that match the names of tags on the remote repository. If so, these local tags may be preventing the git fetch --prune --tags command from working properly. You can delete local tags using the git tag -d command. --------- Co-authored-by: delvh <dev.lh@web.de>
1 year ago
Use the type RefName for all the needed places and fix pull mirror sync bugs (#24634) This PR replaces all string refName as a type `git.RefName` to make the code more maintainable. Fix #15367 Replaces #23070 It also fixed a bug that tags are not sync because `git remote --prune origin` will not remove local tags if remote removed. We in fact should use `git fetch --prune --tags origin` but not `git remote update origin` to do the sync. Some answer from ChatGPT as ref. > If the git fetch --prune --tags command is not working as expected, there could be a few reasons why. Here are a few things to check: > >Make sure that you have the latest version of Git installed on your system. You can check the version by running git --version in your terminal. If you have an outdated version, try updating Git and see if that resolves the issue. > >Check that your Git repository is properly configured to track the remote repository's tags. You can check this by running git config --get-all remote.origin.fetch and verifying that it includes +refs/tags/*:refs/tags/*. If it does not, you can add it by running git config --add remote.origin.fetch "+refs/tags/*:refs/tags/*". > >Verify that the tags you are trying to prune actually exist on the remote repository. You can do this by running git ls-remote --tags origin to list all the tags on the remote repository. > >Check if any local tags have been created that match the names of tags on the remote repository. If so, these local tags may be preventing the git fetch --prune --tags command from working properly. You can delete local tags using the git tag -d command. --------- Co-authored-by: delvh <dev.lh@web.de>
1 year ago
Add context cache as a request level cache (#22294) To avoid duplicated load of the same data in an HTTP request, we can set a context cache to do that. i.e. Some pages may load a user from a database with the same id in different areas on the same page. But the code is hidden in two different deep logic. How should we share the user? As a result of this PR, now if both entry functions accept `context.Context` as the first parameter and we just need to refactor `GetUserByID` to reuse the user from the context cache. Then it will not be loaded twice on an HTTP request. But of course, sometimes we would like to reload an object from the database, that's why `RemoveContextData` is also exposed. The core context cache is here. It defines a new context ```go type cacheContext struct { ctx context.Context data map[any]map[any]any lock sync.RWMutex } var cacheContextKey = struct{}{} func WithCacheContext(ctx context.Context) context.Context { return context.WithValue(ctx, cacheContextKey, &cacheContext{ ctx: ctx, data: make(map[any]map[any]any), }) } ``` Then you can use the below 4 methods to read/write/del the data within the same context. ```go func GetContextData(ctx context.Context, tp, key any) any func SetContextData(ctx context.Context, tp, key, value any) func RemoveContextData(ctx context.Context, tp, key any) func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) ``` Then let's take a look at how `system.GetString` implement it. ```go func GetSetting(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(ctx, key) if err != nil { return "", err } return res.SettingValue, nil }) }) } ``` First, it will check if context data include the setting object with the key. If not, it will query from the global cache which may be memory or a Redis cache. If not, it will get the object from the database. In the end, if the object gets from the global cache or database, it will be set into the context cache. An object stored in the context cache will only be destroyed after the context disappeared.
1 year ago
Use the type RefName for all the needed places and fix pull mirror sync bugs (#24634) This PR replaces all string refName as a type `git.RefName` to make the code more maintainable. Fix #15367 Replaces #23070 It also fixed a bug that tags are not sync because `git remote --prune origin` will not remove local tags if remote removed. We in fact should use `git fetch --prune --tags origin` but not `git remote update origin` to do the sync. Some answer from ChatGPT as ref. > If the git fetch --prune --tags command is not working as expected, there could be a few reasons why. Here are a few things to check: > >Make sure that you have the latest version of Git installed on your system. You can check the version by running git --version in your terminal. If you have an outdated version, try updating Git and see if that resolves the issue. > >Check that your Git repository is properly configured to track the remote repository's tags. You can check this by running git config --get-all remote.origin.fetch and verifying that it includes +refs/tags/*:refs/tags/*. If it does not, you can add it by running git config --add remote.origin.fetch "+refs/tags/*:refs/tags/*". > >Verify that the tags you are trying to prune actually exist on the remote repository. You can do this by running git ls-remote --tags origin to list all the tags on the remote repository. > >Check if any local tags have been created that match the names of tags on the remote repository. If so, these local tags may be preventing the git fetch --prune --tags command from working properly. You can delete local tags using the git tag -d command. --------- Co-authored-by: delvh <dev.lh@web.de>
1 year ago
Use the type RefName for all the needed places and fix pull mirror sync bugs (#24634) This PR replaces all string refName as a type `git.RefName` to make the code more maintainable. Fix #15367 Replaces #23070 It also fixed a bug that tags are not sync because `git remote --prune origin` will not remove local tags if remote removed. We in fact should use `git fetch --prune --tags origin` but not `git remote update origin` to do the sync. Some answer from ChatGPT as ref. > If the git fetch --prune --tags command is not working as expected, there could be a few reasons why. Here are a few things to check: > >Make sure that you have the latest version of Git installed on your system. You can check the version by running git --version in your terminal. If you have an outdated version, try updating Git and see if that resolves the issue. > >Check that your Git repository is properly configured to track the remote repository's tags. You can check this by running git config --get-all remote.origin.fetch and verifying that it includes +refs/tags/*:refs/tags/*. If it does not, you can add it by running git config --add remote.origin.fetch "+refs/tags/*:refs/tags/*". > >Verify that the tags you are trying to prune actually exist on the remote repository. You can do this by running git ls-remote --tags origin to list all the tags on the remote repository. > >Check if any local tags have been created that match the names of tags on the remote repository. If so, these local tags may be preventing the git fetch --prune --tags command from working properly. You can delete local tags using the git tag -d command. --------- Co-authored-by: delvh <dev.lh@web.de>
1 year ago
Add a storage layer for attachments (#11387) * Add a storage layer for attachments * Fix some bug * fix test * Fix copyright head and lint * Fix bug * Add setting for minio and flags for migrate-storage * Add documents * fix lint * Add test for minio store type on attachments * fix test * fix test * Apply suggestions from code review Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> * Add warning when storage migrated successfully * Fix drone * fix test * rebase * Fix test * display the error on console * Move minio test to amd64 since minio docker don't support arm64 * refactor the codes * add trace * Fix test * remove log on xorm * Fi download bug * Add a storage layer for attachments * Add setting for minio and flags for migrate-storage * fix lint * Add test for minio store type on attachments * Apply suggestions from code review Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> * Fix drone * fix test * Fix test * display the error on console * Move minio test to amd64 since minio docker don't support arm64 * refactor the codes * add trace * Fix test * Add URL function to serve attachments directly from S3/Minio * Add ability to enable/disable redirection in attachment configuration * Fix typo * Add a storage layer for attachments * Add setting for minio and flags for migrate-storage * fix lint * Add test for minio store type on attachments * Apply suggestions from code review Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> * Fix drone * fix test * Fix test * display the error on console * Move minio test to amd64 since minio docker don't support arm64 * don't change unrelated files * Fix lint * Fix build * update go.mod and go.sum * Use github.com/minio/minio-go/v6 * Remove unused function * Upgrade minio to v7 and some other improvements * fix lint * Fix go mod Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: Tyler <tystuyfzand@gmail.com>
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package release
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "strings"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/models/db"
  11. git_model "code.gitea.io/gitea/models/git"
  12. repo_model "code.gitea.io/gitea/models/repo"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/modules/container"
  15. "code.gitea.io/gitea/modules/git"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/repository"
  18. "code.gitea.io/gitea/modules/storage"
  19. "code.gitea.io/gitea/modules/timeutil"
  20. "code.gitea.io/gitea/modules/util"
  21. notify_service "code.gitea.io/gitea/services/notify"
  22. )
  23. func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Release, msg string) (bool, error) {
  24. err := rel.LoadAttributes(ctx)
  25. if err != nil {
  26. return false, err
  27. }
  28. err = rel.Repo.MustNotBeArchived()
  29. if err != nil {
  30. return false, err
  31. }
  32. var created bool
  33. // Only actual create when publish.
  34. if !rel.IsDraft {
  35. if !gitRepo.IsTagExist(rel.TagName) {
  36. if err := rel.LoadAttributes(ctx); err != nil {
  37. log.Error("LoadAttributes: %v", err)
  38. return false, err
  39. }
  40. protectedTags, err := git_model.GetProtectedTags(ctx, rel.Repo.ID)
  41. if err != nil {
  42. return false, fmt.Errorf("GetProtectedTags: %w", err)
  43. }
  44. // Trim '--' prefix to prevent command line argument vulnerability.
  45. rel.TagName = strings.TrimPrefix(rel.TagName, "--")
  46. isAllowed, err := git_model.IsUserAllowedToControlTag(ctx, protectedTags, rel.TagName, rel.PublisherID)
  47. if err != nil {
  48. return false, err
  49. }
  50. if !isAllowed {
  51. return false, models.ErrProtectedTagName{
  52. TagName: rel.TagName,
  53. }
  54. }
  55. commit, err := gitRepo.GetCommit(rel.Target)
  56. if err != nil {
  57. return false, fmt.Errorf("createTag::GetCommit[%v]: %w", rel.Target, err)
  58. }
  59. if len(msg) > 0 {
  60. if err = gitRepo.CreateAnnotatedTag(rel.TagName, msg, commit.ID.String()); err != nil {
  61. if strings.Contains(err.Error(), "is not a valid tag name") {
  62. return false, models.ErrInvalidTagName{
  63. TagName: rel.TagName,
  64. }
  65. }
  66. return false, err
  67. }
  68. } else if err = gitRepo.CreateTag(rel.TagName, commit.ID.String()); err != nil {
  69. if strings.Contains(err.Error(), "is not a valid tag name") {
  70. return false, models.ErrInvalidTagName{
  71. TagName: rel.TagName,
  72. }
  73. }
  74. return false, err
  75. }
  76. created = true
  77. rel.LowerTagName = strings.ToLower(rel.TagName)
  78. commits := repository.NewPushCommits()
  79. commits.HeadCommit = repository.CommitToPushCommit(commit)
  80. commits.CompareURL = rel.Repo.ComposeCompareURL(git.EmptySHA, commit.ID.String())
  81. refFullName := git.RefNameFromTag(rel.TagName)
  82. notify_service.PushCommits(
  83. ctx, rel.Publisher, rel.Repo,
  84. &repository.PushUpdateOptions{
  85. RefFullName: refFullName,
  86. OldCommitID: git.EmptySHA,
  87. NewCommitID: commit.ID.String(),
  88. }, commits)
  89. notify_service.CreateRef(ctx, rel.Publisher, rel.Repo, refFullName, commit.ID.String())
  90. rel.CreatedUnix = timeutil.TimeStampNow()
  91. }
  92. commit, err := gitRepo.GetTagCommit(rel.TagName)
  93. if err != nil {
  94. return false, fmt.Errorf("GetTagCommit: %w", err)
  95. }
  96. rel.Sha1 = commit.ID.String()
  97. rel.NumCommits, err = commit.CommitsCount()
  98. if err != nil {
  99. return false, fmt.Errorf("CommitsCount: %w", err)
  100. }
  101. if rel.PublisherID <= 0 {
  102. u, err := user_model.GetUserByEmail(ctx, commit.Author.Email)
  103. if err == nil {
  104. rel.PublisherID = u.ID
  105. }
  106. }
  107. } else {
  108. rel.CreatedUnix = timeutil.TimeStampNow()
  109. }
  110. return created, nil
  111. }
  112. // CreateRelease creates a new release of repository.
  113. func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, attachmentUUIDs []string, msg string) error {
  114. has, err := repo_model.IsReleaseExist(gitRepo.Ctx, rel.RepoID, rel.TagName)
  115. if err != nil {
  116. return err
  117. } else if has {
  118. return repo_model.ErrReleaseAlreadyExist{
  119. TagName: rel.TagName,
  120. }
  121. }
  122. if _, err = createTag(gitRepo.Ctx, gitRepo, rel, msg); err != nil {
  123. return err
  124. }
  125. rel.LowerTagName = strings.ToLower(rel.TagName)
  126. if err = db.Insert(gitRepo.Ctx, rel); err != nil {
  127. return err
  128. }
  129. if err = repo_model.AddReleaseAttachments(gitRepo.Ctx, rel.ID, attachmentUUIDs); err != nil {
  130. return err
  131. }
  132. if !rel.IsDraft {
  133. notify_service.NewRelease(gitRepo.Ctx, rel)
  134. }
  135. return nil
  136. }
  137. // CreateNewTag creates a new repository tag
  138. func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commit, tagName, msg string) error {
  139. has, err := repo_model.IsReleaseExist(ctx, repo.ID, tagName)
  140. if err != nil {
  141. return err
  142. } else if has {
  143. return models.ErrTagAlreadyExists{
  144. TagName: tagName,
  145. }
  146. }
  147. gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath())
  148. if err != nil {
  149. return err
  150. }
  151. defer closer.Close()
  152. rel := &repo_model.Release{
  153. RepoID: repo.ID,
  154. Repo: repo,
  155. PublisherID: doer.ID,
  156. Publisher: doer,
  157. TagName: tagName,
  158. Target: commit,
  159. IsDraft: false,
  160. IsPrerelease: false,
  161. IsTag: true,
  162. }
  163. if _, err = createTag(ctx, gitRepo, rel, msg); err != nil {
  164. return err
  165. }
  166. return db.Insert(ctx, rel)
  167. }
  168. // UpdateRelease updates information, attachments of a release and will create tag if it's not a draft and tag not exist.
  169. // addAttachmentUUIDs accept a slice of new created attachments' uuids which will be reassigned release_id as the created release
  170. // delAttachmentUUIDs accept a slice of attachments' uuids which will be deleted from the release
  171. // editAttachments accept a map of attachment uuid to new attachment name which will be updated with attachments.
  172. func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, rel *repo_model.Release,
  173. addAttachmentUUIDs, delAttachmentUUIDs []string, editAttachments map[string]string,
  174. ) error {
  175. if rel.ID == 0 {
  176. return errors.New("UpdateRelease only accepts an exist release")
  177. }
  178. isCreated, err := createTag(gitRepo.Ctx, gitRepo, rel, "")
  179. if err != nil {
  180. return err
  181. }
  182. rel.LowerTagName = strings.ToLower(rel.TagName)
  183. ctx, committer, err := db.TxContext(ctx)
  184. if err != nil {
  185. return err
  186. }
  187. defer committer.Close()
  188. if err = repo_model.UpdateRelease(ctx, rel); err != nil {
  189. return err
  190. }
  191. if err = repo_model.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil {
  192. return fmt.Errorf("AddReleaseAttachments: %w", err)
  193. }
  194. deletedUUIDs := make(container.Set[string])
  195. if len(delAttachmentUUIDs) > 0 {
  196. // Check attachments
  197. attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, delAttachmentUUIDs)
  198. if err != nil {
  199. return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", delAttachmentUUIDs, err)
  200. }
  201. for _, attach := range attachments {
  202. if attach.ReleaseID != rel.ID {
  203. return util.SilentWrap{
  204. Message: "delete attachment of release permission denied",
  205. Err: util.ErrPermissionDenied,
  206. }
  207. }
  208. deletedUUIDs.Add(attach.UUID)
  209. }
  210. if _, err := repo_model.DeleteAttachments(ctx, attachments, true); err != nil {
  211. return fmt.Errorf("DeleteAttachments [uuids: %v]: %w", delAttachmentUUIDs, err)
  212. }
  213. }
  214. if len(editAttachments) > 0 {
  215. updateAttachmentsList := make([]string, 0, len(editAttachments))
  216. for k := range editAttachments {
  217. updateAttachmentsList = append(updateAttachmentsList, k)
  218. }
  219. // Check attachments
  220. attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, updateAttachmentsList)
  221. if err != nil {
  222. return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", updateAttachmentsList, err)
  223. }
  224. for _, attach := range attachments {
  225. if attach.ReleaseID != rel.ID {
  226. return util.SilentWrap{
  227. Message: "update attachment of release permission denied",
  228. Err: util.ErrPermissionDenied,
  229. }
  230. }
  231. }
  232. for uuid, newName := range editAttachments {
  233. if !deletedUUIDs.Contains(uuid) {
  234. if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{
  235. UUID: uuid,
  236. Name: newName,
  237. }, "name"); err != nil {
  238. return err
  239. }
  240. }
  241. }
  242. }
  243. if err := committer.Commit(); err != nil {
  244. return err
  245. }
  246. for _, uuid := range delAttachmentUUIDs {
  247. if err := storage.Attachments.Delete(repo_model.AttachmentRelativePath(uuid)); err != nil {
  248. // Even delete files failed, but the attachments has been removed from database, so we
  249. // should not return error but only record the error on logs.
  250. // users have to delete this attachments manually or we should have a
  251. // synchronize between database attachment table and attachment storage
  252. log.Error("delete attachment[uuid: %s] failed: %v", uuid, err)
  253. }
  254. }
  255. if !isCreated {
  256. notify_service.UpdateRelease(gitRepo.Ctx, doer, rel)
  257. return nil
  258. }
  259. if !rel.IsDraft {
  260. notify_service.NewRelease(gitRepo.Ctx, rel)
  261. }
  262. return nil
  263. }
  264. // DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
  265. func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, delTag bool) error {
  266. rel, err := repo_model.GetReleaseByID(ctx, id)
  267. if err != nil {
  268. return fmt.Errorf("GetReleaseByID: %w", err)
  269. }
  270. repo, err := repo_model.GetRepositoryByID(ctx, rel.RepoID)
  271. if err != nil {
  272. return fmt.Errorf("GetRepositoryByID: %w", err)
  273. }
  274. if delTag {
  275. protectedTags, err := git_model.GetProtectedTags(ctx, rel.RepoID)
  276. if err != nil {
  277. return fmt.Errorf("GetProtectedTags: %w", err)
  278. }
  279. isAllowed, err := git_model.IsUserAllowedToControlTag(ctx, protectedTags, rel.TagName, rel.PublisherID)
  280. if err != nil {
  281. return err
  282. }
  283. if !isAllowed {
  284. return models.ErrProtectedTagName{
  285. TagName: rel.TagName,
  286. }
  287. }
  288. if stdout, _, err := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName).
  289. SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)).
  290. RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") {
  291. log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err)
  292. return fmt.Errorf("git tag -d: %w", err)
  293. }
  294. refName := git.RefNameFromTag(rel.TagName)
  295. notify_service.PushCommits(
  296. ctx, doer, repo,
  297. &repository.PushUpdateOptions{
  298. RefFullName: refName,
  299. OldCommitID: rel.Sha1,
  300. NewCommitID: git.EmptySHA,
  301. }, repository.NewPushCommits())
  302. notify_service.DeleteRef(ctx, doer, repo, refName)
  303. if err := repo_model.DeleteReleaseByID(ctx, id); err != nil {
  304. return fmt.Errorf("DeleteReleaseByID: %w", err)
  305. }
  306. } else {
  307. rel.IsTag = true
  308. if err = repo_model.UpdateRelease(ctx, rel); err != nil {
  309. return fmt.Errorf("Update: %w", err)
  310. }
  311. }
  312. rel.Repo = repo
  313. if err = rel.LoadAttributes(ctx); err != nil {
  314. return fmt.Errorf("LoadAttributes: %w", err)
  315. }
  316. if err := repo_model.DeleteAttachmentsByRelease(ctx, rel.ID); err != nil {
  317. return fmt.Errorf("DeleteAttachments: %w", err)
  318. }
  319. for i := range rel.Attachments {
  320. attachment := rel.Attachments[i]
  321. if err := storage.Attachments.Delete(attachment.RelativePath()); err != nil {
  322. log.Error("Delete attachment %s of release %s failed: %v", attachment.UUID, rel.ID, err)
  323. }
  324. }
  325. notify_service.DeleteRelease(ctx, doer, rel)
  326. return nil
  327. }