Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API for manipulating Git hooks #6436

Merged
merged 11 commits into from
Apr 17, 2019
14 changes: 8 additions & 6 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ ignored = ["google.golang.org/appengine*"]
non-go = true

[[constraint]]
branch = "master"
branch = "git-hook"
name = "code.gitea.io/git"
source = "https://github.com/codeocean/gitea-git.git"
segevfiner marked this conversation as resolved.
Show resolved Hide resolved

[[constraint]]
branch = "master"
branch = "git-hook"
name = "code.gitea.io/sdk"
source = "https://github.com/codeocean/gitea-go-sdk.git"
segevfiner marked this conversation as resolved.
Show resolved Hide resolved

[[constraint]]
revision = "05d86ea8f6e30456949f612cf68cf4a27ce8c9c5"
Expand Down
4 changes: 2 additions & 2 deletions modules/context/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ func APIContexter() macaron.Handler {
}

// ReferencesGitRepo injects the GitRepo into the Context
func ReferencesGitRepo() macaron.Handler {
func ReferencesGitRepo(allowEmpty bool) macaron.Handler {
return func(ctx *APIContext) {
// Empty repository does not have reference information.
if ctx.Repo.Repository.IsEmpty {
if !allowEmpty && ctx.Repo.Repository.IsEmpty {
return
}

Expand Down
23 changes: 20 additions & 3 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,15 @@ func reqOrgOwnership() macaron.Handler {
}
}

func reqGitHook() macaron.Handler {
return func(ctx *context.APIContext) {
if !ctx.User.CanEditGitHook() {
ctx.NotFound("GitHookService", nil)
return
}
}
}

func orgAssignment(args ...bool) macaron.Handler {
var (
assignOrg bool
Expand Down Expand Up @@ -498,6 +507,14 @@ func RegisterRoutes(m *macaron.Macaron) {
Delete(repo.DeleteHook)
m.Post("/tests", context.RepoRef(), repo.TestHook)
})
m.Group("/git", func() {
m.Combo("").Get(repo.ListGitHooks)
m.Group("/:id", func() {
m.Combo("").Get(repo.GetGitHook).
Patch(bind(api.EditGitHookOption{}), repo.EditGitHook).
Delete(repo.DeleteGitHook)
})
}, reqGitHook(), context.ReferencesGitRepo(true))
}, reqToken(), reqAdmin())
m.Group("/collaborators", func() {
m.Get("", repo.ListCollaborators)
Expand Down Expand Up @@ -589,10 +606,10 @@ func RegisterRoutes(m *macaron.Macaron) {
})
m.Group("/releases", func() {
m.Combo("").Get(repo.ListReleases).
Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.CreateReleaseOption{}), repo.CreateRelease)
m.Group("/:id", func() {
m.Combo("").Get(repo.GetRelease).
Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease).
Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.EditReleaseOption{}), repo.EditRelease).
Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteRelease)
m.Group("/assets", func() {
m.Combo("").Get(repo.ListReleaseAttachments).
Expand All @@ -614,7 +631,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Combo("/merge").Get(repo.IsPullRequestMerged).
Post(reqToken(), mustNotBeArchived, reqRepoWriter(models.UnitTypePullRequests), bind(auth.MergePullRequestForm{}), repo.MergePullRequest)
})
}, mustAllowPulls, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo())
}, mustAllowPulls, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(false))
m.Group("/statuses", func() {
m.Combo("/:sha").Get(repo.GetCommitStatuses).
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
Expand Down
9 changes: 9 additions & 0 deletions routers/api/v1/convert/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,15 @@ func ToHook(repoLink string, w *models.Webhook) *api.Hook {
}
}

// ToGitHook convert git.Hook to api.GitHook
func ToGitHook(h *git.Hook) *api.GitHook {
return &api.GitHook{
Name: h.Name(),
IsActive: h.IsActive,
Content: h.Content,
}
}

// ToDeployKey convert models.DeployKey to api.DeployKey
func ToDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey {
return &api.DeployKey{
Expand Down
187 changes: 187 additions & 0 deletions routers/api/v1/repo/git_hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// Copyright 2019 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 (
"code.gitea.io/git"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/convert"
api "code.gitea.io/sdk/gitea"
)

// ListGitHooks list all Git hooks of a repository
func ListGitHooks(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/hooks/git repository repoListGitHooks
// ---
// summary: List the Git hooks in a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/GitHookList"
hooks, err := ctx.Repo.GitRepo.Hooks()
if err != nil {
ctx.Error(500, "Hooks", err)
return
}

apiHooks := make([]*api.GitHook, len(hooks))
for i := range hooks {
apiHooks[i] = convert.ToGitHook(hooks[i])
}
ctx.JSON(200, &apiHooks)
}

// GetGitHook get a repo's Git hook by id
func GetGitHook(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/hooks/git/{id} repository repoGetGitHook
// ---
// summary: Get a Git hook
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: id
// in: path
// description: id of the hook to get
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/GitHook"
// "404":
// "$ref": "#/responses/notFound"
hookID := ctx.Params(":id")
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
if err != nil {
if err == git.ErrNotValidHook {
ctx.NotFound()
} else {
ctx.Error(500, "GetHook", err)
}
return
}
ctx.JSON(200, convert.ToGitHook(hook))
}

// EditGitHook modify a Git hook of a repository
func EditGitHook(ctx *context.APIContext, form api.EditGitHookOption) {
// swagger:operation PATCH /repos/{owner}/{repo}/hooks/git/{id} repository repoEditGitHook
// ---
// summary: Edit a Git hook in a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: id
// in: path
// description: id of the hook to get
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/EditGitHookOption"
// responses:
// "200":
// "$ref": "#/responses/GitHook"
// "404":
// "$ref": "#/responses/notFound"
hookID := ctx.Params(":id")
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
if err != nil {
if err == git.ErrNotValidHook {
ctx.NotFound()
} else {
ctx.Error(500, "GetHook", err)
}
return
}

hook.Content = form.Content
if err = hook.Update(); err != nil {
ctx.Error(500, "hook.Update", err)
return
}

ctx.JSON(200, convert.ToGitHook(hook))
}

// DeleteGitHook delete a Git hook of a repository
func DeleteGitHook(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/hooks/git/{id} repository repoDeleteGitHook
// ---
// summary: Delete a Git hook in a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: id
// in: path
// description: id of the hook to get
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
hookID := ctx.Params(":id")
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
if err != nil {
if err == git.ErrNotValidHook {
ctx.NotFound()
} else {
ctx.Error(500, "GetHook", err)
}
return
}

hook.Content = ""
if err = hook.Update(); err != nil {
ctx.Error(500, "hook.Update", err)
return
}

ctx.Status(204)
}
3 changes: 3 additions & 0 deletions routers/api/v1/swagger/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ type swaggerParameterBodies struct {
// in:body
EditHookOption api.EditHookOption

// in:body
EditGitHookOption api.EditGitHookOption

// in:body
CreateIssueOption api.CreateIssueOption
// in:body
Expand Down
14 changes: 14 additions & 0 deletions routers/api/v1/swagger/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ type swaggerResponseHookList struct {
Body []api.Branch `json:"body"`
}

// GitHook
// swagger:response GitHook
type swaggerResponseGitHook struct {
// in:body
Body api.GitHook `json:"body"`
}

// GitHookList
// swagger:response GitHookList
type swaggerResponseGitHookList struct {
// in:body
Body []api.GitHook `json:"body"`
}

// Release
// swagger:response Release
type swaggerResponseRelease struct {
Expand Down
Loading