From ec82565089e7010abe488df766089edee79567d8 Mon Sep 17 00:00:00 2001
From: Unknwon
Date: Tue, 14 Feb 2017 16:22:16 -0500
Subject: [PATCH 1/7] git: delegate all server-side Git hooks (#1623)
---
cmd/hook.go | 140 +++++++++++++++++++++++++++++++
cmd/{serve.go => serv.go} | 93 ++++++++++----------
main.go | 2 +-
models/action.go | 2 +-
models/migrations/migrations.go | 2 +
models/migrations/v19.go | 74 ++++++++++++++++
models/repo.go | 54 +++++++-----
models/wiki.go | 4 +-
options/locale/locale_en-US.ini | 4 +-
routers/admin/admin.go | 4 +-
templates/admin/dashboard.tmpl | 2 +-
vendor/code.gitea.io/git/hook.go | 26 +++---
12 files changed, 322 insertions(+), 85 deletions(-)
create mode 100644 cmd/hook.go
rename cmd/{serve.go => serv.go} (80%)
create mode 100644 models/migrations/v19.go
diff --git a/cmd/hook.go b/cmd/hook.go
new file mode 100644
index 000000000000..7b627bd76a42
--- /dev/null
+++ b/cmd/hook.go
@@ -0,0 +1,140 @@
+// Copyright 2014 The Gogs 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 cmd
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+
+ "code.gitea.io/gitea/models"
+
+ "github.com/urfave/cli"
+)
+
+var (
+ CmdHook = cli.Command{
+ Name: "hook",
+ Usage: "Delegate commands to corresponding Git hooks",
+ Description: "All sub-commands should only be called by Git",
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "config, c",
+ Value: "custom/conf/app.ini",
+ Usage: "Custom configuration file path",
+ },
+ },
+ Subcommands: []cli.Command{
+ subcmdHookPreReceive,
+ subcmdHookUpadte,
+ subcmdHookPostReceive,
+ },
+ }
+
+ subcmdHookPreReceive = cli.Command{
+ Name: "pre-receive",
+ Usage: "Delegate pre-receive Git hook",
+ Description: "This command should only be called by Git",
+ Action: runHookPreReceive,
+ }
+ subcmdHookUpadte = cli.Command{
+ Name: "update",
+ Usage: "Delegate update Git hook",
+ Description: "This command should only be called by Git",
+ Action: runHookUpdate,
+ }
+ subcmdHookPostReceive = cli.Command{
+ Name: "post-receive",
+ Usage: "Delegate post-receive Git hook",
+ Description: "This command should only be called by Git",
+ Action: runHookPostReceive,
+ }
+)
+
+func runHookPreReceive(c *cli.Context) error {
+ if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
+ return nil
+ }
+ if err := setup("hooks/pre-receive.log"); err != nil {
+ fail("Hook pre-receive init failed", fmt.Sprintf("setup: %v", err))
+ }
+
+ buf := bytes.NewBuffer(nil)
+ scanner := bufio.NewScanner(os.Stdin)
+ for scanner.Scan() {
+ buf.Write(scanner.Bytes())
+ buf.WriteByte('\n')
+ }
+
+ customHooksPath := os.Getenv(envRepoCustomHooksPath)
+ hookCmd := exec.Command(filepath.Join(customHooksPath, "pre-receive"))
+ hookCmd.Stdout = os.Stdout
+ hookCmd.Stdin = buf
+ hookCmd.Stderr = os.Stderr
+ if err := hookCmd.Run(); err != nil {
+ fail("Internal error", "Fail to execute custom pre-receive hook: %v", err)
+ }
+ return nil
+}
+
+func runHookUpdate(c *cli.Context) error {
+ if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
+ return nil
+ }
+
+ if err := setup("hooks/pre-receive.log"); err != nil {
+ fail("Hook update init failed", fmt.Sprintf("setup: %v", err))
+ }
+
+ args := c.Args()
+ if len(args) != 3 {
+ fail("Arguments received are not equal to three", "Arguments received are not equal to three")
+ } else if len(args[0]) == 0 {
+ fail("First argument 'refName' is empty", "First argument 'refName' is empty")
+ }
+
+ uuid := os.Getenv(envUpdateTaskUUID)
+ if err := models.AddUpdateTask(&models.UpdateTask{
+ UUID: uuid,
+ RefName: args[0],
+ OldCommitID: args[1],
+ NewCommitID: args[2],
+ }); err != nil {
+ fail("Internal error", "Fail to add update task '%s': %v", uuid, err)
+ }
+
+ customHooksPath := os.Getenv(envRepoCustomHooksPath)
+ hookCmd := exec.Command(filepath.Join(customHooksPath, "update"), args...)
+ hookCmd.Stdout = os.Stdout
+ hookCmd.Stdin = os.Stdin
+ hookCmd.Stderr = os.Stderr
+ if err := hookCmd.Run(); err != nil {
+ fail("Internal error", "Fail to execute custom pre-receive hook: %v", err)
+ }
+ return nil
+}
+
+func runHookPostReceive(c *cli.Context) error {
+ if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
+ return nil
+ }
+
+ if err := setup("hooks/pre-receive.log"); err != nil {
+ fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err))
+ }
+
+ customHooksPath := os.Getenv(envRepoCustomHooksPath)
+ hookCmd := exec.Command(filepath.Join(customHooksPath, "post-receive"))
+ hookCmd.Stdout = os.Stdout
+ hookCmd.Stdin = os.Stdin
+ hookCmd.Stderr = os.Stderr
+ if err := hookCmd.Run(); err != nil {
+ fail("Internal error", "Fail to execute custom post-receive hook: %v", err)
+ }
+ return nil
+}
diff --git a/cmd/serve.go b/cmd/serv.go
similarity index 80%
rename from cmd/serve.go
rename to cmd/serv.go
index f4a3c3d2c64e..80864ad520da 100644
--- a/cmd/serve.go
+++ b/cmd/serv.go
@@ -28,8 +28,10 @@ import (
)
const (
- accessDenied = "Repository does not exist or you do not have access"
- lfsAuthenticateVerb = "git-lfs-authenticate"
+ accessDenied = "Repository does not exist or you do not have access"
+ lfsAuthenticateVerb = "git-lfs-authenticate"
+ envUpdateTaskUUID = "GITEA_UUID"
+ envRepoCustomHooksPath = "REPO_CUSTOM_HOOKS_PATH"
)
// CmdServ represents the available serv sub-command.
@@ -52,7 +54,7 @@ func setup(logPath string) error {
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
models.LoadConfigs()
- if setting.UseSQLite3 || setting.UseTiDB {
+ if setting.UseSQLite3 {
workDir, _ := setting.WorkDir()
if err := os.Chdir(workDir); err != nil {
log.GitLogger.Fatal(4, "Failed to change directory %s: %v", workDir, err)
@@ -63,7 +65,7 @@ func setup(logPath string) error {
return models.SetEngine()
}
-func parseCmd(cmd string) (string, string) {
+func parseSSHCmd(cmd string) (string, string) {
ss := strings.SplitN(cmd, " ", 2)
if len(ss) != 2 {
return "", ""
@@ -144,6 +146,8 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
func runServ(c *cli.Context) error {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
+ } else if c.GlobalIsSet("config") {
+ setting.CustomConf = c.GlobalString("config")
}
if err := setup("serv.log"); err != nil {
@@ -166,11 +170,10 @@ func runServ(c *cli.Context) error {
return nil
}
- verb, args := parseCmd(cmd)
+ verb, args := parseSSHCmd(cmd)
var lfsVerb string
if verb == lfsAuthenticateVerb {
-
if !setting.LFS.StartServer {
fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
}
@@ -182,13 +185,13 @@ func runServ(c *cli.Context) error {
}
}
- repoPath := strings.ToLower(strings.Trim(args, "'"))
- rr := strings.SplitN(repoPath, "/", 2)
- if len(rr) != 2 {
+ repoFullName := strings.ToLower(strings.Trim(args, "'"))
+ repoFields := strings.SplitN(repoFullName, "/", 2)
+ if len(repoFields) != 2 {
fail("Invalid repository path", "Invalid repository path: %v", args)
}
- username := strings.ToLower(rr[0])
- reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
+ username := strings.ToLower(repoFields[0])
+ reponame := strings.ToLower(strings.TrimSuffix(repoFields[1], ".git"))
isWiki := false
if strings.HasSuffix(reponame, ".wiki") {
@@ -196,25 +199,26 @@ func runServ(c *cli.Context) error {
reponame = reponame[:len(reponame)-5]
}
- repoUser, err := models.GetUserByName(username)
+ repoOwner, err := models.GetUserByName(username)
if err != nil {
if models.IsErrUserNotExist(err) {
fail("Repository owner does not exist", "Unregistered owner: %s", username)
}
- fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
+ fail("Internal error", "Fail to get repository owner '%s': %v", username, err)
}
- repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
+ repo, err := models.GetRepositoryByName(repoOwner.ID, reponame)
if err != nil {
if models.IsErrRepoNotExist(err) {
- fail(accessDenied, "Repository does not exist: %s/%s", repoUser.Name, reponame)
+ fail(accessDenied, "Repository does not exist: %s/%s", repoOwner.Name, reponame)
}
- fail("Internal error", "Failed to get repository: %v", err)
+ fail("Internal error", "Fail to get repository: %v", err)
}
+ repo.Owner = repoOwner
- requestedMode, has := allowedCommands[verb]
- if !has {
- fail("Unknown git command", "Unknown git command %s", verb)
+ requestedMode, ok := allowedCommands[verb]
+ if !ok {
+ fail("Unknown git command", "Unknown git command '%s'", verb)
}
if verb == lfsAuthenticateVerb {
@@ -235,18 +239,19 @@ func runServ(c *cli.Context) error {
keyID int64
user *models.User
)
- if requestedMode == models.AccessModeWrite || repo.IsPrivate {
- keys := strings.Split(c.Args()[0], "-")
- if len(keys) != 2 {
- fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
- }
- key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
- if err != nil {
- fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
- }
- keyID = key.ID
+ keys := strings.Split(c.Args()[0], "-")
+ if len(keys) != 2 {
+ fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
+ }
+ key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
+ if err != nil {
+ fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
+ }
+ keyID = key.ID
+
+ if requestedMode == models.AccessModeWrite || repo.IsPrivate {
// Check deploy key or user key.
if key.Type == models.KeyTypeDeploy {
if key.Mode < requestedMode {
@@ -282,8 +287,8 @@ func runServ(c *cli.Context) error {
clientMessage = "You do not have sufficient authorization for this action"
}
fail(clientMessage,
- "User %s does not have level %v access to repository %s",
- user.Name, requestedMode, repoPath)
+ "User '%s' does not have level '%v' access to repository '%s'",
+ user.Name, requestedMode, repoFullName)
}
os.Setenv("GITEA_PUSHER_NAME", user.Name)
@@ -291,10 +296,8 @@ func runServ(c *cli.Context) error {
}
//LFS token authentication
-
if verb == lfsAuthenticateVerb {
-
- url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, repoUser.Name, repo.Name)
+ url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, repoOwner.Name, repo.Name)
now := time.Now()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
@@ -326,36 +329,38 @@ func runServ(c *cli.Context) error {
}
uuid := gouuid.NewV4().String()
- os.Setenv("GITEA_UUID", uuid)
+
+ os.Setenv(envUpdateTaskUUID, uuid)
// Keep the old env variable name for backward compability
os.Setenv("uuid", uuid)
+ os.Setenv(envRepoCustomHooksPath, filepath.Join(repo.RepoPath(), "custom_hooks"))
// Special handle for Windows.
if setting.IsWindows {
verb = strings.Replace(verb, "-", " ", 1)
}
- var gitcmd *exec.Cmd
+ var gitCmd *exec.Cmd
verbs := strings.Split(verb, " ")
if len(verbs) == 2 {
- gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
+ gitCmd = exec.Command(verbs[0], verbs[1], repoFullName)
} else {
- gitcmd = exec.Command(verb, repoPath)
+ gitCmd = exec.Command(verb, repoFullName)
}
os.Setenv(models.ProtectedBranchAccessMode, requestedMode.String())
os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))
- gitcmd.Dir = setting.RepoRootPath
- gitcmd.Stdout = os.Stdout
- gitcmd.Stdin = os.Stdin
- gitcmd.Stderr = os.Stderr
- if err = gitcmd.Run(); err != nil {
+ gitCmd.Dir = setting.RepoRootPath
+ gitCmd.Stdout = os.Stdout
+ gitCmd.Stdin = os.Stdin
+ gitCmd.Stderr = os.Stderr
+ if err = gitCmd.Run(); err != nil {
fail("Internal error", "Failed to execute git command: %v", err)
}
if requestedMode == models.AccessModeWrite {
- handleUpdateTask(uuid, user, repoUser, reponame, isWiki)
+ handleUpdateTask(uuid, user, repoOwner, reponame, isWiki)
}
// Update user key activity.
diff --git a/main.go b/main.go
index d41fb4f6f805..b7e8244b4b29 100644
--- a/main.go
+++ b/main.go
@@ -30,7 +30,7 @@ func main() {
app.Commands = []cli.Command{
cmd.CmdWeb,
cmd.CmdServ,
- cmd.CmdUpdate,
+ cmd.CmdHook,
cmd.CmdDump,
cmd.CmdCert,
cmd.CmdAdmin,
diff --git a/models/action.go b/models/action.go
index c736fabf38b3..005875e29cea 100644
--- a/models/action.go
+++ b/models/action.go
@@ -506,7 +506,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
}
if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits); err != nil {
- log.Error(4, "updateIssuesCommit: %v", err)
+ log.Error(4, "UpdateIssuesCommit: %v", err)
}
}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 2a1c70af777e..5ae139cb3f5e 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -86,6 +86,8 @@ var migrations = []Migration{
NewMigration("set protect branches updated with created", setProtectedBranchUpdatedWithCreated),
// v18 -> v19
NewMigration("add external login user", addExternalLoginUser),
+ // v19 -> v20
+ NewMigration("generate and migrate Git hooks", generateAndMigrateGitHooks),
}
// Migrate database to current version
diff --git a/models/migrations/v19.go b/models/migrations/v19.go
new file mode 100644
index 000000000000..445b1e08e32f
--- /dev/null
+++ b/models/migrations/v19.go
@@ -0,0 +1,74 @@
+// 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 migrations
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/Unknwon/com"
+ "github.com/go-xorm/xorm"
+
+ "code.gitea.io/gitea/modules/setting"
+)
+
+func generateAndMigrateGitHooks(x *xorm.Engine) (err error) {
+ type Repository struct {
+ ID int64
+ OwnerID int64
+ Name string
+ }
+ type User struct {
+ ID int64
+ Name string
+ }
+ var (
+ hookNames = []string{"pre-receive", "update", "post-receive"}
+ hookTpls = []string{
+ fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
+ fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
+ fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
+ }
+ )
+
+ return x.Where("id > 0").Iterate(new(Repository),
+ func(idx int, bean interface{}) error {
+ repo := bean.(*Repository)
+ user := new(User)
+ has, err := x.Where("id = ?", repo.OwnerID).Get(user)
+ if err != nil {
+ return fmt.Errorf("query owner of repository [repo_id: %d, owner_id: %d]: %v", repo.ID, repo.OwnerID, err)
+ } else if !has {
+ return nil
+ }
+
+ repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git"
+ hookDir := filepath.Join(repoPath, "hooks")
+ customHookDir := filepath.Join(repoPath, "custom_hooks")
+
+ for i, hookName := range hookNames {
+ oldHookPath := filepath.Join(hookDir, hookName)
+ newHookPath := filepath.Join(customHookDir, hookName)
+
+ // Gogs didn't allow user to set custom update hook thus no migration for it.
+ // In case user runs this migration multiple times, and custom hook exists,
+ // we assume it's been migrated already.
+ if hookName != "update" && com.IsFile(oldHookPath) && !com.IsExist(newHookPath) {
+ os.MkdirAll(customHookDir, os.ModePerm)
+ if err = os.Rename(oldHookPath, newHookPath); err != nil {
+ return fmt.Errorf("move hook file to custom directory '%s' -> '%s': %v", oldHookPath, newHookPath, err)
+ }
+ }
+
+ if err = ioutil.WriteFile(oldHookPath, []byte(hookTpls[i]), 0777); err != nil {
+ return fmt.Errorf("write hook file '%s': %v", oldHookPath, err)
+ }
+ }
+ return nil
+ })
+}
diff --git a/models/repo.go b/models/repo.go
index af8f68833904..968a8e6d89b9 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -146,6 +146,7 @@ func NewRepoContext() {
if version.Compare("1.7.1", setting.Git.Version, ">") {
log.Fatal(4, "Gitea requires Git version greater or equal to 1.7.1")
}
+ git.HookDir = "custom_hooks"
// Git requires setting user.name and user.email in order to commit changes.
for configKey, defaultValue := range map[string]string{"user.name": "Gitea", "user.email": "gitea@fake.local"} {
@@ -831,20 +832,33 @@ func cleanUpMigrateGitConfig(configPath string) error {
return nil
}
-func createUpdateHook(repoPath string) error {
- return git.SetUpdateHook(repoPath,
- fmt.Sprintf(tplUpdateHook, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf))
+var hooksTpls = map[string]string{
+ "pre-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n",
+ "update": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n",
+ "post-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n",
+}
+
+func createDelegateHooks(repoPath string) (err error) {
+ for _, name := range git.HookNames {
+ hookPath := filepath.Join(repoPath, "hooks", name)
+ if err = ioutil.WriteFile(hookPath,
+ []byte(fmt.Sprintf(hooksTpls[name], setting.ScriptType, setting.AppPath, setting.CustomConf)),
+ os.ModePerm); err != nil {
+ return fmt.Errorf("create delegate hook '%s': %v", hookPath, err)
+ }
+ }
+ return nil
}
// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors.
func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
repoPath := repo.RepoPath()
- if err := createUpdateHook(repoPath); err != nil {
- return repo, fmt.Errorf("createUpdateHook: %v", err)
+ if err := createDelegateHooks(repoPath); err != nil {
+ return repo, fmt.Errorf("createDelegateHooks: %v", err)
}
if repo.HasWiki() {
- if err := createUpdateHook(repo.WikiPath()); err != nil {
- return repo, fmt.Errorf("createUpdateHook (wiki): %v", err)
+ if err := createDelegateHooks(repo.WikiPath()); err != nil {
+ return repo, fmt.Errorf("createDelegateHooks.(wiki): %v", err)
}
}
@@ -853,7 +867,7 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
}
if repo.HasWiki() {
if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil {
- return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err)
+ return repo, fmt.Errorf("cleanUpMigrateGitConfig.(wiki): %v", err)
}
}
@@ -994,8 +1008,8 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts C
// Init bare new repository.
if err = git.InitRepository(repoPath, true); err != nil {
return fmt.Errorf("InitRepository: %v", err)
- } else if err = createUpdateHook(repoPath); err != nil {
- return fmt.Errorf("createUpdateHook: %v", err)
+ } else if err = createDelegateHooks(repoPath); err != nil {
+ return fmt.Errorf("createDelegateHooks: %v", err)
}
tmpDir := filepath.Join(os.TempDir(), "gitea-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond()))
@@ -2009,15 +2023,13 @@ func ReinitMissingRepositories() error {
return nil
}
-// RewriteRepositoryUpdateHook rewrites all repositories' update hook.
-func RewriteRepositoryUpdateHook() error {
- return x.
- Where("id > 0").
- Iterate(new(Repository),
- func(idx int, bean interface{}) error {
- repo := bean.(*Repository)
- return createUpdateHook(repo.RepoPath())
- })
+// SyncRepositoryHooks rewrites all repositories' pre-receive, update and post-receive hooks
+// to make sure the binary and custom conf path are up-to-date.
+func SyncRepositoryHooks() error {
+ return x.Where("id > 0").Iterate(new(Repository),
+ func(idx int, bean interface{}) error {
+ return createDelegateHooks(bean.(*Repository).RepoPath())
+ })
}
// Prevent duplicate running tasks.
@@ -2345,8 +2357,8 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
return nil, fmt.Errorf("git update-server-info: %v", stderr)
}
- if err = createUpdateHook(repoPath); err != nil {
- return nil, fmt.Errorf("createUpdateHook: %v", err)
+ if err = createDelegateHooks(repoPath); err != nil {
+ return nil, fmt.Errorf("createDelegateHooks: %v", err)
}
//Commit repo to get Fork ID
diff --git a/models/wiki.go b/models/wiki.go
index 39e4b2a44341..690da707c657 100644
--- a/models/wiki.go
+++ b/models/wiki.go
@@ -69,8 +69,8 @@ func (repo *Repository) InitWiki() error {
if err := git.InitRepository(repo.WikiPath(), true); err != nil {
return fmt.Errorf("InitRepository: %v", err)
- } else if err = createUpdateHook(repo.WikiPath()); err != nil {
- return fmt.Errorf("createUpdateHook: %v", err)
+ } else if err = createDelegateHooks(repo.WikiPath()); err != nil {
+ return fmt.Errorf("createDelegateHooks: %v", err)
}
return nil
}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 661a44caf56a..9043ff12b6ca 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1003,8 +1003,8 @@ dashboard.git_gc_repos = Do garbage collection on repositories
dashboard.git_gc_repos_success = All repositories have done garbage collection successfully.
dashboard.resync_all_sshkeys = Rewrite '.ssh/authorized_keys' file (caution: non-Gitea keys will be lost)
dashboard.resync_all_sshkeys_success = All public keys have been rewritten successfully.
-dashboard.resync_all_update_hooks = Rewrite all update hook of repositories (needed when custom config path is changed)
-dashboard.resync_all_update_hooks_success = All repositories' update hook have been rewritten successfully.
+dashboard.resync_all_hooks = Resync pre-receive, update and post-receive hooks of all repositories.
+dashboard.resync_all_hooks_success = All repositories' pre-receive, update and post-receive hooks have been resynced successfully.
dashboard.reinit_missing_repos = Reinitialize all repository records that lost Git files
dashboard.reinit_missing_repos_success = All repository records that lost Git files have been reinitialized successfully.
diff --git a/routers/admin/admin.go b/routers/admin/admin.go
index 7158a4ee91cb..6b5b33f734d9 100644
--- a/routers/admin/admin.go
+++ b/routers/admin/admin.go
@@ -152,8 +152,8 @@ func Dashboard(ctx *context.Context) {
success = ctx.Tr("admin.dashboard.resync_all_sshkeys_success")
err = models.RewriteAllPublicKeys()
case syncRepositoryUpdateHook:
- success = ctx.Tr("admin.dashboard.resync_all_update_hooks_success")
- err = models.RewriteRepositoryUpdateHook()
+ success = ctx.Tr("admin.dashboard.resync_all_hooks_success")
+ err = models.SyncRepositoryHooks()
case reinitMissingRepository:
success = ctx.Tr("admin.dashboard.reinit_missing_repos_success")
err = models.ReinitMissingRepositories()
diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl
index af4563a02d54..acffdc43140d 100644
--- a/templates/admin/dashboard.tmpl
+++ b/templates/admin/dashboard.tmpl
@@ -40,7 +40,7 @@
{{.i18n.Tr "admin.dashboard.operation_run"}} |
- {{.i18n.Tr "admin.dashboard.resync_all_update_hooks"}} |
+ {{.i18n.Tr "admin.dashboard.resync_all_hooks"}} |
{{.i18n.Tr "admin.dashboard.operation_run"}} |
diff --git a/vendor/code.gitea.io/git/hook.go b/vendor/code.gitea.io/git/hook.go
index d8069b6b828c..d0a8ec2b394f 100644
--- a/vendor/code.gitea.io/git/hook.go
+++ b/vendor/code.gitea.io/git/hook.go
@@ -14,12 +14,16 @@ import (
"github.com/Unknwon/com"
)
-// hookNames is a list of Git server hooks' name that are supported.
-var hookNames = []string{
- "pre-receive",
- // "update",
- "post-receive",
-}
+var (
+ // Direcotry of hook file. Can be changed to "custom_hooks" for very purpose.
+ HookDir = "hooks"
+ // HookNames is a list of Git server hooks' name that are supported.
+ HookNames = []string{
+ "pre-receive",
+ "update",
+ "post-receive",
+ }
+)
var (
// ErrNotValidHook error when a git hook is not valid
@@ -28,7 +32,7 @@ var (
// IsValidHookName returns true if given name is a valid Git hook.
func IsValidHookName(name string) bool {
- for _, hn := range hookNames {
+ for _, hn := range HookNames {
if hn == name {
return true
}
@@ -52,7 +56,7 @@ func GetHook(repoPath, name string) (*Hook, error) {
}
h := &Hook{
name: name,
- path: path.Join(repoPath, "hooks", name),
+ path: path.Join(repoPath, HookDir, name),
}
if isFile(h.path) {
data, err := ioutil.ReadFile(h.path)
@@ -76,7 +80,7 @@ func (h *Hook) Name() string {
return h.name
}
-// Update updates hook settings.
+// Update updates content hook file.
func (h *Hook) Update() error {
if len(strings.TrimSpace(h.Content)) == 0 {
if isExist(h.path) {
@@ -93,8 +97,8 @@ func ListHooks(repoPath string) (_ []*Hook, err error) {
return nil, errors.New("hooks path does not exist")
}
- hooks := make([]*Hook, len(hookNames))
- for i, name := range hookNames {
+ hooks := make([]*Hook, len(HookNames))
+ for i, name := range HookNames {
hooks[i], err = GetHook(repoPath, name)
if err != nil {
return nil, err
From 24be2d7cf12f33c116323bf3398c0cf272f63936 Mon Sep 17 00:00:00 2001
From: Lunny Xiao
Date: Thu, 16 Feb 2017 22:44:55 +0800
Subject: [PATCH 2/7] create hooks directories
---
cmd/hook.go | 35 -----------------------------------
cmd/serv.go | 14 +++++++++-----
2 files changed, 9 insertions(+), 40 deletions(-)
diff --git a/cmd/hook.go b/cmd/hook.go
index 7b627bd76a42..810a196f48e1 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -5,12 +5,8 @@
package cmd
import (
- "bufio"
- "bytes"
"fmt"
"os"
- "os/exec"
- "path/filepath"
"code.gitea.io/gitea/models"
@@ -64,21 +60,6 @@ func runHookPreReceive(c *cli.Context) error {
fail("Hook pre-receive init failed", fmt.Sprintf("setup: %v", err))
}
- buf := bytes.NewBuffer(nil)
- scanner := bufio.NewScanner(os.Stdin)
- for scanner.Scan() {
- buf.Write(scanner.Bytes())
- buf.WriteByte('\n')
- }
-
- customHooksPath := os.Getenv(envRepoCustomHooksPath)
- hookCmd := exec.Command(filepath.Join(customHooksPath, "pre-receive"))
- hookCmd.Stdout = os.Stdout
- hookCmd.Stdin = buf
- hookCmd.Stderr = os.Stderr
- if err := hookCmd.Run(); err != nil {
- fail("Internal error", "Fail to execute custom pre-receive hook: %v", err)
- }
return nil
}
@@ -108,14 +89,6 @@ func runHookUpdate(c *cli.Context) error {
fail("Internal error", "Fail to add update task '%s': %v", uuid, err)
}
- customHooksPath := os.Getenv(envRepoCustomHooksPath)
- hookCmd := exec.Command(filepath.Join(customHooksPath, "update"), args...)
- hookCmd.Stdout = os.Stdout
- hookCmd.Stdin = os.Stdin
- hookCmd.Stderr = os.Stderr
- if err := hookCmd.Run(); err != nil {
- fail("Internal error", "Fail to execute custom pre-receive hook: %v", err)
- }
return nil
}
@@ -128,13 +101,5 @@ func runHookPostReceive(c *cli.Context) error {
fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err))
}
- customHooksPath := os.Getenv(envRepoCustomHooksPath)
- hookCmd := exec.Command(filepath.Join(customHooksPath, "post-receive"))
- hookCmd.Stdout = os.Stdout
- hookCmd.Stdin = os.Stdin
- hookCmd.Stderr = os.Stderr
- if err := hookCmd.Run(); err != nil {
- fail("Internal error", "Fail to execute custom post-receive hook: %v", err)
- }
return nil
}
diff --git a/cmd/serv.go b/cmd/serv.go
index 80864ad520da..930184cfb4cd 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -28,10 +28,12 @@ import (
)
const (
- accessDenied = "Repository does not exist or you do not have access"
- lfsAuthenticateVerb = "git-lfs-authenticate"
- envUpdateTaskUUID = "GITEA_UUID"
- envRepoCustomHooksPath = "REPO_CUSTOM_HOOKS_PATH"
+ accessDenied = "Repository does not exist or you do not have access"
+ lfsAuthenticateVerb = "git-lfs-authenticate"
+ envUpdateTaskUUID = "GITEA_UUID"
+ envRepoUpdateHooksPath = "GITEA_REPO_UPDATE_HOOKS_PATH"
+ envRepoPreReceiveHooksPath = "GITEA_REPO_PRERECEIVE_HOOKS_PATH"
+ envRepoPostReceiveHooksPath = "GITEA_REPO_POSTRECEIVE_HOOKS_PATH"
)
// CmdServ represents the available serv sub-command.
@@ -333,7 +335,9 @@ func runServ(c *cli.Context) error {
os.Setenv(envUpdateTaskUUID, uuid)
// Keep the old env variable name for backward compability
os.Setenv("uuid", uuid)
- os.Setenv(envRepoCustomHooksPath, filepath.Join(repo.RepoPath(), "custom_hooks"))
+ os.Setenv(envRepoUpdateHooksPath, filepath.Join(repo.RepoPath(), "hooks", "update.d"))
+ os.Setenv(envRepoPreReceiveHooksPath, filepath.Join(repo.RepoPath(), "hooks", "pre-receive.d"))
+ os.Setenv(envRepoPostReceiveHooksPath, filepath.Join(repo.RepoPath(), "hooks", "post-receive.d"))
// Special handle for Windows.
if setting.IsWindows {
From 6a98cc03a4237c978058343b87af2937310f0843 Mon Sep 17 00:00:00 2001
From: Lunny Xiao
Date: Wed, 22 Feb 2017 11:18:16 +0800
Subject: [PATCH 3/7] take control hooks back
---
cmd/hook.go | 10 +--
cmd/serv.go | 93 ++++++++++++--------------
models/action.go | 2 +-
models/migrations/v19.go | 31 ++++++---
models/repo.go | 53 ++++++++++-----
vendor/code.gitea.io/git/hook.go | 32 +++++----
vendor/code.gitea.io/git/repo_blame.go | 10 +++
vendor/vendor.json | 6 +-
8 files changed, 134 insertions(+), 103 deletions(-)
create mode 100644 vendor/code.gitea.io/git/repo_blame.go
diff --git a/cmd/hook.go b/cmd/hook.go
index 810a196f48e1..b6c7a9afa34b 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -1,4 +1,4 @@
-// Copyright 2014 The Gogs Authors. All rights reserved.
+// 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.
@@ -8,9 +8,9 @@ import (
"fmt"
"os"
- "code.gitea.io/gitea/models"
-
"github.com/urfave/cli"
+
+ "code.gitea.io/gitea/models"
)
var (
@@ -68,7 +68,7 @@ func runHookUpdate(c *cli.Context) error {
return nil
}
- if err := setup("hooks/pre-receive.log"); err != nil {
+ if err := setup("hooks/update.log"); err != nil {
fail("Hook update init failed", fmt.Sprintf("setup: %v", err))
}
@@ -97,7 +97,7 @@ func runHookPostReceive(c *cli.Context) error {
return nil
}
- if err := setup("hooks/pre-receive.log"); err != nil {
+ if err := setup("hooks/post-receive.log"); err != nil {
fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err))
}
diff --git a/cmd/serv.go b/cmd/serv.go
index 930184cfb4cd..141a58a2acf5 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -28,12 +28,9 @@ import (
)
const (
- accessDenied = "Repository does not exist or you do not have access"
- lfsAuthenticateVerb = "git-lfs-authenticate"
- envUpdateTaskUUID = "GITEA_UUID"
- envRepoUpdateHooksPath = "GITEA_REPO_UPDATE_HOOKS_PATH"
- envRepoPreReceiveHooksPath = "GITEA_REPO_PRERECEIVE_HOOKS_PATH"
- envRepoPostReceiveHooksPath = "GITEA_REPO_POSTRECEIVE_HOOKS_PATH"
+ accessDenied = "Repository does not exist or you do not have access"
+ lfsAuthenticateVerb = "git-lfs-authenticate"
+ envUpdateTaskUUID = "GITEA_UUID"
)
// CmdServ represents the available serv sub-command.
@@ -56,7 +53,7 @@ func setup(logPath string) error {
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
models.LoadConfigs()
- if setting.UseSQLite3 {
+ if setting.UseSQLite3 || setting.UseTiDB {
workDir, _ := setting.WorkDir()
if err := os.Chdir(workDir); err != nil {
log.GitLogger.Fatal(4, "Failed to change directory %s: %v", workDir, err)
@@ -67,7 +64,7 @@ func setup(logPath string) error {
return models.SetEngine()
}
-func parseSSHCmd(cmd string) (string, string) {
+func parseCmd(cmd string) (string, string) {
ss := strings.SplitN(cmd, " ", 2)
if len(ss) != 2 {
return "", ""
@@ -148,8 +145,6 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
func runServ(c *cli.Context) error {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
- } else if c.GlobalIsSet("config") {
- setting.CustomConf = c.GlobalString("config")
}
if err := setup("serv.log"); err != nil {
@@ -172,7 +167,7 @@ func runServ(c *cli.Context) error {
return nil
}
- verb, args := parseSSHCmd(cmd)
+ verb, args := parseCmd(cmd)
var lfsVerb string
if verb == lfsAuthenticateVerb {
@@ -187,13 +182,13 @@ func runServ(c *cli.Context) error {
}
}
- repoFullName := strings.ToLower(strings.Trim(args, "'"))
- repoFields := strings.SplitN(repoFullName, "/", 2)
- if len(repoFields) != 2 {
+ repoPath := strings.ToLower(strings.Trim(args, "'"))
+ rr := strings.SplitN(repoPath, "/", 2)
+ if len(rr) != 2 {
fail("Invalid repository path", "Invalid repository path: %v", args)
}
- username := strings.ToLower(repoFields[0])
- reponame := strings.ToLower(strings.TrimSuffix(repoFields[1], ".git"))
+ username := strings.ToLower(rr[0])
+ reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
isWiki := false
if strings.HasSuffix(reponame, ".wiki") {
@@ -201,26 +196,25 @@ func runServ(c *cli.Context) error {
reponame = reponame[:len(reponame)-5]
}
- repoOwner, err := models.GetUserByName(username)
+ repoUser, err := models.GetUserByName(username)
if err != nil {
if models.IsErrUserNotExist(err) {
fail("Repository owner does not exist", "Unregistered owner: %s", username)
}
- fail("Internal error", "Fail to get repository owner '%s': %v", username, err)
+ fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
}
- repo, err := models.GetRepositoryByName(repoOwner.ID, reponame)
+ repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
if err != nil {
if models.IsErrRepoNotExist(err) {
- fail(accessDenied, "Repository does not exist: %s/%s", repoOwner.Name, reponame)
+ fail(accessDenied, "Repository does not exist: %s/%s", repoUser.Name, reponame)
}
- fail("Internal error", "Fail to get repository: %v", err)
+ fail("Internal error", "Failed to get repository: %v", err)
}
- repo.Owner = repoOwner
- requestedMode, ok := allowedCommands[verb]
- if !ok {
- fail("Unknown git command", "Unknown git command '%s'", verb)
+ requestedMode, has := allowedCommands[verb]
+ if !has {
+ fail("Unknown git command", "Unknown git command %s", verb)
}
if verb == lfsAuthenticateVerb {
@@ -241,19 +235,18 @@ func runServ(c *cli.Context) error {
keyID int64
user *models.User
)
+ if requestedMode == models.AccessModeWrite || repo.IsPrivate {
+ keys := strings.Split(c.Args()[0], "-")
+ if len(keys) != 2 {
+ fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
+ }
- keys := strings.Split(c.Args()[0], "-")
- if len(keys) != 2 {
- fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
- }
-
- key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
- if err != nil {
- fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
- }
- keyID = key.ID
+ key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
+ if err != nil {
+ fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
+ }
+ keyID = key.ID
- if requestedMode == models.AccessModeWrite || repo.IsPrivate {
// Check deploy key or user key.
if key.Type == models.KeyTypeDeploy {
if key.Mode < requestedMode {
@@ -289,8 +282,8 @@ func runServ(c *cli.Context) error {
clientMessage = "You do not have sufficient authorization for this action"
}
fail(clientMessage,
- "User '%s' does not have level '%v' access to repository '%s'",
- user.Name, requestedMode, repoFullName)
+ "User %s does not have level %v access to repository %s",
+ user.Name, requestedMode, repoPath)
}
os.Setenv("GITEA_PUSHER_NAME", user.Name)
@@ -299,7 +292,7 @@ func runServ(c *cli.Context) error {
//LFS token authentication
if verb == lfsAuthenticateVerb {
- url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, repoOwner.Name, repo.Name)
+ url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, repoUser.Name, repo.Name)
now := time.Now()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
@@ -331,40 +324,36 @@ func runServ(c *cli.Context) error {
}
uuid := gouuid.NewV4().String()
-
os.Setenv(envUpdateTaskUUID, uuid)
// Keep the old env variable name for backward compability
os.Setenv("uuid", uuid)
- os.Setenv(envRepoUpdateHooksPath, filepath.Join(repo.RepoPath(), "hooks", "update.d"))
- os.Setenv(envRepoPreReceiveHooksPath, filepath.Join(repo.RepoPath(), "hooks", "pre-receive.d"))
- os.Setenv(envRepoPostReceiveHooksPath, filepath.Join(repo.RepoPath(), "hooks", "post-receive.d"))
// Special handle for Windows.
if setting.IsWindows {
verb = strings.Replace(verb, "-", " ", 1)
}
- var gitCmd *exec.Cmd
+ var gitcmd *exec.Cmd
verbs := strings.Split(verb, " ")
if len(verbs) == 2 {
- gitCmd = exec.Command(verbs[0], verbs[1], repoFullName)
+ gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
} else {
- gitCmd = exec.Command(verb, repoFullName)
+ gitcmd = exec.Command(verb, repoPath)
}
os.Setenv(models.ProtectedBranchAccessMode, requestedMode.String())
os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))
- gitCmd.Dir = setting.RepoRootPath
- gitCmd.Stdout = os.Stdout
- gitCmd.Stdin = os.Stdin
- gitCmd.Stderr = os.Stderr
- if err = gitCmd.Run(); err != nil {
+ gitcmd.Dir = setting.RepoRootPath
+ gitcmd.Stdout = os.Stdout
+ gitcmd.Stdin = os.Stdin
+ gitcmd.Stderr = os.Stderr
+ if err = gitcmd.Run(); err != nil {
fail("Internal error", "Failed to execute git command: %v", err)
}
if requestedMode == models.AccessModeWrite {
- handleUpdateTask(uuid, user, repoOwner, reponame, isWiki)
+ handleUpdateTask(uuid, user, repoUser, reponame, isWiki)
}
// Update user key activity.
diff --git a/models/action.go b/models/action.go
index 005875e29cea..c736fabf38b3 100644
--- a/models/action.go
+++ b/models/action.go
@@ -506,7 +506,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
}
if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits); err != nil {
- log.Error(4, "UpdateIssuesCommit: %v", err)
+ log.Error(4, "updateIssuesCommit: %v", err)
}
}
diff --git a/models/migrations/v19.go b/models/migrations/v19.go
index 445b1e08e32f..558b01b48027 100644
--- a/models/migrations/v19.go
+++ b/models/migrations/v19.go
@@ -27,9 +27,15 @@ func generateAndMigrateGitHooks(x *xorm.Engine) (err error) {
ID int64
Name string
}
+
var (
hookNames = []string{"pre-receive", "update", "post-receive"}
hookTpls = []string{
+ "cd ./pre-receive.d\nfor i in `ls`; do\n sh $i\ndone",
+ "cd ./update.d\nfor i in `ls`; do\n sh $i $1 $2 $3\ndone",
+ "cd ./post-receive.d\nfor i in `ls`; do\n sh $i\ndone",
+ }
+ giteaHookTpls = []string{
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
@@ -49,24 +55,29 @@ func generateAndMigrateGitHooks(x *xorm.Engine) (err error) {
repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git"
hookDir := filepath.Join(repoPath, "hooks")
- customHookDir := filepath.Join(repoPath, "custom_hooks")
for i, hookName := range hookNames {
oldHookPath := filepath.Join(hookDir, hookName)
- newHookPath := filepath.Join(customHookDir, hookName)
+ newHookPath := filepath.Join(hookDir, hookName+".d", "gitea")
+
+ if err = os.MkdirAll(filepath.Join(hookDir, hookName+".d"), os.ModePerm); err != nil {
+ return fmt.Errorf("create hooks dir '%s': %v", filepath.Join(hookDir, hookName+".d"), err)
+ }
- // Gogs didn't allow user to set custom update hook thus no migration for it.
- // In case user runs this migration multiple times, and custom hook exists,
- // we assume it's been migrated already.
- if hookName != "update" && com.IsFile(oldHookPath) && !com.IsExist(newHookPath) {
- os.MkdirAll(customHookDir, os.ModePerm)
- if err = os.Rename(oldHookPath, newHookPath); err != nil {
- return fmt.Errorf("move hook file to custom directory '%s' -> '%s': %v", oldHookPath, newHookPath, err)
+ // WARNING: Old server-side hooks will be moved to sub directory with the same name
+ if hookName != "update" && com.IsExist(oldHookPath) {
+ newPlace := filepath.Join(hookDir, hookName+".d", hookName)
+ if err = os.Rename(oldHookPath, newPlace); err != nil {
+ return fmt.Errorf("Remove old hook file '%s' to '%s': %v", oldHookPath, newPlace, err)
}
}
if err = ioutil.WriteFile(oldHookPath, []byte(hookTpls[i]), 0777); err != nil {
- return fmt.Errorf("write hook file '%s': %v", oldHookPath, err)
+ return fmt.Errorf("write old hook file '%s': %v", oldHookPath, err)
+ }
+
+ if err = ioutil.WriteFile(newHookPath, []byte(giteaHookTpls[i]), 0777); err != nil {
+ return fmt.Errorf("write new hook file '%s': %v", oldHookPath, err)
}
}
return nil
diff --git a/models/repo.go b/models/repo.go
index 968a8e6d89b9..6237f0c8ea3a 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -146,7 +146,6 @@ func NewRepoContext() {
if version.Compare("1.7.1", setting.Git.Version, ">") {
log.Fatal(4, "Gitea requires Git version greater or equal to 1.7.1")
}
- git.HookDir = "custom_hooks"
// Git requires setting user.name and user.email in order to commit changes.
for configKey, defaultValue := range map[string]string{"user.name": "Gitea", "user.email": "gitea@fake.local"} {
@@ -832,21 +831,42 @@ func cleanUpMigrateGitConfig(configPath string) error {
return nil
}
-var hooksTpls = map[string]string{
- "pre-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n",
- "update": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n",
- "post-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n",
-}
-
+// createDelegateHooks creates all the hooks scripts for the repo
func createDelegateHooks(repoPath string) (err error) {
- for _, name := range git.HookNames {
- hookPath := filepath.Join(repoPath, "hooks", name)
- if err = ioutil.WriteFile(hookPath,
- []byte(fmt.Sprintf(hooksTpls[name], setting.ScriptType, setting.AppPath, setting.CustomConf)),
- os.ModePerm); err != nil {
- return fmt.Errorf("create delegate hook '%s': %v", hookPath, err)
+ var (
+ hookNames = []string{"pre-receive", "update", "post-receive"}
+ hookTpls = []string{
+ "cd ./pre-receive.d\nfor i in `ls`; do\n sh $i\ndone",
+ "cd ./update.d\nfor i in `ls`; do\n sh $i $1 $2 $3\ndone",
+ "cd ./post-receive.d\nfor i in `ls`; do\n sh $i\ndone",
+ }
+ giteaHookTpls = []string{
+ fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
+ fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
+ fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
+ }
+ )
+
+ hookDir := filepath.Join(repoPath, "hooks")
+
+ for i, hookName := range hookNames {
+ oldHookPath := filepath.Join(hookDir, hookName)
+ newHookPath := filepath.Join(hookDir, hookName+".d", "gitea")
+
+ if err := os.MkdirAll(filepath.Join(hookDir, hookName+".d"), os.ModePerm); err != nil {
+ return fmt.Errorf("create hooks dir '%s': %v", filepath.Join(hookDir, hookName+".d"), err)
+ }
+
+ // WARNING: This will override all old server-side hooks
+ if err = ioutil.WriteFile(oldHookPath, []byte(hookTpls[i]), 0777); err != nil {
+ return fmt.Errorf("write old hook file '%s': %v", oldHookPath, err)
+ }
+
+ if err = ioutil.WriteFile(newHookPath, []byte(giteaHookTpls[i]), 0777); err != nil {
+ return fmt.Errorf("write new hook file '%s': %v", newHookPath, err)
}
}
+
return nil
}
@@ -867,7 +887,7 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
}
if repo.HasWiki() {
if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil {
- return repo, fmt.Errorf("cleanUpMigrateGitConfig.(wiki): %v", err)
+ return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err)
}
}
@@ -2028,7 +2048,10 @@ func ReinitMissingRepositories() error {
func SyncRepositoryHooks() error {
return x.Where("id > 0").Iterate(new(Repository),
func(idx int, bean interface{}) error {
- return createDelegateHooks(bean.(*Repository).RepoPath())
+ if err := createDelegateHooks(bean.(*Repository).RepoPath()); err != nil {
+ return err
+ }
+ return nil
})
}
diff --git a/vendor/code.gitea.io/git/hook.go b/vendor/code.gitea.io/git/hook.go
index d0a8ec2b394f..afed623e6eac 100644
--- a/vendor/code.gitea.io/git/hook.go
+++ b/vendor/code.gitea.io/git/hook.go
@@ -9,21 +9,18 @@ import (
"io/ioutil"
"os"
"path"
+ "path/filepath"
"strings"
"github.com/Unknwon/com"
)
-var (
- // Direcotry of hook file. Can be changed to "custom_hooks" for very purpose.
- HookDir = "hooks"
- // HookNames is a list of Git server hooks' name that are supported.
- HookNames = []string{
- "pre-receive",
- "update",
- "post-receive",
- }
-)
+// hookNames is a list of Git server hooks' name that are supported.
+var hookNames = []string{
+ "pre-receive",
+ "update",
+ "post-receive",
+}
var (
// ErrNotValidHook error when a git hook is not valid
@@ -32,7 +29,7 @@ var (
// IsValidHookName returns true if given name is a valid Git hook.
func IsValidHookName(name string) bool {
- for _, hn := range HookNames {
+ for _, hn := range hookNames {
if hn == name {
return true
}
@@ -56,8 +53,9 @@ func GetHook(repoPath, name string) (*Hook, error) {
}
h := &Hook{
name: name,
- path: path.Join(repoPath, HookDir, name),
+ path: path.Join(repoPath, "hooks", name+".d", name),
}
+ samplePath := filepath.Join(repoPath, "hooks", name+".sample")
if isFile(h.path) {
data, err := ioutil.ReadFile(h.path)
if err != nil {
@@ -65,8 +63,8 @@ func GetHook(repoPath, name string) (*Hook, error) {
}
h.IsActive = true
h.Content = string(data)
- } else if isFile(h.path + ".sample") {
- data, err := ioutil.ReadFile(h.path + ".sample")
+ } else if isFile(samplePath) {
+ data, err := ioutil.ReadFile(samplePath)
if err != nil {
return nil, err
}
@@ -80,7 +78,7 @@ func (h *Hook) Name() string {
return h.name
}
-// Update updates content hook file.
+// Update updates hook settings.
func (h *Hook) Update() error {
if len(strings.TrimSpace(h.Content)) == 0 {
if isExist(h.path) {
@@ -97,8 +95,8 @@ func ListHooks(repoPath string) (_ []*Hook, err error) {
return nil, errors.New("hooks path does not exist")
}
- hooks := make([]*Hook, len(HookNames))
- for i, name := range HookNames {
+ hooks := make([]*Hook, len(hookNames))
+ for i, name := range hookNames {
hooks[i], err = GetHook(repoPath, name)
if err != nil {
return nil, err
diff --git a/vendor/code.gitea.io/git/repo_blame.go b/vendor/code.gitea.io/git/repo_blame.go
new file mode 100644
index 000000000000..b48cbeea6c2d
--- /dev/null
+++ b/vendor/code.gitea.io/git/repo_blame.go
@@ -0,0 +1,10 @@
+// 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 git
+
+// FileBlame return the Blame object of file
+func (repo *Repository) FileBlame(revision, path, file string) ([]byte, error) {
+ return NewCommand("blame", "--root", file).RunInDirBytes(path)
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 49dcec8c4521..3a4ebc57809d 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -3,10 +3,10 @@
"ignore": "test",
"package": [
{
- "checksumSHA1": "km1AOUs34DCwgXT55fh6PrkPdiU=",
+ "checksumSHA1": "nt2y/SNJe3Rl0tzdaEyGQfCc4L4=",
"path": "code.gitea.io/git",
- "revision": "dd951bf625ebf5c16ef403f681aaec6c34324bca",
- "revisionTime": "2017-02-05T02:50:57Z"
+ "revision": "b4c06a53d0f619e84a99eb042184663d4ad8a32b",
+ "revisionTime": "2017-02-22T02:52:05Z"
},
{
"checksumSHA1": "BKj0haFTDebzdC2nACpoGzp3s8A=",
From a3ae5402fdee687b81933264f877e6b58c6563f6 Mon Sep 17 00:00:00 2001
From: Lunny Xiao
Date: Wed, 22 Feb 2017 11:29:03 +0800
Subject: [PATCH 4/7] fix lint
---
cmd/hook.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/cmd/hook.go b/cmd/hook.go
index b6c7a9afa34b..1449a3d08b17 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -14,6 +14,7 @@ import (
)
var (
+ // CmdHook represents the available hooks sub-command.
CmdHook = cli.Command{
Name: "hook",
Usage: "Delegate commands to corresponding Git hooks",
From a62eb0fa799ce7b07ab3c154512b24bcb92748a6 Mon Sep 17 00:00:00 2001
From: Lunny Xiao
Date: Wed, 22 Feb 2017 13:12:16 +0800
Subject: [PATCH 5/7] bug fixed and minor changes
---
cmd/hook.go | 2 +-
models/migrations/v18.go | 4 ++--
models/repo.go | 8 ++++----
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/cmd/hook.go b/cmd/hook.go
index 1449a3d08b17..47c17da9ef3d 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -18,7 +18,7 @@ var (
CmdHook = cli.Command{
Name: "hook",
Usage: "Delegate commands to corresponding Git hooks",
- Description: "All sub-commands should only be called by Git",
+ Description: "This should only be called by Git",
Flags: []cli.Flag{
cli.StringFlag{
Name: "config, c",
diff --git a/models/migrations/v18.go b/models/migrations/v18.go
index be5161534194..3b3cd23ccfe0 100644
--- a/models/migrations/v18.go
+++ b/models/migrations/v18.go
@@ -13,8 +13,8 @@ import (
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
type ExternalLoginUser struct {
ExternalID string `xorm:"NOT NULL"`
- UserID int64 `xorm:"NOT NULL"`
- LoginSourceID int64 `xorm:"NOT NULL"`
+ UserID int64 `xorm:"NOT NULL"`
+ LoginSourceID int64 `xorm:"NOT NULL"`
}
func addExternalLoginUser(x *xorm.Engine) error {
diff --git a/models/repo.go b/models/repo.go
index 6237f0c8ea3a..e276a87ae6cc 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -836,9 +836,9 @@ func createDelegateHooks(repoPath string) (err error) {
var (
hookNames = []string{"pre-receive", "update", "post-receive"}
hookTpls = []string{
- "cd ./pre-receive.d\nfor i in `ls`; do\n sh $i\ndone",
- "cd ./update.d\nfor i in `ls`; do\n sh $i $1 $2 $3\ndone",
- "cd ./post-receive.d\nfor i in `ls`; do\n sh $i\ndone",
+ fmt.Sprintf("#!/usr/bin/env %s\nORI_DIR=`pwd`\nSHELL_FOLDER=$(cd \"$(dirname \"$0\")\";pwd)\ncd \"$ORI_DIR\"\nfor i in `ls \"$SHELL_FOLDER/pre-receive.d\"`; do\n sh \"$SHELL_FOLDER/pre-receive.d/$i\"\ndone", setting.ScriptType),
+ fmt.Sprintf("#!/usr/bin/env %s\nORI_DIR=`pwd`\nSHELL_FOLDER=$(cd \"$(dirname \"$0\")\";pwd)\ncd \"$ORI_DIR\"\nfor i in `ls \"$SHELL_FOLDER/update.d\"`; do\n sh \"$SHELL_FOLDER/update.d/$i\" $1 $2 $3\ndone", setting.ScriptType),
+ fmt.Sprintf("#!/usr/bin/env %s\nORI_DIR=`pwd`\nSHELL_FOLDER=$(cd \"$(dirname \"$0\")\";pwd)\ncd \"$ORI_DIR\"\nfor i in `ls \"$SHELL_FOLDER/post-receive.d\"`; do\n sh \"$SHELL_FOLDER/post-receive.d/$i\"\ndone", setting.ScriptType),
}
giteaHookTpls = []string{
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
@@ -2049,7 +2049,7 @@ func SyncRepositoryHooks() error {
return x.Where("id > 0").Iterate(new(Repository),
func(idx int, bean interface{}) error {
if err := createDelegateHooks(bean.(*Repository).RepoPath()); err != nil {
- return err
+ return fmt.Errorf("SyncRepositoryHook: %v", err)
}
return nil
})
From 9a9f14db1a91dadb8b6b4663b8652c53fa656f56 Mon Sep 17 00:00:00 2001
From: Lunny Xiao
Date: Wed, 22 Feb 2017 22:52:22 +0800
Subject: [PATCH 6/7] fix imports style
---
cmd/hook.go | 4 ++--
models/migrations/v19.go | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/cmd/hook.go b/cmd/hook.go
index 47c17da9ef3d..15ad74f8e0bd 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -8,9 +8,9 @@ import (
"fmt"
"os"
- "github.com/urfave/cli"
-
"code.gitea.io/gitea/models"
+
+ "github.com/urfave/cli"
)
var (
diff --git a/models/migrations/v19.go b/models/migrations/v19.go
index 558b01b48027..f6d3cde62d17 100644
--- a/models/migrations/v19.go
+++ b/models/migrations/v19.go
@@ -11,10 +11,10 @@ import (
"path/filepath"
"strings"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
-
- "code.gitea.io/gitea/modules/setting"
)
func generateAndMigrateGitHooks(x *xorm.Engine) (err error) {
From 2c338158d038e4f6fcb30fe78e4fbca93d079f65 Mon Sep 17 00:00:00 2001
From: Lunny Xiao
Date: Thu, 23 Feb 2017 10:15:26 +0800
Subject: [PATCH 7/7] fix migration scripts
---
models/migrations/v19.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/models/migrations/v19.go b/models/migrations/v19.go
index f6d3cde62d17..6e0dbedaa3f1 100644
--- a/models/migrations/v19.go
+++ b/models/migrations/v19.go
@@ -31,9 +31,9 @@ func generateAndMigrateGitHooks(x *xorm.Engine) (err error) {
var (
hookNames = []string{"pre-receive", "update", "post-receive"}
hookTpls = []string{
- "cd ./pre-receive.d\nfor i in `ls`; do\n sh $i\ndone",
- "cd ./update.d\nfor i in `ls`; do\n sh $i $1 $2 $3\ndone",
- "cd ./post-receive.d\nfor i in `ls`; do\n sh $i\ndone",
+ fmt.Sprintf("#!/usr/bin/env %s\nORI_DIR=`pwd`\nSHELL_FOLDER=$(cd \"$(dirname \"$0\")\";pwd)\ncd \"$ORI_DIR\"\nfor i in `ls \"$SHELL_FOLDER/pre-receive.d\"`; do\n sh \"$SHELL_FOLDER/pre-receive.d/$i\"\ndone", setting.ScriptType),
+ fmt.Sprintf("#!/usr/bin/env %s\nORI_DIR=`pwd`\nSHELL_FOLDER=$(cd \"$(dirname \"$0\")\";pwd)\ncd \"$ORI_DIR\"\nfor i in `ls \"$SHELL_FOLDER/update.d\"`; do\n sh \"$SHELL_FOLDER/update.d/$i\" $1 $2 $3\ndone", setting.ScriptType),
+ fmt.Sprintf("#!/usr/bin/env %s\nORI_DIR=`pwd`\nSHELL_FOLDER=$(cd \"$(dirname \"$0\")\";pwd)\ncd \"$ORI_DIR\"\nfor i in `ls \"$SHELL_FOLDER/post-receive.d\"`; do\n sh \"$SHELL_FOLDER/post-receive.d/$i\"\ndone", setting.ScriptType),
}
giteaHookTpls = []string{
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),