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  }