Skip to content

Commit 4d072a4

Browse files
authored
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).
1 parent e8ac6a9 commit 4d072a4

File tree

4 files changed

+96
-0
lines changed

4 files changed

+96
-0
lines changed

Diff for: routers/api/v1/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,7 @@ func Routes(ctx gocontext.Context) *web.Route {
10111011
m.Group("/releases", func() {
10121012
m.Combo("").Get(repo.ListReleases).
10131013
Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
1014+
m.Combo("/latest").Get(repo.GetLatestRelease)
10141015
m.Group("/{id}", func() {
10151016
m.Combo("").Get(repo.GetRelease).
10161017
Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease).

Diff for: routers/api/v1/repo/release.go

+41
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,47 @@ func GetRelease(ctx *context.APIContext) {
6767
ctx.JSON(http.StatusOK, convert.ToRelease(release))
6868
}
6969

70+
// GetLatestRelease gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at
71+
func GetLatestRelease(ctx *context.APIContext) {
72+
// swagger:operation GET /repos/{owner}/{repo}/releases/latest repository repoGetLatestRelease
73+
// ---
74+
// summary: Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at
75+
// produces:
76+
// - application/json
77+
// parameters:
78+
// - name: owner
79+
// in: path
80+
// description: owner of the repo
81+
// type: string
82+
// required: true
83+
// - name: repo
84+
// in: path
85+
// description: name of the repo
86+
// type: string
87+
// required: true
88+
// responses:
89+
// "200":
90+
// "$ref": "#/responses/Release"
91+
// "404":
92+
// "$ref": "#/responses/notFound"
93+
release, err := repo_model.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID)
94+
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
95+
ctx.Error(http.StatusInternalServerError, "GetLatestRelease", err)
96+
return
97+
}
98+
if err != nil && repo_model.IsErrReleaseNotExist(err) ||
99+
release.IsTag || release.RepoID != ctx.Repo.Repository.ID {
100+
ctx.NotFound()
101+
return
102+
}
103+
104+
if err := release.LoadAttributes(ctx); err != nil {
105+
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
106+
return
107+
}
108+
ctx.JSON(http.StatusOK, convert.ToRelease(release))
109+
}
110+
70111
// ListReleases list a repository's releases
71112
func ListReleases(ctx *context.APIContext) {
72113
// swagger:operation GET /repos/{owner}/{repo}/releases repository repoListReleases

Diff for: templates/swagger/v1_json.tmpl

+36
Original file line numberDiff line numberDiff line change
@@ -9776,6 +9776,42 @@
97769776
}
97779777
}
97789778
},
9779+
"/repos/{owner}/{repo}/releases/latest": {
9780+
"get": {
9781+
"produces": [
9782+
"application/json"
9783+
],
9784+
"tags": [
9785+
"repository"
9786+
],
9787+
"summary": "Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at",
9788+
"operationId": "repoGetLatestRelease",
9789+
"parameters": [
9790+
{
9791+
"type": "string",
9792+
"description": "owner of the repo",
9793+
"name": "owner",
9794+
"in": "path",
9795+
"required": true
9796+
},
9797+
{
9798+
"type": "string",
9799+
"description": "name of the repo",
9800+
"name": "repo",
9801+
"in": "path",
9802+
"required": true
9803+
}
9804+
],
9805+
"responses": {
9806+
"200": {
9807+
"$ref": "#/responses/Release"
9808+
},
9809+
"404": {
9810+
"$ref": "#/responses/notFound"
9811+
}
9812+
}
9813+
}
9814+
},
97799815
"/repos/{owner}/{repo}/releases/tags/{tag}": {
97809816
"get": {
97819817
"produces": [

Diff for: tests/integration/api_releases_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,24 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) {
176176
createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
177177
}
178178

179+
func TestAPIGetLatestRelease(t *testing.T) {
180+
defer tests.PrepareTestEnv(t)()
181+
182+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
183+
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
184+
185+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/latest",
186+
owner.Name, repo.Name)
187+
188+
req := NewRequestf(t, "GET", urlStr)
189+
resp := MakeRequest(t, req, http.StatusOK)
190+
191+
var release *api.Release
192+
DecodeJSON(t, resp, &release)
193+
194+
assert.Equal(t, "testing-release", release.Title)
195+
}
196+
179197
func TestAPIGetReleaseByTag(t *testing.T) {
180198
defer tests.PrepareTestEnv(t)()
181199

0 commit comments

Comments
 (0)