git: delegate all server-side Git hooks (#1623)

master
Unknwon 2017-02-14 16:22:16 -05:00
parent 859009259a
commit 039dc33367
No known key found for this signature in database
GPG Key ID: 25B575AE3213B2B3
22 changed files with 337 additions and 185 deletions

View File

@ -18,7 +18,7 @@ The issue will be closed without any reasons if it does not satisfy any of follo
- [ ] Yes (provide example URL)
- [ ] No
- [ ] Not relevant
- Log gist:
- Log gist (usually found in `log/gogs.log`):
## Description

127
cmd/hook.go Normal file
View File

@ -0,0 +1,127 @@
// 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"
"os"
"os/exec"
"path/filepath"
"github.com/urfave/cli"
"github.com/gogits/gogs/models"
)
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{
stringFlag("config, c", "custom/conf/app.ini", "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
}
setup(c, "hooks/pre-receive.log")
buf := bytes.NewBuffer(nil)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
buf.Write(scanner.Bytes())
buf.WriteByte('\n')
}
customHooksPath := os.Getenv(_ENV_REPO_CUSTOM_HOOKS_PATH)
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
}
setup(c, "hooks/update.log")
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(_ENV_UPDATE_TASK_UUID)
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(_ENV_REPO_CUSTOM_HOOKS_PATH)
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
}
setup(c, "hooks/post-receive.log")
customHooksPath := os.Getenv(_ENV_REPO_CUSTOM_HOOKS_PATH)
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
}

View File

@ -14,7 +14,7 @@ import (
"time"
"github.com/Unknwon/com"
git "github.com/gogits/git-module"
"github.com/gogits/git-module"
gouuid "github.com/satori/go.uuid"
"github.com/urfave/cli"
log "gopkg.in/clog.v1"
@ -26,10 +26,12 @@ import (
)
const (
_ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
_ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
_ENV_UPDATE_TASK_UUID = "UPDATE_TASK_UUID"
_ENV_REPO_CUSTOM_HOOKS_PATH = "REPO_CUSTOM_HOOKS_PATH"
)
var CmdServ = cli.Command{
var Serv = cli.Command{
Name: "serv",
Usage: "This command should only be called by SSH shell",
Description: `Serv provide access auth for repositories`,
@ -39,7 +41,13 @@ var CmdServ = cli.Command{
},
}
func setup(logPath string) {
func setup(c *cli.Context, logPath string) {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
} else if c.GlobalIsSet("config") {
setting.CustomConf = c.GlobalString("config")
}
setting.NewContext()
setting.NewService()
log.New(log.FILE, log.FileConfig{
@ -54,7 +62,7 @@ func setup(logPath string) {
models.LoadConfigs()
if setting.UseSQLite3 || setting.UseTiDB {
if setting.UseSQLite3 {
workDir, _ := setting.WorkDir()
os.Chdir(workDir)
}
@ -62,7 +70,7 @@ func setup(logPath string) {
models.SetEngine()
}
func parseCmd(cmd string) (string, string) {
func parseSSHCmd(cmd string) (string, string) {
ss := strings.SplitN(cmd, " ", 2)
if len(ss) != 2 {
return "", ""
@ -157,11 +165,7 @@ 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")
}
setup("serv.log")
setup(c, "serv.log")
if setting.SSH.Disabled {
println("Gogs: SSH has been disabled")
@ -172,21 +176,21 @@ func runServ(c *cli.Context) error {
fail("Not enough arguments", "Not enough arguments")
}
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if len(cmd) == 0 {
sshCmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if len(sshCmd) == 0 {
println("Hi there, You've successfully authenticated, but Gogs does not provide shell access.")
println("If this is unexpected, please log in with password and setup Gogs under another user.")
return nil
}
verb, args := parseCmd(cmd)
repoPath := strings.ToLower(strings.Trim(args, "'"))
rr := strings.SplitN(repoPath, "/", 2)
if len(rr) != 2 {
verb, args := parseSSHCmd(sshCmd)
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") {
@ -194,29 +198,30 @@ 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(_ACCESS_DENIED_MESSAGE, "Repository does not exist: %s/%s", repoUser.Name, reponame)
fail(_ACCESS_DENIED_MESSAGE, "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)
requestMode, ok := allowedCommands[verb]
if !ok {
fail("Unknown git command", "Unknown git command '%s'", verb)
}
// Prohibit push to mirror repositories.
if requestedMode > models.ACCESS_MODE_READ && repo.IsMirror {
if requestMode > models.ACCESS_MODE_READ && repo.IsMirror {
fail("mirror repository is read-only", "")
}
@ -225,33 +230,35 @@ func runServ(c *cli.Context) error {
key, err := models.GetPublicKeyByID(com.StrTo(strings.TrimPrefix(c.Args()[0], "key-")).MustInt64())
if err != nil {
fail("Invalid key ID", "Invalid key ID [%s]: %v", c.Args()[0], err)
fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
}
if requestedMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
if requestMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
// Check deploy key or user key.
if key.IsDeployKey() {
if key.Mode < requestedMode {
if key.Mode < requestMode {
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
}
checkDeployKey(key, repo)
} else {
user, err = models.GetUserByKeyID(key.ID)
if err != nil {
fail("internal error", "Failed to get user by key ID(%d): %v", key.ID, err)
fail("Internal error", "Fail to get user by key ID '%d': %v", key.ID, err)
}
mode, err := models.AccessLevel(user, repo)
if err != nil {
fail("Internal error", "Fail to check access: %v", err)
} else if mode < requestedMode {
}
if mode < requestMode {
clientMessage := _ACCESS_DENIED_MESSAGE
if mode >= models.ACCESS_MODE_READ {
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, requestMode, repoFullName)
}
}
} else {
@ -265,30 +272,31 @@ func runServ(c *cli.Context) error {
}
uuid := gouuid.NewV4().String()
os.Setenv("uuid", uuid)
os.Setenv(_ENV_UPDATE_TASK_UUID, uuid)
os.Setenv(_ENV_REPO_CUSTOM_HOOKS_PATH, 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)
}
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)
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", "Fail to execute git command: %v", err)
}
if requestedMode == models.ACCESS_MODE_WRITE {
handleUpdateTask(uuid, user, repoUser, reponame, isWiki)
if requestMode == models.ACCESS_MODE_WRITE {
handleUpdateTask(uuid, user, repoOwner, reponame, isWiki)
}
// Update user key activity.

View File

@ -1,58 +0,0 @@
// 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 (
"os"
"github.com/urfave/cli"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/setting"
)
var CmdUpdate = cli.Command{
Name: "update",
Usage: "This command should only be called by Git hook",
Description: `Update get pushed info and insert into database`,
Action: runUpdate,
Flags: []cli.Flag{
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
},
}
func runUpdate(c *cli.Context) error {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
}
setup("update.log")
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
log.Trace("SSH_ORIGINAL_COMMAND is empty")
return nil
}
args := c.Args()
if len(args) != 3 {
log.Fatal(2, "Arguments received are not equal to three")
} else if len(args[0]) == 0 {
log.Fatal(2, "First argument 'refName' is empty, shouldn't use")
}
task := models.UpdateTask{
UUID: os.Getenv("uuid"),
RefName: args[0],
OldCommitID: args[1],
NewCommitID: args[2],
}
if err := models.AddUpdateTask(&task); err != nil {
log.Fatal(2, "AddUpdateTask: %v", err)
}
return nil
}

View File

@ -84,6 +84,7 @@ func checkVersion() {
}
// Check dependency version.
// LEGACY [0.11]: no need to check version as we check in vendor into version control
checkers := []VerChecker{
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.6.0"},
{"github.com/go-macaron/binding", binding.Version, "0.3.2"},
@ -94,7 +95,7 @@ func checkVersion() {
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
{"gopkg.in/ini.v1", ini.Version, "1.8.4"},
{"gopkg.in/macaron.v1", macaron.Version, "1.1.7"},
{"github.com/gogits/git-module", git.Version, "0.4.6"},
{"github.com/gogits/git-module", git.Version, "0.4.7"},
{"github.com/gogits/go-gogs-client", gogs.Version, "0.12.1"},
}
for _, c := range checkers {

View File

@ -918,8 +918,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-Gogs 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.

View File

@ -16,7 +16,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
const APP_VER = "0.9.146.0214"
const APP_VER = "0.9.147.0214"
func init() {
setting.AppVer = APP_VER
@ -29,8 +29,8 @@ func main() {
app.Version = APP_VER
app.Commands = []cli.Command{
cmd.CmdWeb,
cmd.CmdServ,
cmd.CmdUpdate,
cmd.Serv,
cmd.CmdHook,
cmd.CmdDump,
cmd.CmdCert,
cmd.CmdAdmin,

View File

@ -468,7 +468,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)
}
}

View File

@ -72,6 +72,8 @@ var migrations = []Migration{
// v13 -> v14:v0.9.87
NewMigration("set comment updated with created", setCommentUpdatedWithCreated),
// v14 -> v15:v0.9.147
NewMigration("generate and migrate Git hooks", generateAndMigrateGitHooks),
}
// Migrate database to current version

82
models/migrations/v15.go Normal file
View File

@ -0,0 +1,82 @@
// Copyright 2017 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 migrations
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
"github.com/gogits/gogs/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),
}
)
// Cleanup old update.log files.
filepath.Walk(setting.LogRootPath, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() && strings.HasPrefix(filepath.Base(path), "update.log") {
os.Remove(path)
}
return nil
})
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
})
}

View File

@ -88,8 +88,6 @@ func LoadConfigs() {
setting.UsePostgreSQL = true
case "mssql":
setting.UseMSSQL = true
case "tidb":
setting.UseTiDB = true
}
DbCfg.Host = sec.Key("HOST").String()
DbCfg.Name = sec.Key("NAME").String()

View File

@ -36,10 +36,6 @@ import (
"github.com/gogits/gogs/modules/sync"
)
const (
_TPL_UPDATE_HOOK = "#!/usr/bin/env %s\n%s update $1 $2 $3 --config='%s'\n"
)
var repoWorkingPool = sync.NewExclusivePool()
var (
@ -125,6 +121,7 @@ func NewRepoContext() {
if version.Compare("1.7.1", setting.Git.Version, ">") {
log.Fatal(4, "Gogs 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": "Gogs", "user.email": "gogs@fake.local"} {
@ -715,20 +712,33 @@ func cleanUpMigrateGitConfig(configPath string) error {
return nil
}
func createUpdateHook(repoPath string) error {
return git.SetUpdateHook(repoPath,
fmt.Sprintf(_TPL_UPDATE_HOOK, 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
}
// Finish 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)
}
}
@ -737,7 +747,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)
}
}
@ -862,8 +872,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(), "gogs-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond()))
@ -1648,12 +1658,12 @@ func ReinitMissingRepositories() error {
return nil
}
// RewriteRepositoryUpdateHook rewrites all repositories' update hook.
func RewriteRepositoryUpdateHook() error {
// 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 {
repo := bean.(*Repository)
return createUpdateHook(repo.RepoPath())
return createDelegateHooks(bean.(*Repository).RepoPath())
})
}
@ -2098,21 +2108,21 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
repoPath := RepoPath(u.Name, repo.Name)
_, stderr, err := process.ExecTimeout(10*time.Minute,
fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name),
fmt.Sprintf("ForkRepository 'git clone': %s/%s", u.Name, repo.Name),
"git", "clone", "--bare", oldRepo.RepoPath(), repoPath)
if err != nil {
return nil, fmt.Errorf("git clone: %v", stderr)
}
_, stderr, err = process.ExecDir(-1,
repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath),
repoPath, fmt.Sprintf("ForkRepository 'git update-server-info': %s", repoPath),
"git", "update-server-info")
if err != nil {
return nil, fmt.Errorf("git update-server-info: %v", err)
}
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)
}
return repo, sess.Commit()

View File

@ -64,8 +64,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
}

File diff suppressed because one or more lines are too long

View File

@ -106,7 +106,6 @@ var (
UseMySQL bool
UsePostgreSQL bool
UseMSSQL bool
UseTiDB bool
// Webhook settings
Webhook struct {

View File

@ -121,7 +121,7 @@ const (
CLEAN_MISSING_REPOS
GIT_GC_REPOS
SYNC_SSH_AUTHORIZED_KEY
SYNC_REPOSITORY_UPDATE_HOOK
SYNC_REPOSITORY_HOOKS
REINIT_MISSING_REPOSITORY
)
@ -152,9 +152,9 @@ func Dashboard(ctx *context.Context) {
case SYNC_SSH_AUTHORIZED_KEY:
success = ctx.Tr("admin.dashboard.resync_all_sshkeys_success")
err = models.RewriteAllPublicKeys()
case SYNC_REPOSITORY_UPDATE_HOOK:
success = ctx.Tr("admin.dashboard.resync_all_update_hooks_success")
err = models.RewriteRepositoryUpdateHook()
case SYNC_REPOSITORY_HOOKS:
success = ctx.Tr("admin.dashboard.resync_all_hooks_success")
err = models.SyncRepositoryHooks()
case REINIT_MISSING_REPOSITORY:
success = ctx.Tr("admin.dashboard.reinit_missing_repos_success")
err = models.ReinitMissingRepositories()

View File

@ -65,7 +65,7 @@ func GlobalInit() {
highlight.NewContext()
markdown.BuildSanitizer()
if err := models.NewEngine(); err != nil {
log.Fatal(4, "Fail to initialize ORM engine: %v", err)
log.Fatal(2, "Fail to initialize ORM engine: %v", err)
}
models.HasEngine = true

View File

@ -1 +1 @@
0.9.146.0214
0.9.147.0214

View File

@ -40,7 +40,7 @@
<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubUrl}}/admin?op=5">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
</tr>
<tr>
<td>{{.i18n.Tr "admin.dashboard.resync_all_update_hooks"}}</td>
<td>{{.i18n.Tr "admin.dashboard.resync_all_hooks"}}</td>
<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubUrl}}/admin?op=6">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
</tr>
<tr>

View File

@ -10,7 +10,7 @@ import (
"time"
)
const _VERSION = "0.4.6"
const _VERSION = "0.4.7"
func Version() string {
return _VERSION

View File

@ -10,16 +10,18 @@ import (
"os"
"path"
"strings"
"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 = errors.New("not a valid Git hook")
@ -27,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
}
@ -51,7 +53,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)
@ -74,7 +76,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) {
@ -91,8 +93,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
@ -100,22 +102,3 @@ func ListHooks(repoPath string) (_ []*Hook, err error) {
}
return hooks, nil
}
const (
HOOK_PATH_UPDATE = "hooks/update"
)
// SetUpdateHook writes given content to update hook of the reposiotry.
func SetUpdateHook(repoPath, content string) (err error) {
log("Setting update hook: %s", repoPath)
hookPath := path.Join(repoPath, HOOK_PATH_UPDATE)
if com.IsExist(hookPath) {
err = os.Remove(hookPath)
} else {
err = os.MkdirAll(path.Dir(hookPath), os.ModePerm)
}
if err != nil {
return err
}
return ioutil.WriteFile(hookPath, []byte(content), 0777)
}

6
vendor/vendor.json vendored
View File

@ -159,10 +159,10 @@
"revisionTime": "2016-08-10T03:50:02Z"
},
{
"checksumSHA1": "ZHQdOAFE192O5dAsLx0drW+VP8U=",
"checksumSHA1": "eH7yo/XLaT4A9yurJ0rrRxdbBTE=",
"path": "github.com/gogits/git-module",
"revision": "172cbc21accbf0085a58fd0832f46a9f694130e8",
"revisionTime": "2017-01-31T23:38:55Z"
"revision": "4d18cee9bde82bffe8c91747f1585afbf06311b2",
"revisionTime": "2017-02-14T20:50:54Z"
},
{
"checksumSHA1": "SdCLcPmklkXjPVMGkG1pYNmuO2Q=",