summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--models/release.go28
-rw-r--r--modules/structs/release.go1
-rw-r--r--routers/repo/release.go59
-rw-r--r--routers/routes/routes.go4
-rw-r--r--templates/repo/release/list.tmpl2
-rw-r--r--templates/swagger/v1_json.tmpl4
6 files changed, 96 insertions, 2 deletions
diff --git a/models/release.go b/models/release.go
index 0f670f374f..0c76d17f4b 100644
--- a/models/release.go
+++ b/models/release.go
@@ -80,6 +80,11 @@ func (r *Release) TarURL() string {
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
func (r *Release) APIFormat() *api.Release {
assets := make([]*api.Attachment, 0)
@@ -93,6 +98,7 @@ func (r *Release) APIFormat() *api.Release {
Title: r.Title,
Note: r.Note,
URL: r.APIURL(),
+ HTMLURL: r.HTMLURL(),
TarURL: r.TarURL(),
ZipURL: r.ZipURL(),
IsDraft: r.IsDraft,
@@ -217,6 +223,28 @@ func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions) ([]*Release, er
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.
func GetReleasesByRepoIDAndNames(ctx DBContext, repoID int64, tagNames []string) (rels []*Release, err error) {
err = ctx.e.
diff --git a/modules/structs/release.go b/modules/structs/release.go
index b7575af39a..38ce60bbe2 100644
--- a/modules/structs/release.go
+++ b/modules/structs/release.go
@@ -16,6 +16,7 @@ type Release struct {
Title string `json:"name"`
Note string `json:"body"`
URL string `json:"url"`
+ HTMLURL string `json:"html_url"`
TarURL string `json:"tarball_url"`
ZipURL string `json:"zipball_url"`
IsDraft bool `json:"draft"`
diff --git a/routers/repo/release.go b/routers/repo/release.go
index 5454195181..1eac3dce97 100644
--- a/routers/repo/release.go
+++ b/routers/repo/release.go
@@ -131,6 +131,65 @@ func Releases(ctx *context.Context) {
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
func NewRelease(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index e2514054bf..a7828885bf 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -805,7 +805,9 @@ func RegisterRoutes(m *macaron.Macaron) {
// Releases
m.Group("/:username/:reponame", 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())
m.Group("/releases", func() {
m.Get("/new", repo.NewRelease)
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index 22aea09f7b..d0b160a1c0 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -49,7 +49,7 @@
</div>
{{else}}
<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}}
</h3>
<p class="text grey">
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index e9368a7d2a..e87af4f5c9 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -13107,6 +13107,10 @@
"type": "boolean",
"x-go-name": "IsDraft"
},
+ "html_url": {
+ "type": "string",
+ "x-go-name": "HTMLURL"
+ },
"id": {
"type": "integer",
"format": "int64",