@@ -15,12 +15,16 @@ import ( | |||
"code.gitea.io/git" | |||
"code.gitea.io/gitea/modules/process" | |||
"code.gitea.io/gitea/modules/setting" | |||
api "code.gitea.io/sdk/gitea" | |||
) | |||
// Release represents a release of repository. | |||
type Release struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
RepoID int64 | |||
Repo *Repository `xorm:"-"` | |||
PublisherID int64 | |||
Publisher *User `xorm:"-"` | |||
TagName string | |||
@@ -53,6 +57,62 @@ func (r *Release) AfterSet(colName string, _ xorm.Cell) { | |||
} | |||
} | |||
func (r *Release) loadAttributes(e Engine) error { | |||
var err error | |||
if r.Repo == nil { | |||
r.Repo, err = GetRepositoryByID(r.RepoID) | |||
if err != nil { | |||
return err | |||
} | |||
} | |||
if r.Publisher == nil { | |||
r.Publisher, err = GetUserByID(r.PublisherID) | |||
if err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} | |||
// LoadAttributes load repo and publisher attributes for a realease | |||
func (r *Release) LoadAttributes() error { | |||
return r.loadAttributes(x) | |||
} | |||
// APIURL the api url for a release. release must have attributes loaded | |||
func (r *Release) APIURL() string { | |||
return fmt.Sprintf("%sapi/v1/%s/releases/%d", | |||
setting.AppURL, r.Repo.FullName(), r.ID) | |||
} | |||
// ZipURL the zip url for a release. release must have attributes loaded | |||
func (r *Release) ZipURL() string { | |||
return fmt.Sprintf("%s/archive/%s.zip", r.Repo.HTMLURL(), r.TagName) | |||
} | |||
// TarURL the tar.gz url for a release. release must have attributes loaded | |||
func (r *Release) TarURL() string { | |||
return fmt.Sprintf("%s/archive/%s.tar.gz", r.Repo.HTMLURL(), r.TagName) | |||
} | |||
// APIFormat convert a Release to api.Release | |||
func (r *Release) APIFormat() *api.Release { | |||
return &api.Release{ | |||
ID: r.ID, | |||
TagName: r.TagName, | |||
Target: r.Target, | |||
Note: r.Note, | |||
URL: r.APIURL(), | |||
TarURL: r.TarURL(), | |||
ZipURL: r.ZipURL(), | |||
IsDraft: r.IsDraft, | |||
IsPrerelease: r.IsPrerelease, | |||
CreatedAt: r.Created, | |||
PublishedAt: r.Created, | |||
Publisher: r.Publisher.APIFormat(), | |||
} | |||
} | |||
// IsReleaseExist returns true if release with given tag name already exists. | |||
func IsReleaseExist(repoID int64, tagName string) (bool, error) { | |||
if len(tagName) == 0 { |
@@ -331,6 +331,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Put("", user.Watch) | |||
m.Delete("", user.Unwatch) | |||
}) | |||
m.Group("/releases", func() { | |||
m.Combo("").Get(repo.ListReleases). | |||
Post(bind(api.CreateReleaseOption{}), repo.CreateRelease) | |||
m.Combo("/:id").Get(repo.GetRelease). | |||
Patch(bind(api.EditReleaseOption{}), repo.EditRelease). | |||
Delete(repo.DeleteRelease) | |||
}) | |||
m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig) | |||
m.Group("/pulls", func() { | |||
m.Combo("").Get(bind(api.ListPullRequestsOptions{}), repo.ListPullRequests).Post(reqRepoWriter(), bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) |
@@ -0,0 +1,186 @@ | |||
// Copyright 2016 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package repo | |||
import ( | |||
"strings" | |||
api "code.gitea.io/sdk/gitea" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/context" | |||
) | |||
// GetRelease get a single release of a repository | |||
func GetRelease(ctx *context.APIContext) { | |||
id := ctx.ParamsInt64(":id") | |||
release, err := models.GetReleaseByID(id) | |||
if err != nil { | |||
ctx.Error(500, "GetReleaseByID", err) | |||
return | |||
} | |||
if release.RepoID != ctx.Repo.Repository.ID { | |||
ctx.Status(404) | |||
return | |||
} | |||
if err := release.LoadAttributes(); err != nil { | |||
ctx.Error(500, "LoadAttributes", err) | |||
return | |||
} | |||
ctx.JSON(200, release.APIFormat()) | |||
} | |||
// ListReleases list a repository's releases | |||
func ListReleases(ctx *context.APIContext) { | |||
releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, 1, 2147483647) | |||
if err != nil { | |||
ctx.Error(500, "GetReleasesByRepoID", err) | |||
return | |||
} | |||
rels := make([]*api.Release, len(releases)) | |||
access, err := models.AccessLevel(ctx.User, ctx.Repo.Repository) | |||
if err != nil { | |||
ctx.Error(500, "AccessLevel", err) | |||
return | |||
} | |||
for i, release := range releases { | |||
if release.IsDraft && access < models.AccessModeWrite { | |||
// hide drafts from users without push access | |||
continue | |||
} | |||
if err := release.LoadAttributes(); err != nil { | |||
ctx.Error(500, "LoadAttributes", err) | |||
return | |||
} | |||
rels[i] = release.APIFormat() | |||
} | |||
ctx.JSON(200, rels) | |||
} | |||
// CreateRelease create a release | |||
func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { | |||
if ctx.Repo.AccessMode < models.AccessModeWrite { | |||
ctx.Status(403) | |||
return | |||
} | |||
if !ctx.Repo.GitRepo.IsTagExist(form.TagName) { | |||
ctx.Status(404) | |||
return | |||
} | |||
tag, err := ctx.Repo.GitRepo.GetTag(form.TagName) | |||
if err != nil { | |||
ctx.Error(500, "GetTag", err) | |||
return | |||
} | |||
commit, err := tag.Commit() | |||
if err != nil { | |||
ctx.Error(500, "Commit", err) | |||
return | |||
} | |||
commitsCount, err := commit.CommitsCount() | |||
if err != nil { | |||
ctx.Error(500, "CommitsCount", err) | |||
return | |||
} | |||
rel := &models.Release{ | |||
RepoID: ctx.Repo.Repository.ID, | |||
PublisherID: ctx.User.ID, | |||
Publisher: ctx.User, | |||
TagName: form.TagName, | |||
LowerTagName: strings.ToLower(form.TagName), | |||
Target: form.Target, | |||
Title: form.Title, | |||
Sha1: commit.ID.String(), | |||
NumCommits: commitsCount, | |||
Note: form.Note, | |||
IsDraft: form.IsDraft, | |||
IsPrerelease: form.IsPrerelease, | |||
CreatedUnix: commit.Author.When.Unix(), | |||
} | |||
if err := models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil { | |||
if models.IsErrReleaseAlreadyExist(err) { | |||
ctx.Status(409) | |||
} else { | |||
ctx.Error(500, "CreateRelease", err) | |||
} | |||
return | |||
} | |||
ctx.JSON(201, rel.APIFormat()) | |||
} | |||
// EditRelease edit a release | |||
func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) { | |||
if ctx.Repo.AccessMode < models.AccessModeWrite { | |||
ctx.Status(403) | |||
return | |||
} | |||
id := ctx.ParamsInt64(":id") | |||
rel, err := models.GetReleaseByID(id) | |||
if err != nil { | |||
ctx.Error(500, "GetReleaseByID", err) | |||
return | |||
} | |||
if rel.RepoID != ctx.Repo.Repository.ID { | |||
ctx.Status(404) | |||
return | |||
} | |||
if len(form.TagName) > 0 { | |||
rel.TagName = form.TagName | |||
} | |||
if len(form.Target) > 0 { | |||
rel.Target = form.Target | |||
} | |||
if len(form.Title) > 0 { | |||
rel.Title = form.Title | |||
} | |||
if len(form.Note) > 0 { | |||
rel.Note = form.Note | |||
} | |||
if form.IsDraft != nil { | |||
rel.IsDraft = *form.IsDraft | |||
} | |||
if form.IsPrerelease != nil { | |||
rel.IsPrerelease = *form.IsPrerelease | |||
} | |||
if err := models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil { | |||
ctx.Error(500, "UpdateRelease", err) | |||
return | |||
} | |||
rel, err = models.GetReleaseByID(id) | |||
if err != nil { | |||
ctx.Error(500, "GetReleaseByID", err) | |||
return | |||
} | |||
if err := rel.LoadAttributes(); err != nil { | |||
ctx.Error(500, "LoadAttributes", err) | |||
return | |||
} | |||
ctx.JSON(200, rel.APIFormat()) | |||
} | |||
// DeleteRelease delete a release from a repository | |||
func DeleteRelease(ctx *context.APIContext) { | |||
if ctx.Repo.AccessMode < models.AccessModeWrite { | |||
ctx.Status(403) | |||
return | |||
} | |||
id := ctx.ParamsInt64(":id") | |||
release, err := models.GetReleaseByID(id) | |||
if err != nil { | |||
ctx.Error(500, "GetReleaseByID", err) | |||
return | |||
} | |||
if release.RepoID != ctx.Repo.Repository.ID { | |||
ctx.Status(404) | |||
return | |||
} | |||
if err := models.DeleteReleaseByID(id, ctx.User); err != nil { | |||
ctx.Error(500, "DeleteReleaseByID", err) | |||
return | |||
} | |||
ctx.Status(204) | |||
} |
@@ -115,7 +115,6 @@ func (c *Client) DeleteOrgHook(org string, id int64) error { | |||
return err | |||
} | |||
// DeleteRepoHook delete one hook from a repository, with hook id | |||
func (c *Client) DeleteRepoHook(user, repo string, id int64) error { | |||
_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil) |
@@ -0,0 +1,101 @@ | |||
// Copyright 2016 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package gitea | |||
import ( | |||
"bytes" | |||
"encoding/json" | |||
"fmt" | |||
"time" | |||
) | |||
// Release represents a repository release | |||
type Release struct { | |||
ID int64 `json:"id"` | |||
TagName string `json:"name"` | |||
Target string `json:"target_commitish"` | |||
Title string `json:"name"` | |||
Note string `json:"body"` | |||
URL string `json:"url"` | |||
TarURL string `json:"tarball_url"` | |||
ZipURL string `json:"zipball_url"` | |||
IsDraft bool `json:"draft"` | |||
IsPrerelease bool `json:"prerelease"` | |||
CreatedAt time.Time `json:"created_at"` | |||
PublishedAt time.Time `json:"published_at"` | |||
Publisher *User `json:"author"` | |||
} | |||
// ListReleases list releases of a repository | |||
func (c *Client) ListReleases(user, repo string) ([]*Release, error) { | |||
releases := make([]*Release, 0, 10) | |||
err := c.getParsedResponse("GET", | |||
fmt.Sprintf("/repos/%s/%s/releases", user, repo), | |||
nil, nil, &releases) | |||
return releases, err | |||
} | |||
// GetRelease get a release of a repository | |||
func (c *Client) GetRelease(user, repo string, id int64) (*Release, error) { | |||
r := new(Release) | |||
err := c.getParsedResponse("GET", | |||
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), | |||
nil, nil, &r) | |||
return r, err | |||
} | |||
// CreateReleaseOption options when creating a release | |||
type CreateReleaseOption struct { | |||
TagName string `json:"tag_name" binding:"Required"` | |||
Target string `json:"target_commitish"` | |||
Title string `json:"name"` | |||
Note string `json:"body"` | |||
IsDraft bool `json:"draft"` | |||
IsPrerelease bool `json:"prerelease"` | |||
} | |||
// CreateRelease create a release | |||
func (c *Client) CreateRelease(user, repo string, form CreateReleaseOption) (*Release, error) { | |||
body, err := json.Marshal(form) | |||
if err != nil { | |||
return nil, err | |||
} | |||
r := new(Release) | |||
err = c.getParsedResponse("POST", | |||
fmt.Sprintf("/repos/%s/%s/releases", user, repo), | |||
jsonHeader, bytes.NewReader(body), r) | |||
return r, err | |||
} | |||
// EditReleaseOption options when editing a release | |||
type EditReleaseOption struct { | |||
TagName string `json:"tag_name"` | |||
Target string `json:"target_commitish"` | |||
Title string `json:"name"` | |||
Note string `json:"body"` | |||
IsDraft *bool `json:"draft"` | |||
IsPrerelease *bool `json:"prerelease"` | |||
} | |||
// EditRelease edit a release | |||
func (c *Client) EditRelease(user, repo string, id int64, form EditReleaseOption) (*Release, error) { | |||
body, err := json.Marshal(form) | |||
if err != nil { | |||
return nil, err | |||
} | |||
r := new(Release) | |||
err = c.getParsedResponse("PATCH", | |||
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), | |||
jsonHeader, bytes.NewReader(body), r) | |||
return r, err | |||
} | |||
// DeleteRelease delete a release from a repository | |||
func (c *Client) DeleteRelease(user, repo string, id int64) error { | |||
_, err := c.getResponse("DELETE", | |||
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), | |||
nil, nil) | |||
return err | |||
} |
@@ -9,10 +9,10 @@ | |||
"revisionTime": "2016-12-28T14:57:51Z" | |||
}, | |||
{ | |||
"checksumSHA1": "5J8ejjEp2moLyK1pD++Jzof8DFs=", | |||
"checksumSHA1": "BKj0haFTDebzdC2nACpoGzp3s8A=", | |||
"path": "code.gitea.io/sdk/gitea", | |||
"revision": "c0e081342a4b99d90371081b888765b91f05546f", | |||
"revisionTime": "2016-12-29T09:40:42Z" | |||
"revision": "2064cc397bc48b0a46f8324a97421a824b11882e", | |||
"revisionTime": "2016-12-31T14:43:27Z" | |||
}, | |||
{ | |||
"checksumSHA1": "IyfS7Rbl6OgR83QR7TOfKdDCq+M=", |