mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Add API endpoint to get latest release (#21267)
This PR adds a new API endpoint to get the latest stable release of a repo, similar to [GitHub API](https://docs.github.com/en/rest/releases/releases#get-the-latest-release).
This commit is contained in:
		@@ -1011,6 +1011,7 @@ func Routes(ctx gocontext.Context) *web.Route {
 | 
				
			|||||||
				m.Group("/releases", func() {
 | 
									m.Group("/releases", func() {
 | 
				
			||||||
					m.Combo("").Get(repo.ListReleases).
 | 
										m.Combo("").Get(repo.ListReleases).
 | 
				
			||||||
						Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
 | 
											Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
 | 
				
			||||||
 | 
										m.Combo("/latest").Get(repo.GetLatestRelease)
 | 
				
			||||||
					m.Group("/{id}", func() {
 | 
										m.Group("/{id}", func() {
 | 
				
			||||||
						m.Combo("").Get(repo.GetRelease).
 | 
											m.Combo("").Get(repo.GetRelease).
 | 
				
			||||||
							Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease).
 | 
												Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease).
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,6 +67,47 @@ func GetRelease(ctx *context.APIContext) {
 | 
				
			|||||||
	ctx.JSON(http.StatusOK, convert.ToRelease(release))
 | 
						ctx.JSON(http.StatusOK, convert.ToRelease(release))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetLatestRelease gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at
 | 
				
			||||||
 | 
					func GetLatestRelease(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation GET /repos/{owner}/{repo}/releases/latest repository repoGetLatestRelease
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/Release"
 | 
				
			||||||
 | 
						//   "404":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
						release, err := repo_model.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID)
 | 
				
			||||||
 | 
						if err != nil && !repo_model.IsErrReleaseNotExist(err) {
 | 
				
			||||||
 | 
							ctx.Error(http.StatusInternalServerError, "GetLatestRelease", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil && repo_model.IsErrReleaseNotExist(err) ||
 | 
				
			||||||
 | 
							release.IsTag || release.RepoID != ctx.Repo.Repository.ID {
 | 
				
			||||||
 | 
							ctx.NotFound()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := release.LoadAttributes(ctx); err != nil {
 | 
				
			||||||
 | 
							ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSON(http.StatusOK, convert.ToRelease(release))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListReleases list a repository's releases
 | 
					// ListReleases list a repository's releases
 | 
				
			||||||
func ListReleases(ctx *context.APIContext) {
 | 
					func ListReleases(ctx *context.APIContext) {
 | 
				
			||||||
	// swagger:operation GET /repos/{owner}/{repo}/releases repository repoListReleases
 | 
						// swagger:operation GET /repos/{owner}/{repo}/releases repository repoListReleases
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9776,6 +9776,42 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/repos/{owner}/{repo}/releases/latest": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "repository"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at",
 | 
				
			||||||
 | 
					        "operationId": "repoGetLatestRelease",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/Release"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/repos/{owner}/{repo}/releases/tags/{tag}": {
 | 
					    "/repos/{owner}/{repo}/releases/tags/{tag}": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "produces": [
 | 
					        "produces": [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -176,6 +176,24 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) {
 | 
				
			|||||||
	createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
 | 
						createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPIGetLatestRelease(t *testing.T) {
 | 
				
			||||||
 | 
						defer tests.PrepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
 | 
				
			||||||
 | 
						owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/latest",
 | 
				
			||||||
 | 
							owner.Name, repo.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req := NewRequestf(t, "GET", urlStr)
 | 
				
			||||||
 | 
						resp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var release *api.Release
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &release)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Equal(t, "testing-release", release.Title)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAPIGetReleaseByTag(t *testing.T) {
 | 
					func TestAPIGetReleaseByTag(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user