* 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>tags/v1.21.12.1
| @@ -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", | ||||