| @@ -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=", | |||