diff --git a/cmd/hook.go b/cmd/hook.go index d120f21b2..06250181d 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -7,20 +7,18 @@ package cmd import ( "bufio" "bytes" - "crypto/tls" "fmt" "os" + "path/filepath" "strconv" "strings" "code.gitea.io/git" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" - "github.com/Unknwon/com" "github.com/urfave/cli" ) @@ -64,6 +62,12 @@ var ( } ) +func hookSetup(logPath string) { + setting.NewContext() + log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath)) + models.LoadConfigs() +} + func runHookPreReceive(c *cli.Context) error { if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { return nil @@ -75,9 +79,7 @@ func runHookPreReceive(c *cli.Context) error { setting.CustomConf = c.GlobalString("config") } - if err := setup("hooks/pre-receive.log"); err != nil { - fail("Hook pre-receive init failed", fmt.Sprintf("setup: %v", err)) - } + hookSetup("hooks/pre-receive.log") // the environment setted on serv command repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64) @@ -119,18 +121,20 @@ func runHookPreReceive(c *cli.Context) error { }*/ branchName := strings.TrimPrefix(refFullName, git.BranchPrefix) - protectBranch, err := models.GetProtectedBranchBy(repoID, branchName) + protectBranch, err := private.GetProtectedBranchBy(repoID, branchName) if err != nil { log.GitLogger.Fatal(2, "retrieve protected branches information failed") } if protectBranch != nil { - // check and deletion - if newCommitID == git.EmptySHA { - fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "") - } else { - fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "") - //fail(fmt.Sprintf("branch %s is protected from force push", branchName), "") + if !protectBranch.CanPush { + // check and deletion + if newCommitID == git.EmptySHA { + fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "") + } else { + fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "") + //fail(fmt.Sprintf("branch %s is protected from force push", branchName), "") + } } } } @@ -149,9 +153,7 @@ func runHookUpdate(c *cli.Context) error { setting.CustomConf = c.GlobalString("config") } - if err := setup("hooks/update.log"); err != nil { - fail("Hook update init failed", fmt.Sprintf("setup: %v", err)) - } + hookSetup("hooks/update.log") return nil } @@ -167,13 +169,10 @@ func runHookPostReceive(c *cli.Context) error { setting.CustomConf = c.GlobalString("config") } - if err := setup("hooks/post-receive.log"); err != nil { - fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err)) - } + hookSetup("hooks/post-receive.log") // the environment setted on serv command repoUser := os.Getenv(models.EnvRepoUsername) - repoUserSalt := os.Getenv(models.EnvRepoUserSalt) isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true") repoName := os.Getenv(models.EnvRepoName) pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64) @@ -199,7 +198,7 @@ func runHookPostReceive(c *cli.Context) error { newCommitID := string(fields[1]) refFullName := string(fields[2]) - if err := models.PushUpdate(models.PushUpdateOptions{ + if err := private.PushUpdate(models.PushUpdateOptions{ RefFullName: refFullName, OldCommitID: oldCommitID, NewCommitID: newCommitID, @@ -210,23 +209,6 @@ func runHookPostReceive(c *cli.Context) error { }); err != nil { log.GitLogger.Error(2, "Update: %v", err) } - - // Ask for running deliver hook and test pull request tasks. - reqURL := setting.LocalURL + repoUser + "/" + repoName + "/tasks/trigger?branch=" + - strings.TrimPrefix(refFullName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUserSalt) + "&pusher=" + com.ToStr(pusherID) - log.GitLogger.Trace("Trigger task: %s", reqURL) - - resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{ - InsecureSkipVerify: true, - }).Response() - if err == nil { - resp.Body.Close() - if resp.StatusCode/100 != 2 { - log.GitLogger.Error(2, "Failed to trigger task: not 2xx response code") - } - } else { - log.GitLogger.Error(2, "Failed to trigger task: %v", err) - } } return nil diff --git a/models/update.go b/models/update.go index b3a8a1c9f..7ee00f2c2 100644 --- a/models/update.go +++ b/models/update.go @@ -65,11 +65,11 @@ type PushUpdateOptions struct { // PushUpdate must be called for any push actions in order to // generates necessary push action history feeds. -func PushUpdate(opts PushUpdateOptions) (err error) { +func PushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { isNewRef := opts.OldCommitID == git.EmptySHA isDelRef := opts.NewCommitID == git.EmptySHA if isNewRef && isDelRef { - return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA) + return nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA) } repoPath := RepoPath(opts.RepoUserName, opts.RepoName) @@ -77,28 +77,28 @@ func PushUpdate(opts PushUpdateOptions) (err error) { gitUpdate := exec.Command("git", "update-server-info") gitUpdate.Dir = repoPath if err = gitUpdate.Run(); err != nil { - return fmt.Errorf("Failed to call 'git update-server-info': %v", err) + return nil, fmt.Errorf("Failed to call 'git update-server-info': %v", err) } - if isDelRef { - log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %s", - opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName) - return nil + owner, err := GetUserByName(opts.RepoUserName) + if err != nil { + return nil, fmt.Errorf("GetUserByName: %v", err) } - gitRepo, err := git.OpenRepository(repoPath) + repo, err = GetRepositoryByName(owner.ID, opts.RepoName) if err != nil { - return fmt.Errorf("OpenRepository: %v", err) + return nil, fmt.Errorf("GetRepositoryByName: %v", err) } - owner, err := GetUserByName(opts.RepoUserName) - if err != nil { - return fmt.Errorf("GetUserByName: %v", err) + if isDelRef { + log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %s", + opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName) + return repo, nil } - repo, err := GetRepositoryByName(owner.ID, opts.RepoName) + gitRepo, err := git.OpenRepository(repoPath) if err != nil { - return fmt.Errorf("GetRepositoryByName: %v", err) + return nil, fmt.Errorf("OpenRepository: %v", err) } if err = repo.UpdateSize(); err != nil { @@ -116,14 +116,14 @@ func PushUpdate(opts PushUpdateOptions) (err error) { NewCommitID: opts.NewCommitID, Commits: &PushCommits{}, }); err != nil { - return fmt.Errorf("CommitRepoAction (tag): %v", err) + return nil, fmt.Errorf("CommitRepoAction (tag): %v", err) } - return nil + return repo, nil } newCommit, err := gitRepo.GetCommit(opts.NewCommitID) if err != nil { - return fmt.Errorf("gitRepo.GetCommit: %v", err) + return nil, fmt.Errorf("gitRepo.GetCommit: %v", err) } // Push new branch. @@ -131,12 +131,12 @@ func PushUpdate(opts PushUpdateOptions) (err error) { if isNewRef { l, err = newCommit.CommitsBeforeLimit(10) if err != nil { - return fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err) + return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err) } } else { l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID) if err != nil { - return fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err) + return nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err) } } @@ -149,7 +149,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) { NewCommitID: opts.NewCommitID, Commits: ListToPushCommits(l), }); err != nil { - return fmt.Errorf("CommitRepoAction (branch): %v", err) + return nil, fmt.Errorf("CommitRepoAction (branch): %v", err) } - return nil + return repo, nil } diff --git a/modules/private/branch.go b/modules/private/branch.go new file mode 100644 index 000000000..faee1c918 --- /dev/null +++ b/modules/private/branch.go @@ -0,0 +1,43 @@ +// Copyright 2017 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 private + +import ( + "crypto/tls" + "encoding/json" + "fmt" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +// GetProtectedBranchBy get protected branch information +func GetProtectedBranchBy(repoID int64, branchName string) (*models.ProtectedBranch, error) { + // Ask for running deliver hook and test pull request tasks. + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/branch/%d/%s", repoID, branchName) + log.GitLogger.Trace("GetProtectedBranchBy: %s", reqURL) + + resp, err := newRequest(reqURL, "GET").SetTLSClientConfig(&tls.Config{ + InsecureSkipVerify: true, + }).Response() + if err != nil { + return nil, err + } + + var branch models.ProtectedBranch + if err := json.NewDecoder(resp.Body).Decode(&branch); err != nil { + return nil, err + } + + defer resp.Body.Close() + + // All 2XX status codes are accepted and others will return an error + if resp.StatusCode/100 != 2 { + return nil, fmt.Errorf("Failed to update public key: %s", decodeJSONError(resp).Err) + } + + return &branch, nil +} diff --git a/modules/private/internal.go b/modules/private/internal.go index 017e265b7..fbf9d6a01 100644 --- a/modules/private/internal.go +++ b/modules/private/internal.go @@ -1,3 +1,7 @@ +// Copyright 2017 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 private import ( diff --git a/modules/private/push_update.go b/modules/private/push_update.go new file mode 100644 index 000000000..348a40e0a --- /dev/null +++ b/modules/private/push_update.go @@ -0,0 +1,43 @@ +// Copyright 2017 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 private + +import ( + "crypto/tls" + "encoding/json" + "fmt" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +// PushUpdate update publick key updates +func PushUpdate(opt models.PushUpdateOptions) error { + // Ask for running deliver hook and test pull request tasks. + reqURL := setting.LocalURL + "api/internal/push/update" + log.GitLogger.Trace("PushUpdate: %s", reqURL) + + body, err := json.Marshal(&opt) + if err != nil { + return err + } + + resp, err := newRequest(reqURL, "POST").Body(body).SetTLSClientConfig(&tls.Config{ + InsecureSkipVerify: true, + }).Response() + if err != nil { + return err + } + + defer resp.Body.Close() + + // All 2XX status codes are accepted and others will return an error + if resp.StatusCode/100 != 2 { + return fmt.Errorf("Failed to update public key: %s", decodeJSONError(resp).Err) + } + + return nil +} diff --git a/routers/private/branch.go b/routers/private/branch.go new file mode 100644 index 000000000..e74087950 --- /dev/null +++ b/routers/private/branch.go @@ -0,0 +1,30 @@ +// Copyright 2017 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 private + +import ( + "code.gitea.io/gitea/models" + + macaron "gopkg.in/macaron.v1" +) + +// GetProtectedBranchBy get protected branch information +func GetProtectedBranchBy(ctx *macaron.Context) { + repoID := ctx.ParamsInt64(":id") + branchName := ctx.Params(":branch") + protectBranch, err := models.GetProtectedBranchBy(repoID, branchName) + if err != nil { + ctx.JSON(500, map[string]interface{}{ + "err": err.Error(), + }) + return + } else if protectBranch != nil { + ctx.JSON(200, protectBranch) + } else { + ctx.JSON(200, &models.ProtectedBranch{ + CanPush: true, + }) + } +} diff --git a/routers/private/internal.go b/routers/private/internal.go index d662aa2c7..f663306e9 100644 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/setting" + macaron "gopkg.in/macaron.v1" ) @@ -40,5 +41,7 @@ func UpdatePublicKey(ctx *macaron.Context) { func RegisterRoutes(m *macaron.Macaron) { m.Group("/", func() { m.Post("/ssh/:id/update", UpdatePublicKey) + m.Post("/push/update", PushUpdate) + m.Get("/branch/:id/:branch", GetProtectedBranchBy) }, CheckInternalToken) } diff --git a/routers/private/push_update.go b/routers/private/push_update.go new file mode 100644 index 000000000..3008ef0e7 --- /dev/null +++ b/routers/private/push_update.go @@ -0,0 +1,60 @@ +// Copyright 2017 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 private + +import ( + "encoding/json" + "strings" + + "code.gitea.io/git" + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + + macaron "gopkg.in/macaron.v1" +) + +// PushUpdate update public key updates +func PushUpdate(ctx *macaron.Context) { + var opt models.PushUpdateOptions + if err := json.NewDecoder(ctx.Req.Request.Body).Decode(&opt); err != nil { + ctx.JSON(500, map[string]interface{}{ + "err": err.Error(), + }) + return + } + + branch := strings.TrimPrefix(opt.RefFullName, git.BranchPrefix) + if len(branch) == 0 || opt.PusherID <= 0 { + ctx.Error(404) + log.Trace("PushUpdate: branch or secret is empty, or pusher ID is not valid") + return + } + + repo, err := models.PushUpdate(opt) + if err != nil { + ctx.JSON(500, map[string]interface{}{ + "err": err.Error(), + }) + return + } + + pusher, err := models.GetUserByID(opt.PusherID) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Error(404) + } else { + ctx.JSON(500, map[string]interface{}{ + "err": err.Error(), + }) + } + return + } + + log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name) + + go models.HookQueue.Add(repo.ID) + go models.AddTestPullRequestTask(pusher, repo.ID, branch, true) + ctx.Status(202) +}