mirror of https://github.com/gogs/gogs.git
git: delegate all server-side Git hooks (#1623)
parent
859009259a
commit
039dc33367
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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.
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
6
gogs.go
6
gogs.go
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
@ -106,7 +106,6 @@ var (
|
|||
UseMySQL bool
|
||||
UsePostgreSQL bool
|
||||
UseMSSQL bool
|
||||
UseTiDB bool
|
||||
|
||||
// Webhook settings
|
||||
Webhook struct {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.9.146.0214
|
||||
0.9.147.0214
|
|
@ -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>
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const _VERSION = "0.4.6"
|
||||
const _VERSION = "0.4.7"
|
||||
|
||||
func Version() string {
|
||||
return _VERSION
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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=",
|
||||
|
|
Loading…
Reference in New Issue