@ -0,0 +1,111 @@ | |||
// 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 git | |||
import ( | |||
"errors" | |||
"io/ioutil" | |||
"os" | |||
"path" | |||
"strings" | |||
) | |||
// hookNames is a list of Git hooks' name that are supported. | |||
var hookNames = []string{ | |||
"pre-applypatch", | |||
"applypatch-msg", | |||
"prepare-commit-msg", | |||
"commit-msg", | |||
"pre-commit", | |||
"pre-rebase", | |||
"post-commit", | |||
"post-receive", | |||
"post-update", | |||
} | |||
var ( | |||
ErrNotValidHook = errors.New("not a valid Git hook") | |||
) | |||
// IsValidHookName returns true if given name is a valid Git hook. | |||
func IsValidHookName(name string) bool { | |||
for _, hn := range hookNames { | |||
if hn == name { | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
// Hook represents a Git hook. | |||
type Hook struct { | |||
name string | |||
IsActive bool // Indicates whether repository has this hook. | |||
Content string // Content of hook if it's active. | |||
Sample string // Sample content from Git. | |||
path string // Hook file path. | |||
} | |||
// GetHook returns a Git hook by given name and repository. | |||
func GetHook(repoPath, name string) (*Hook, error) { | |||
if !IsValidHookName(name) { | |||
return nil, ErrNotValidHook | |||
} | |||
h := &Hook{ | |||
name: name, | |||
path: path.Join(repoPath, "hooks", name), | |||
} | |||
if isFile(h.path) { | |||
data, err := ioutil.ReadFile(h.path) | |||
if err != nil { | |||
return nil, err | |||
} | |||
h.IsActive = true | |||
h.Content = string(data) | |||
} else if isFile(h.path + ".sample") { | |||
data, err := ioutil.ReadFile(h.path + ".sample") | |||
if err != nil { | |||
return nil, err | |||
} | |||
h.Sample = string(data) | |||
} | |||
return h, nil | |||
} | |||
func (h *Hook) Name() string { | |||
return h.name | |||
} | |||
// Update updates hook settings. | |||
func (h *Hook) Update() error { | |||
if len(strings.TrimSpace(h.Content)) == 0 { | |||
return os.Remove(h.path) | |||
} | |||
return ioutil.WriteFile(h.path, []byte(h.Content), os.ModePerm) | |||
} | |||
// ListHooks returns a list of Git hooks of given repository. | |||
func ListHooks(repoPath string) (_ []*Hook, err error) { | |||
if !isDir(path.Join(repoPath, "hooks")) { | |||
return nil, errors.New("hooks path does not exist") | |||
} | |||
hooks := make([]*Hook, len(hookNames)) | |||
for i, name := range hookNames { | |||
hooks[i], err = GetHook(repoPath, name) | |||
if err != nil { | |||
return nil, err | |||
} | |||
} | |||
return hooks, nil | |||
} | |||
func (repo *Repository) GetHook(name string) (*Hook, error) { | |||
return GetHook(repo.Path, name) | |||
} | |||
func (repo *Repository) Hooks() ([]*Hook, error) { | |||
return ListHooks(repo.Path) | |||
} |
@ -1 +1 @@ | |||
0.5.4.1005 Beta | |||
0.5.5.1006 Beta |
@ -0,0 +1,41 @@ | |||
{{template "ng/base/head" .}} | |||
{{template "ng/base/header" .}} | |||
<div id="repo-wrapper"> | |||
{{template "repo/header" .}} | |||
<div id="setting-wrapper" class="main-wrapper"> | |||
<div id="repo-setting" class="container clear"> | |||
{{template "repo/settings/nav" .}} | |||
<div class="grid-4-5 left"> | |||
<div class="setting-content"> | |||
{{template "ng/base/alert" .}} | |||
<div id="setting-content"> | |||
<div id="repo-hooks-panel" class="panel panel-radius"> | |||
<div class="panel-header"> | |||
<strong>{{.i18n.Tr "repo.settings.githooks"}}</strong> | |||
</div> | |||
<form class="form form-align panel-body" id="repo-setting-form" action="{{.RepoLink}}/settings/hooks/git/{{.Hook.Name}}" method="post"> | |||
{{.CsrfTokenHtml}} | |||
<div class="text-center panel-desc">{{.i18n.Tr "repo.settings.githook_edit_desc"}}</div> | |||
{{with .Hook}} | |||
<div class="field"> | |||
<label>{{$.i18n.Tr "repo.settings.githook_name"}}</label> | |||
<label class="text-left">{{.Name}}</label> | |||
</div> | |||
<div class="field clear"> | |||
<label class="left" for="content">{{$.i18n.Tr "repo.settings.githook_content"}}</label> | |||
<textarea class="ipt-textarea ipt-large ipt-radius" id="content" name="content" cols="60" rows="20" wrap="off">{{if .IsActive}}{{.Content}}{{else}}{{.Sample}}{{end}}</textarea> | |||
</div> | |||
<div class="field"> | |||
<span class="form-label"></span> | |||
<button class="btn btn-green btn-large btn-radius" id="change-reponame-btn" href="#change-reponame-modal">{{$.i18n.Tr "repo.settings.update_githook"}}</button> | |||
</div> | |||
{{end}} | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "ng/base/footer" .}} |
@ -0,0 +1,37 @@ | |||
{{template "ng/base/head" .}} | |||
{{template "ng/base/header" .}} | |||
<div id="repo-wrapper"> | |||
{{template "repo/header" .}} | |||
<div id="setting-wrapper" class="main-wrapper"> | |||
<div id="repo-setting" class="container clear"> | |||
{{template "repo/settings/nav" .}} | |||
<div class="grid-4-5 left"> | |||
<div class="setting-content"> | |||
{{template "ng/base/alert" .}} | |||
<div id="setting-content"> | |||
<div id="repo-hooks-panel" class="panel panel-radius"> | |||
<div class="panel-header"> | |||
<strong>{{.i18n.Tr "repo.settings.githooks"}}</strong> | |||
</div> | |||
<ul class="panel-body setting-list"> | |||
<li>{{.i18n.Tr "repo.settings.githooks_desc" | Str2html}}</li> | |||
{{range .Hooks}} | |||
<li> | |||
{{if .IsActive}} | |||
<span class="left text-success"><i class="octicon octicon-check"></i></span> | |||
{{else}} | |||
<span class="left text-grey"><i class="octicon octicon-primitive-dot"></i></span> | |||
{{end}} | |||
<span>{{.Name}}</span> | |||
<a href="{{$.RepoLink}}/settings/hooks/git/{{.Name}}" class="text-blue right"><i class="fa fa-pencil"></i></a> | |||
</li> | |||
{{end}} | |||
</ul> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "ng/base/footer" .}} |