Add single release page and latest redirect (#11102)
* Add single release and latest release routes Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update API and move latest search to models Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix swagger Signed-off-by: jolheiser <john.olheiser@gmail.com> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
parent
1b86f174ce
commit
4f597b1866
6 changed files with 96 additions and 2 deletions
|
@ -80,6 +80,11 @@ func (r *Release) TarURL() string {
|
||||||
return fmt.Sprintf("%s/archive/%s.tar.gz", r.Repo.HTMLURL(), r.TagName)
|
return fmt.Sprintf("%s/archive/%s.tar.gz", r.Repo.HTMLURL(), r.TagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTMLURL the url for a release on the web UI. release must have attributes loaded
|
||||||
|
func (r *Release) HTMLURL() string {
|
||||||
|
return fmt.Sprintf("%s/releases/tag/%s", r.Repo.HTMLURL(), r.TagName)
|
||||||
|
}
|
||||||
|
|
||||||
// APIFormat convert a Release to api.Release
|
// APIFormat convert a Release to api.Release
|
||||||
func (r *Release) APIFormat() *api.Release {
|
func (r *Release) APIFormat() *api.Release {
|
||||||
assets := make([]*api.Attachment, 0)
|
assets := make([]*api.Attachment, 0)
|
||||||
|
@ -93,6 +98,7 @@ func (r *Release) APIFormat() *api.Release {
|
||||||
Title: r.Title,
|
Title: r.Title,
|
||||||
Note: r.Note,
|
Note: r.Note,
|
||||||
URL: r.APIURL(),
|
URL: r.APIURL(),
|
||||||
|
HTMLURL: r.HTMLURL(),
|
||||||
TarURL: r.TarURL(),
|
TarURL: r.TarURL(),
|
||||||
ZipURL: r.ZipURL(),
|
ZipURL: r.ZipURL(),
|
||||||
IsDraft: r.IsDraft,
|
IsDraft: r.IsDraft,
|
||||||
|
@ -217,6 +223,28 @@ func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions) ([]*Release, er
|
||||||
return rels, sess.Find(&rels)
|
return rels, sess.Find(&rels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLatestReleaseByRepoID returns the latest release for a repository
|
||||||
|
func GetLatestReleaseByRepoID(repoID int64) (*Release, error) {
|
||||||
|
cond := builder.NewCond().
|
||||||
|
And(builder.Eq{"repo_id": repoID}).
|
||||||
|
And(builder.Eq{"is_draft": false}).
|
||||||
|
And(builder.Eq{"is_prerelease": false}).
|
||||||
|
And(builder.Eq{"is_tag": false})
|
||||||
|
|
||||||
|
rel := new(Release)
|
||||||
|
has, err := x.
|
||||||
|
Desc("created_unix", "id").
|
||||||
|
Where(cond).
|
||||||
|
Get(rel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, ErrReleaseNotExist{0, "latest"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rel, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
|
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
|
||||||
func GetReleasesByRepoIDAndNames(ctx DBContext, repoID int64, tagNames []string) (rels []*Release, err error) {
|
func GetReleasesByRepoIDAndNames(ctx DBContext, repoID int64, tagNames []string) (rels []*Release, err error) {
|
||||||
err = ctx.e.
|
err = ctx.e.
|
||||||
|
|
|
@ -16,6 +16,7 @@ type Release struct {
|
||||||
Title string `json:"name"`
|
Title string `json:"name"`
|
||||||
Note string `json:"body"`
|
Note string `json:"body"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
TarURL string `json:"tarball_url"`
|
TarURL string `json:"tarball_url"`
|
||||||
ZipURL string `json:"zipball_url"`
|
ZipURL string `json:"zipball_url"`
|
||||||
IsDraft bool `json:"draft"`
|
IsDraft bool `json:"draft"`
|
||||||
|
|
|
@ -131,6 +131,65 @@ func Releases(ctx *context.Context) {
|
||||||
ctx.HTML(200, tplReleases)
|
ctx.HTML(200, tplReleases)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SingleRelease renders a single release's page
|
||||||
|
func SingleRelease(ctx *context.Context) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
|
||||||
|
ctx.Data["PageIsReleaseList"] = true
|
||||||
|
|
||||||
|
writeAccess := ctx.Repo.CanWrite(models.UnitTypeReleases)
|
||||||
|
ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
|
||||||
|
|
||||||
|
release, err := models.GetRelease(ctx.Repo.Repository.ID, ctx.Params("tag"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetReleasesByRepoID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = models.GetReleaseAttachments(release)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetReleaseAttachments", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
release.Publisher, err = models.GetUserByID(release.PublisherID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrUserNotExist(err) {
|
||||||
|
release.Publisher = models.NewGhostUser()
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("GetUserByID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := calReleaseNumCommitsBehind(ctx.Repo, release, make(map[string]int64)); err != nil {
|
||||||
|
ctx.ServerError("calReleaseNumCommitsBehind", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
release.Note = markdown.RenderString(release.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())
|
||||||
|
|
||||||
|
ctx.Data["Releases"] = []*models.Release{release}
|
||||||
|
ctx.HTML(200, tplReleases)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LatestRelease redirects to the latest release
|
||||||
|
func LatestRelease(ctx *context.Context) {
|
||||||
|
release, err := models.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrReleaseNotExist(err) {
|
||||||
|
ctx.NotFound("LatestRelease", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.ServerError("GetLatestReleaseByRepoID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := release.LoadAttributes(); err != nil {
|
||||||
|
ctx.ServerError("LoadAttributes", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(release.HTMLURL())
|
||||||
|
}
|
||||||
|
|
||||||
// NewRelease render creating release page
|
// NewRelease render creating release page
|
||||||
func NewRelease(ctx *context.Context) {
|
func NewRelease(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
|
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
|
||||||
|
|
|
@ -805,7 +805,9 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
// Releases
|
// Releases
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/:username/:reponame", func() {
|
||||||
m.Group("/releases", func() {
|
m.Group("/releases", func() {
|
||||||
m.Get("/", repo.MustBeNotEmpty, repo.Releases)
|
m.Get("/", repo.Releases)
|
||||||
|
m.Get("/tag/:tag", repo.SingleRelease)
|
||||||
|
m.Get("/latest", repo.LatestRelease)
|
||||||
}, repo.MustBeNotEmpty, context.RepoRef())
|
}, repo.MustBeNotEmpty, context.RepoRef())
|
||||||
m.Group("/releases", func() {
|
m.Group("/releases", func() {
|
||||||
m.Get("/new", repo.NewRelease)
|
m.Get("/new", repo.NewRelease)
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<h3>
|
<h3>
|
||||||
<a href="{{$.RepoLink}}/src/tag/{{.TagName | EscapePound}}">{{.Title}}</a>
|
<a href="{{$.RepoLink}}/releases/tag/{{.TagName | EscapePound}}">{{.Title}}</a>
|
||||||
{{if $.CanCreateRelease}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName | EscapePound}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}}
|
{{if $.CanCreateRelease}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName | EscapePound}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text grey">
|
<p class="text grey">
|
||||||
|
|
|
@ -13107,6 +13107,10 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"x-go-name": "IsDraft"
|
"x-go-name": "IsDraft"
|
||||||
},
|
},
|
||||||
|
"html_url": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "HTMLURL"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
|
|
Loading…
Reference in a new issue