code.gitea.io/gitea@v1.21.7/routers/api/v1/repo/release.go (about) 1 // Copyright 2016 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package repo 5 6 import ( 7 "net/http" 8 9 "code.gitea.io/gitea/models" 10 "code.gitea.io/gitea/models/perm" 11 repo_model "code.gitea.io/gitea/models/repo" 12 "code.gitea.io/gitea/models/unit" 13 "code.gitea.io/gitea/modules/context" 14 api "code.gitea.io/gitea/modules/structs" 15 "code.gitea.io/gitea/modules/web" 16 "code.gitea.io/gitea/routers/api/v1/utils" 17 "code.gitea.io/gitea/services/convert" 18 release_service "code.gitea.io/gitea/services/release" 19 ) 20 21 // GetRelease get a single release of a repository 22 func GetRelease(ctx *context.APIContext) { 23 // swagger:operation GET /repos/{owner}/{repo}/releases/{id} repository repoGetRelease 24 // --- 25 // summary: Get a release 26 // produces: 27 // - application/json 28 // parameters: 29 // - name: owner 30 // in: path 31 // description: owner of the repo 32 // type: string 33 // required: true 34 // - name: repo 35 // in: path 36 // description: name of the repo 37 // type: string 38 // required: true 39 // - name: id 40 // in: path 41 // description: id of the release to get 42 // type: integer 43 // format: int64 44 // required: true 45 // responses: 46 // "200": 47 // "$ref": "#/responses/Release" 48 // "404": 49 // "$ref": "#/responses/notFound" 50 51 id := ctx.ParamsInt64(":id") 52 release, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id) 53 if err != nil && !repo_model.IsErrReleaseNotExist(err) { 54 ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err) 55 return 56 } 57 if err != nil && repo_model.IsErrReleaseNotExist(err) || release.IsTag { 58 ctx.NotFound() 59 return 60 } 61 62 if err := release.LoadAttributes(ctx); err != nil { 63 ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) 64 return 65 } 66 ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)) 67 } 68 69 // GetLatestRelease gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at 70 func GetLatestRelease(ctx *context.APIContext) { 71 // swagger:operation GET /repos/{owner}/{repo}/releases/latest repository repoGetLatestRelease 72 // --- 73 // summary: Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at 74 // produces: 75 // - application/json 76 // parameters: 77 // - name: owner 78 // in: path 79 // description: owner of the repo 80 // type: string 81 // required: true 82 // - name: repo 83 // in: path 84 // description: name of the repo 85 // type: string 86 // required: true 87 // responses: 88 // "200": 89 // "$ref": "#/responses/Release" 90 // "404": 91 // "$ref": "#/responses/notFound" 92 release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID) 93 if err != nil && !repo_model.IsErrReleaseNotExist(err) { 94 ctx.Error(http.StatusInternalServerError, "GetLatestRelease", err) 95 return 96 } 97 if err != nil && repo_model.IsErrReleaseNotExist(err) || 98 release.IsTag || release.RepoID != ctx.Repo.Repository.ID { 99 ctx.NotFound() 100 return 101 } 102 103 if err := release.LoadAttributes(ctx); err != nil { 104 ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) 105 return 106 } 107 ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)) 108 } 109 110 // ListReleases list a repository's releases 111 func ListReleases(ctx *context.APIContext) { 112 // swagger:operation GET /repos/{owner}/{repo}/releases repository repoListReleases 113 // --- 114 // summary: List a repo's releases 115 // produces: 116 // - application/json 117 // parameters: 118 // - name: owner 119 // in: path 120 // description: owner of the repo 121 // type: string 122 // required: true 123 // - name: repo 124 // in: path 125 // description: name of the repo 126 // type: string 127 // required: true 128 // - name: draft 129 // in: query 130 // description: filter (exclude / include) drafts, if you dont have repo write access none will show 131 // type: boolean 132 // - name: pre-release 133 // in: query 134 // description: filter (exclude / include) pre-releases 135 // type: boolean 136 // - name: per_page 137 // in: query 138 // description: page size of results, deprecated - use limit 139 // type: integer 140 // deprecated: true 141 // - name: page 142 // in: query 143 // description: page number of results to return (1-based) 144 // type: integer 145 // - name: limit 146 // in: query 147 // description: page size of results 148 // type: integer 149 // responses: 150 // "200": 151 // "$ref": "#/responses/ReleaseList" 152 // "404": 153 // "$ref": "#/responses/notFound" 154 listOptions := utils.GetListOptions(ctx) 155 if listOptions.PageSize == 0 && ctx.FormInt("per_page") != 0 { 156 listOptions.PageSize = ctx.FormInt("per_page") 157 } 158 159 opts := repo_model.FindReleasesOptions{ 160 ListOptions: listOptions, 161 IncludeDrafts: ctx.Repo.AccessMode >= perm.AccessModeWrite || ctx.Repo.UnitAccessMode(unit.TypeReleases) >= perm.AccessModeWrite, 162 IncludeTags: false, 163 IsDraft: ctx.FormOptionalBool("draft"), 164 IsPreRelease: ctx.FormOptionalBool("pre-release"), 165 } 166 167 releases, err := repo_model.GetReleasesByRepoID(ctx, ctx.Repo.Repository.ID, opts) 168 if err != nil { 169 ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err) 170 return 171 } 172 rels := make([]*api.Release, len(releases)) 173 for i, release := range releases { 174 if err := release.LoadAttributes(ctx); err != nil { 175 ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) 176 return 177 } 178 rels[i] = convert.ToAPIRelease(ctx, ctx.Repo.Repository, release) 179 } 180 181 filteredCount, err := repo_model.CountReleasesByRepoID(ctx, ctx.Repo.Repository.ID, opts) 182 if err != nil { 183 ctx.InternalServerError(err) 184 return 185 } 186 187 ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) 188 ctx.SetTotalCountHeader(filteredCount) 189 ctx.JSON(http.StatusOK, rels) 190 } 191 192 // CreateRelease create a release 193 func CreateRelease(ctx *context.APIContext) { 194 // swagger:operation POST /repos/{owner}/{repo}/releases repository repoCreateRelease 195 // --- 196 // summary: Create a release 197 // consumes: 198 // - application/json 199 // produces: 200 // - application/json 201 // parameters: 202 // - name: owner 203 // in: path 204 // description: owner of the repo 205 // type: string 206 // required: true 207 // - name: repo 208 // in: path 209 // description: name of the repo 210 // type: string 211 // required: true 212 // - name: body 213 // in: body 214 // schema: 215 // "$ref": "#/definitions/CreateReleaseOption" 216 // responses: 217 // "201": 218 // "$ref": "#/responses/Release" 219 // "404": 220 // "$ref": "#/responses/notFound" 221 // "409": 222 // "$ref": "#/responses/error" 223 form := web.GetForm(ctx).(*api.CreateReleaseOption) 224 rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, form.TagName) 225 if err != nil { 226 if !repo_model.IsErrReleaseNotExist(err) { 227 ctx.Error(http.StatusInternalServerError, "GetRelease", err) 228 return 229 } 230 // If target is not provided use default branch 231 if len(form.Target) == 0 { 232 form.Target = ctx.Repo.Repository.DefaultBranch 233 } 234 rel = &repo_model.Release{ 235 RepoID: ctx.Repo.Repository.ID, 236 PublisherID: ctx.Doer.ID, 237 Publisher: ctx.Doer, 238 TagName: form.TagName, 239 Target: form.Target, 240 Title: form.Title, 241 Note: form.Note, 242 IsDraft: form.IsDraft, 243 IsPrerelease: form.IsPrerelease, 244 IsTag: false, 245 Repo: ctx.Repo.Repository, 246 } 247 if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil { 248 if repo_model.IsErrReleaseAlreadyExist(err) { 249 ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err) 250 } else { 251 ctx.Error(http.StatusInternalServerError, "CreateRelease", err) 252 } 253 return 254 } 255 } else { 256 if !rel.IsTag { 257 ctx.Error(http.StatusConflict, "GetRelease", "Release is has no Tag") 258 return 259 } 260 261 rel.Title = form.Title 262 rel.Note = form.Note 263 rel.IsDraft = form.IsDraft 264 rel.IsPrerelease = form.IsPrerelease 265 rel.PublisherID = ctx.Doer.ID 266 rel.IsTag = false 267 rel.Repo = ctx.Repo.Repository 268 rel.Publisher = ctx.Doer 269 rel.Target = form.Target 270 271 if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil { 272 ctx.Error(http.StatusInternalServerError, "UpdateRelease", err) 273 return 274 } 275 } 276 ctx.JSON(http.StatusCreated, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel)) 277 } 278 279 // EditRelease edit a release 280 func EditRelease(ctx *context.APIContext) { 281 // swagger:operation PATCH /repos/{owner}/{repo}/releases/{id} repository repoEditRelease 282 // --- 283 // summary: Update a release 284 // consumes: 285 // - application/json 286 // produces: 287 // - application/json 288 // parameters: 289 // - name: owner 290 // in: path 291 // description: owner of the repo 292 // type: string 293 // required: true 294 // - name: repo 295 // in: path 296 // description: name of the repo 297 // type: string 298 // required: true 299 // - name: id 300 // in: path 301 // description: id of the release to edit 302 // type: integer 303 // format: int64 304 // required: true 305 // - name: body 306 // in: body 307 // schema: 308 // "$ref": "#/definitions/EditReleaseOption" 309 // responses: 310 // "200": 311 // "$ref": "#/responses/Release" 312 // "404": 313 // "$ref": "#/responses/notFound" 314 315 form := web.GetForm(ctx).(*api.EditReleaseOption) 316 id := ctx.ParamsInt64(":id") 317 rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id) 318 if err != nil && !repo_model.IsErrReleaseNotExist(err) { 319 ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err) 320 return 321 } 322 if err != nil && repo_model.IsErrReleaseNotExist(err) || rel.IsTag { 323 ctx.NotFound() 324 return 325 } 326 327 if len(form.TagName) > 0 { 328 rel.TagName = form.TagName 329 } 330 if len(form.Target) > 0 { 331 rel.Target = form.Target 332 } 333 if len(form.Title) > 0 { 334 rel.Title = form.Title 335 } 336 if len(form.Note) > 0 { 337 rel.Note = form.Note 338 } 339 if form.IsDraft != nil { 340 rel.IsDraft = *form.IsDraft 341 } 342 if form.IsPrerelease != nil { 343 rel.IsPrerelease = *form.IsPrerelease 344 } 345 if err := release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil { 346 ctx.Error(http.StatusInternalServerError, "UpdateRelease", err) 347 return 348 } 349 350 // reload data from database 351 rel, err = repo_model.GetReleaseByID(ctx, id) 352 if err != nil { 353 ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err) 354 return 355 } 356 if err := rel.LoadAttributes(ctx); err != nil { 357 ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) 358 return 359 } 360 ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel)) 361 } 362 363 // DeleteRelease delete a release from a repository 364 func DeleteRelease(ctx *context.APIContext) { 365 // swagger:operation DELETE /repos/{owner}/{repo}/releases/{id} repository repoDeleteRelease 366 // --- 367 // summary: Delete a release 368 // parameters: 369 // - name: owner 370 // in: path 371 // description: owner of the repo 372 // type: string 373 // required: true 374 // - name: repo 375 // in: path 376 // description: name of the repo 377 // type: string 378 // required: true 379 // - name: id 380 // in: path 381 // description: id of the release to delete 382 // type: integer 383 // format: int64 384 // required: true 385 // responses: 386 // "204": 387 // "$ref": "#/responses/empty" 388 // "404": 389 // "$ref": "#/responses/notFound" 390 // "405": 391 // "$ref": "#/responses/empty" 392 393 id := ctx.ParamsInt64(":id") 394 rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id) 395 if err != nil && !repo_model.IsErrReleaseNotExist(err) { 396 ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err) 397 return 398 } 399 if err != nil && repo_model.IsErrReleaseNotExist(err) || rel.IsTag { 400 ctx.NotFound() 401 return 402 } 403 if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, false); err != nil { 404 if models.IsErrProtectedTagName(err) { 405 ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag") 406 return 407 } 408 ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err) 409 return 410 } 411 ctx.Status(http.StatusNoContent) 412 }