aboutsummaryrefslogtreecommitdiff
path: root/models
diff options
context:
space:
mode:
authorMichael Boke <michael@mbict.nl>2014-10-03 22:51:07 +0200
committerMichael Boke <michael@mbict.nl>2014-10-03 22:51:07 +0200
commitba1270df2d3d835b397317f133963e7b517242f1 (patch)
tree1265a142a1fd9951d30ae11648e7fbfb5806e594 /models
parentba0feadc34400cb91ff23f66096884d862651cdd (diff)
parent405ee14711ab946bd709ec28a526890c40cbc03b (diff)
Merge remote-tracking branch 'upstream/master'
Conflicts: conf/app.ini
Diffstat (limited to 'models')
-rw-r--r--models/action.go117
-rw-r--r--models/git_diff.go56
-rw-r--r--models/issue.go2
-rw-r--r--models/login.go8
-rw-r--r--models/models.go49
-rw-r--r--models/org.go32
-rw-r--r--models/publickey.go25
-rw-r--r--models/repo.go169
-rw-r--r--models/slack.go125
-rw-r--r--models/update.go11
-rw-r--r--models/user.go82
-rw-r--r--models/webhook.go140
12 files changed, 590 insertions, 226 deletions
diff --git a/models/action.go b/models/action.go
index b5f692c4..4203ead3 100644
--- a/models/action.go
+++ b/models/action.go
@@ -137,7 +137,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
return err
}
- url := fmt.Sprintf("/%s/%s/commit/%s", repoUserName, repoName, c.Sha1)
+ url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)
if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil {
@@ -172,7 +172,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
// CommitRepoAction adds new action for committing repository.
func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
- repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits) error {
+ repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits, oldCommitId string, newCommitId string) error {
opType := COMMIT_REPO
// Check it's tag push or branch.
@@ -220,21 +220,52 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
ws, err := GetActiveWebhooksByRepoId(repoId)
if err != nil {
- return errors.New("action.CommitRepoAction(GetWebhooksByRepoId): " + err.Error())
- } else if len(ws) == 0 {
+ return errors.New("action.CommitRepoAction(GetActiveWebhooksByRepoId): " + err.Error())
+ }
+
+ // check if repo belongs to org and append additional webhooks
+ if repo.Owner.IsOrganization() {
+ // get hooks for org
+ orgws, err := GetActiveWebhooksByOrgId(repo.OwnerId)
+ if err != nil {
+ return errors.New("action.CommitRepoAction(GetActiveWebhooksByOrgId): " + err.Error())
+ }
+ ws = append(ws, orgws...)
+ }
+
+ if len(ws) == 0 {
return nil
}
repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
+ compareUrl := ""
+ // if not the first commit, set the compareUrl
+ if !strings.HasPrefix(oldCommitId, "0000000") {
+ compareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId)
+ }
+
+ pusher_email, pusher_name := "", ""
+ pusher, err := GetUserByName(userName)
+ if err == nil {
+ pusher_email = pusher.Email
+ pusher_name = pusher.GetFullNameFallback()
+ }
+
commits := make([]*PayloadCommit, len(commit.Commits))
for i, cmt := range commit.Commits {
+ author_username := ""
+ author, err := GetUserByEmail(cmt.AuthorEmail)
+ if err == nil {
+ author_username = author.Name
+ }
commits[i] = &PayloadCommit{
Id: cmt.Sha1,
Message: cmt.Message,
Url: fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1),
Author: &PayloadAuthor{
- Name: cmt.AuthorName,
- Email: cmt.AuthorEmail,
+ Name: cmt.AuthorName,
+ Email: cmt.AuthorEmail,
+ UserName: author_username,
},
}
}
@@ -249,15 +280,20 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
Website: repo.Website,
Watchers: repo.NumWatches,
Owner: &PayloadAuthor{
- Name: repoUserName,
- Email: actEmail,
+ Name: repo.Owner.GetFullNameFallback(),
+ Email: repo.Owner.Email,
+ UserName: repo.Owner.Name,
},
Private: repo.IsPrivate,
},
Pusher: &PayloadAuthor{
- Name: repo.Owner.LowerName,
- Email: repo.Owner.Email,
+ Name: pusher_name,
+ Email: pusher_email,
+ UserName: userName,
},
+ Before: oldCommitId,
+ After: newCommitId,
+ CompareUrl: compareUrl,
}
for _, w := range ws {
@@ -266,15 +302,36 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
continue
}
- p.Secret = w.Secret
- CreateHookTask(&HookTask{
- Type: WEBHOOK,
- Url: w.Url,
- Payload: p,
- ContentType: w.ContentType,
- IsSsl: w.IsSsl,
- })
+ switch w.HookTaskType {
+ case SLACK:
+ {
+ s, err := GetSlackPayload(p, w.Meta)
+ if err != nil {
+ return errors.New("action.GetSlackPayload: " + err.Error())
+ }
+ CreateHookTask(&HookTask{
+ Type: w.HookTaskType,
+ Url: w.Url,
+ BasePayload: s,
+ ContentType: w.ContentType,
+ IsSsl: w.IsSsl,
+ })
+ }
+ default:
+ {
+ p.Secret = w.Secret
+ CreateHookTask(&HookTask{
+ Type: w.HookTaskType,
+ Url: w.Url,
+ BasePayload: p,
+ ContentType: w.ContentType,
+ IsSsl: w.IsSsl,
+ })
+ }
+ }
}
+
+ go DeliverHooks()
return nil
}
@@ -293,13 +350,29 @@ func NewRepoAction(u *User, repo *Repository) (err error) {
// TransferRepoAction adds new action for transfering repository.
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
- if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email,
- OpType: TRANSFER_REPO, RepoId: repo.Id, RepoName: repo.Name, Content: newUser.Name,
- IsPrivate: repo.IsPrivate}); err != nil {
+ action := &Action{
+ ActUserId: u.Id,
+ ActUserName: u.Name,
+ ActEmail: u.Email,
+ OpType: TRANSFER_REPO,
+ RepoId: repo.Id,
+ RepoUserName: newUser.Name,
+ RepoName: repo.Name,
+ IsPrivate: repo.IsPrivate,
+ Content: path.Join(repo.Owner.LowerName, repo.LowerName),
+ }
+ if err = NotifyWatchers(action); err != nil {
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
return err
}
+ // Remove watch for organization.
+ if repo.Owner.IsOrganization() {
+ if err = WatchRepo(repo.Owner.Id, repo.Id, false); err != nil {
+ log.Error(4, "WatchRepo", err)
+ }
+ }
+
log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name)
return err
}
@@ -309,7 +382,7 @@ func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) {
actions := make([]*Action, 0, 20)
sess := x.Limit(20, int(offset)).Desc("id").Where("user_id=?", uid)
if isProfile {
- sess.Where("is_private=?", false).And("act_user_id=?", uid)
+ sess.And("is_private=?", false).And("act_user_id=?", uid)
}
err := sess.Find(&actions)
return actions, err
diff --git a/models/git_diff.go b/models/git_diff.go
index 4b4d1234..e093e7ab 100644
--- a/models/git_diff.go
+++ b/models/git_diff.go
@@ -15,8 +15,7 @@ import (
"github.com/Unknwon/com"
- "github.com/gogits/git"
-
+ "github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
)
@@ -71,7 +70,7 @@ func (diff *Diff) NumFiles() int {
const DIFF_HEAD = "diff --git "
-func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
+func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
scanner := bufio.NewScanner(reader)
var (
curFile *DiffFile
@@ -80,6 +79,7 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
}
leftLine, rightLine int
+ isTooLong bool
)
diff := &Diff{Files: make([]*DiffFile, 0)}
@@ -91,16 +91,17 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
continue
}
+ if line == "" {
+ continue
+ }
+
i = i + 1
- // Diff data too large.
- if i == 5000 {
+ // Diff data too large, we only show the first about maxlines lines
+ if i == maxlines {
+ isTooLong = true
log.Warn("Diff data too large")
- return &Diff{}, nil
- }
-
- if line == "" {
- continue
+ //return &Diff{}, nil
}
switch {
@@ -111,6 +112,10 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
curSection.Lines = append(curSection.Lines, diffLine)
continue
case line[0] == '@':
+ if isTooLong {
+ return diff, nil
+ }
+
curSection = &DiffSection{}
curFile.Sections = append(curFile.Sections, curSection)
ss := strings.Split(line, "@@")
@@ -144,6 +149,10 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
// Get new file.
if strings.HasPrefix(line, DIFF_HEAD) {
+ if isTooLong {
+ return diff, nil
+ }
+
fs := strings.Split(line[len(DIFF_HEAD):], " ")
a := fs[0]
@@ -175,25 +184,30 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
return diff, nil
}
-func GetDiff(repoPath, commitid string) (*Diff, error) {
+func GetDiffRange(repoPath, beforeCommitId string, afterCommitId string, maxlines int) (*Diff, error) {
repo, err := git.OpenRepository(repoPath)
if err != nil {
return nil, err
}
- commit, err := repo.GetCommit(commitid)
+ commit, err := repo.GetCommit(afterCommitId)
if err != nil {
return nil, err
}
rd, wr := io.Pipe()
var cmd *exec.Cmd
- // First commit of repository.
- if commit.ParentCount() == 0 {
- cmd = exec.Command("git", "show", commitid)
+ // if "after" commit given
+ if beforeCommitId == "" {
+ // First commit of repository.
+ if commit.ParentCount() == 0 {
+ cmd = exec.Command("git", "show", afterCommitId)
+ } else {
+ c, _ := commit.Parent(0)
+ cmd = exec.Command("git", "diff", c.Id.String(), afterCommitId)
+ }
} else {
- c, _ := commit.Parent(0)
- cmd = exec.Command("git", "diff", c.Id.String(), commitid)
+ cmd = exec.Command("git", "diff", beforeCommitId, afterCommitId)
}
cmd.Dir = repoPath
cmd.Stdout = wr
@@ -208,7 +222,7 @@ func GetDiff(repoPath, commitid string) (*Diff, error) {
}()
defer rd.Close()
- desc := fmt.Sprintf("GetDiff(%s)", repoPath)
+ desc := fmt.Sprintf("GetDiffRange(%s)", repoPath)
pid := process.Add(desc, cmd)
go func() {
// In case process became zombie.
@@ -224,5 +238,9 @@ func GetDiff(repoPath, commitid string) (*Diff, error) {
}
}()
- return ParsePatch(pid, cmd, rd)
+ return ParsePatch(pid, maxlines, cmd, rd)
+}
+
+func GetDiffCommit(repoPath, commitId string, maxlines int) (*Diff, error) {
+ return GetDiffRange(repoPath, "", commitId, maxlines)
}
diff --git a/models/issue.go b/models/issue.go
index 307ace81..f16c2e25 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -612,7 +612,7 @@ type Milestone struct {
RepoId int64 `xorm:"INDEX"`
Index int64
Name string
- Content string
+ Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
IsClosed bool
NumIssues int
diff --git a/models/login.go b/models/login.go
index da7722f2..2c5fc68e 100644
--- a/models/login.go
+++ b/models/login.go
@@ -161,12 +161,8 @@ func UserSignIn(uname, passwd string) (*User, error) {
return nil, err
}
- if u.LoginType == NOTYPE {
- if has {
- u.LoginType = PLAIN
- } else {
- return nil, ErrUserNotExist
- }
+ if u.LoginType == NOTYPE && has {
+ u.LoginType = PLAIN
}
// For plain login, user must exist to reach this line.
diff --git a/models/models.go b/models/models.go
index 4e2e08cf..570df0c1 100644
--- a/models/models.go
+++ b/models/models.go
@@ -55,11 +55,12 @@ func LoadModelsConfig() {
DbCfg.Path = setting.Cfg.MustValue("database", "PATH", "data/gogs.db")
}
-func NewTestEngine(x *xorm.Engine) (err error) {
+func getEngine() (*xorm.Engine, error) {
+ cnnstr := ""
switch DbCfg.Type {
case "mysql":
- x, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8",
- DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name))
+ cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8",
+ DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name)
case "postgres":
var host, port = "127.0.0.1", "5432"
fields := strings.Split(DbCfg.Host, ":")
@@ -69,48 +70,33 @@ func NewTestEngine(x *xorm.Engine) (err error) {
if len(fields) > 1 && len(strings.TrimSpace(fields[1])) > 0 {
port = fields[1]
}
- cnnstr := fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s",
+ cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s",
DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode)
- x, err = xorm.NewEngine("postgres", cnnstr)
case "sqlite3":
if !EnableSQLite3 {
- return fmt.Errorf("Unknown database type: %s", DbCfg.Type)
+ return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
}
os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm)
- x, err = xorm.NewEngine("sqlite3", DbCfg.Path)
+ cnnstr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
default:
- return fmt.Errorf("Unknown database type: %s", DbCfg.Type)
+ return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
}
+ return xorm.NewEngine(DbCfg.Type, cnnstr)
+}
+
+func NewTestEngine(x *xorm.Engine) (err error) {
+ x, err = getEngine()
if err != nil {
- return fmt.Errorf("models.init(fail to conntect database): %v", err)
+ return fmt.Errorf("models.init(fail to connect to database): %v", err)
}
+
return x.Sync(tables...)
}
func SetEngine() (err error) {
- switch DbCfg.Type {
- case "mysql":
- x, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8",
- DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name))
- case "postgres":
- var host, port = "127.0.0.1", "5432"
- fields := strings.Split(DbCfg.Host, ":")
- if len(fields) > 0 && len(strings.TrimSpace(fields[0])) > 0 {
- host = fields[0]
- }
- if len(fields) > 1 && len(strings.TrimSpace(fields[1])) > 0 {
- port = fields[1]
- }
- x, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s",
- DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode))
- case "sqlite3":
- os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm)
- x, err = xorm.NewEngine("sqlite3", DbCfg.Path)
- default:
- return fmt.Errorf("Unknown database type: %s", DbCfg.Type)
- }
+ x, err = getEngine()
if err != nil {
- return fmt.Errorf("models.init(fail to conntect database): %v", err)
+ return fmt.Errorf("models.init(fail to connect to database): %v", err)
}
// WARNNING: for serv command, MUST remove the output to os.stdout,
@@ -125,6 +111,7 @@ func SetEngine() (err error) {
x.Logger = xorm.NewSimpleLogger(f)
x.ShowSQL = true
+ x.ShowInfo = true
x.ShowDebug = true
x.ShowErr = true
x.ShowWarn = true
diff --git a/models/org.go b/models/org.go
index ce506705..31db8e36 100644
--- a/models/org.go
+++ b/models/org.go
@@ -507,7 +507,7 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
mode := AuthorizeToAccessType(t.Authorize)
for _, u := range t.Members {
- auth, err := GetHighestAuthorize(t.OrgId, u.Id, t.Id, repo.Id)
+ auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id)
if err != nil {
sess.Rollback()
return err
@@ -517,13 +517,7 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
UserName: u.LowerName,
RepoName: path.Join(repo.Owner.LowerName, repo.LowerName),
}
- if auth == 0 {
- access.Mode = mode
- if _, err = sess.Insert(access); err != nil {
- sess.Rollback()
- return fmt.Errorf("fail to insert access: %v", err)
- }
- } else if auth < t.Authorize {
+ if auth < t.Authorize {
if err = addAccessWithAuthorize(sess, access, mode); err != nil {
sess.Rollback()
return err
@@ -570,7 +564,7 @@ func (t *Team) RemoveRepository(repoId int64) error {
// Remove access to team members.
for _, u := range t.Members {
- auth, err := GetHighestAuthorize(t.OrgId, u.Id, t.Id, repo.Id)
+ auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id)
if err != nil {
sess.Rollback()
return err
@@ -668,7 +662,7 @@ func GetTeamById(teamId int64) (*Team, error) {
}
// GetHighestAuthorize returns highest repository authorize level for given user and team.
-func GetHighestAuthorize(orgId, uid, teamId, repoId int64) (AuthorizeType, error) {
+func GetHighestAuthorize(orgId, uid, repoId, teamId int64) (AuthorizeType, error) {
ts, err := GetUserTeams(orgId, uid)
if err != nil {
return 0, err
@@ -687,6 +681,7 @@ func GetHighestAuthorize(orgId, uid, teamId, repoId int64) (AuthorizeType, error
}
}
}
+
return auth, nil
}
@@ -728,7 +723,7 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
// ORG_WRITABLE is the highest authorize level for now.
// Skip checking others if current team has this level.
if t.Authorize < ORG_WRITABLE {
- auth, err := GetHighestAuthorize(org.Id, u.Id, t.Id, repo.Id)
+ auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id)
if err != nil {
sess.Rollback()
return err
@@ -782,7 +777,7 @@ func DeleteTeam(t *Team) error {
// Delete all accesses.
for _, repo := range t.Repos {
for _, u := range t.Members {
- auth, err := GetHighestAuthorize(org.Id, u.Id, t.Id, repo.Id)
+ auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id)
if err != nil {
sess.Rollback()
return err
@@ -943,7 +938,7 @@ func AddTeamMember(orgId, teamId, uid int64) error {
// Give access to team repositories.
mode := AuthorizeToAccessType(t.Authorize)
for _, repo := range t.Repos {
- auth, err := GetHighestAuthorize(orgId, uid, teamId, repo.Id)
+ auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, teamId)
if err != nil {
sess.Rollback()
return err
@@ -953,14 +948,7 @@ func AddTeamMember(orgId, teamId, uid int64) error {
UserName: u.LowerName,
RepoName: path.Join(org.LowerName, repo.LowerName),
}
- // Equal 0 means given access doesn't exist.
- if auth == 0 {
- access.Mode = mode
- if _, err = sess.Insert(access); err != nil {
- sess.Rollback()
- return fmt.Errorf("fail to insert access: %v", err)
- }
- } else if auth < t.Authorize {
+ if auth < t.Authorize {
if err = addAccessWithAuthorize(sess, access, mode); err != nil {
sess.Rollback()
return err
@@ -1037,7 +1025,7 @@ func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) erro
// Delete access to team repositories.
for _, repo := range t.Repos {
- auth, err := GetHighestAuthorize(orgId, uid, teamId, repo.Id)
+ auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, teamId)
if err != nil {
sess.Rollback()
return err
diff --git a/models/publickey.go b/models/publickey.go
index 1246cffc..8bb924e8 100644
--- a/models/publickey.go
+++ b/models/publickey.go
@@ -22,6 +22,7 @@ import (
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
+ "github.com/gogits/gogs/modules/setting"
)
const (
@@ -100,6 +101,7 @@ var (
"(MCE)": 1702,
"(McE)": 1702,
"(RSA)": 2048,
+ "(DSA)": 1024,
}
)
@@ -119,23 +121,30 @@ func CheckPublicKeyString(content string) (bool, error) {
tmpFile.WriteString(content)
tmpFile.Close()
- // … see if ssh-keygen recognizes its contents
+ // Check if ssh-keygen recognizes its contents.
stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath)
if err != nil {
return false, errors.New("ssh-keygen -l -f: " + stderr)
} else if len(stdout) < 2 {
return false, errors.New("ssh-keygen returned not enough output to evaluate the key")
}
+
+ // The ssh-keygen in Windows does not print key type, so no need go further.
+ if setting.IsWindows {
+ return true, nil
+ }
+
sshKeygenOutput := strings.Split(stdout, " ")
if len(sshKeygenOutput) < 4 {
return false, errors.New("Not enough fields returned by ssh-keygen -l -f")
}
+
+ // Check if key type and key size match.
keySize, err := com.StrTo(sshKeygenOutput[0]).Int()
if err != nil {
return false, errors.New("Cannot get key size of the given key")
}
keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1])
-
if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 {
return false, errors.New("Sorry, unrecognized public key type")
} else if keySize < minimumKeySize {
@@ -160,10 +169,14 @@ func saveAuthorizedKeyFile(key *PublicKey) error {
if err != nil {
return err
}
- if finfo.Mode().Perm() > 0600 {
- log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", finfo.Mode().Perm().String())
- if err = f.Chmod(0600); err != nil {
- return err
+
+ // FIXME: following command does not support in Windows.
+ if !setting.IsWindows {
+ if finfo.Mode().Perm() > 0600 {
+ log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", finfo.Mode().Perm().String())
+ if err = f.Chmod(0600); err != nil {
+ return err
+ }
}
}
diff --git a/models/repo.go b/models/repo.go
index 47036966..a79c2491 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -95,24 +95,35 @@ func NewRepoContext() {
if err != nil {
log.Fatal(4, "Fail to get Git version: %v", err)
}
- if ver.Major < 2 && ver.Minor < 8 {
- log.Fatal(4, "Gogs requires Git version greater or equal to 1.8.0")
- }
- // Check if server has basic git setting.
- stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name")
+ reqVer, err := git.ParseVersion("1.7.1")
if err != nil {
- log.Fatal(4, "Fail to get git user.name: %s", stderr)
- } else if err != nil || len(strings.TrimSpace(stdout)) == 0 {
- if _, stderr, err = process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", "gogitservice@gmail.com"); err != nil {
- log.Fatal(4, "Fail to set git user.email: %s", stderr)
- } else if _, stderr, err = process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", "Gogs"); err != nil {
- log.Fatal(4, "Fail to set git user.name: %s", stderr)
+ log.Fatal(4, "Fail to parse required Git version: %v", err)
+ }
+ if ver.LessThan(reqVer) {
+ log.Fatal(4, "Gogs requires Git version greater or equal to 1.7.1")
+ }
+
+ // Check if server has basic git setting and set if not.
+ if stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name"); err != nil || strings.TrimSpace(stdout) == "" {
+ // ExitError indicates user.name is not set
+ if _, ok := err.(*exec.ExitError); ok || strings.TrimSpace(stdout) == "" {
+ stndrdUserName := "Gogs"
+ stndrdUserEmail := "gogitservice@gmail.com"
+ if _, stderr, gerr := process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", stndrdUserName); gerr != nil {
+ log.Fatal(4, "Fail to set git user.name(%s): %s", gerr, stderr)
+ }
+ if _, stderr, gerr := process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", stndrdUserEmail); gerr != nil {
+ log.Fatal(4, "Fail to set git user.email(%s): %s", gerr, stderr)
+ }
+ log.Info("Git user.name and user.email set to %s <%s>", stndrdUserName, stndrdUserEmail)
+ } else {
+ log.Fatal(4, "Fail to get git user.name(%s): %s", err, stderr)
}
}
// Set git some configurations.
- if _, stderr, err = process.Exec("NewRepoContext(git config --global core.quotepath false)",
+ if _, stderr, err := process.Exec("NewRepoContext(git config --global core.quotepath false)",
"git", "config", "--global", "core.quotepath", "false"); err != nil {
log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr)
}
@@ -305,30 +316,17 @@ func MigrateRepository(u *User, name, desc string, private, mirror bool, url str
}
repo.IsMirror = true
return repo, UpdateRepository(repo)
+ } else {
+ os.RemoveAll(repoPath)
}
- // Clone from local repository.
+ // this command could for both migrate and mirror
_, stderr, err := process.ExecTimeout(10*time.Minute,
- fmt.Sprintf("MigrateRepository(git clone): %s", repoPath),
- "git", "clone", repoPath, tmpDir)
+ fmt.Sprintf("MigrateRepository: %s", repoPath),
+ "git", "clone", "--mirror", "--bare", url, repoPath)
if err != nil {
return repo, errors.New("git clone: " + stderr)
}
-
- // Add remote and fetch data.
- if _, stderr, err = process.ExecDir(3*time.Minute,
- tmpDir, fmt.Sprintf("MigrateRepository(git pull): %s", repoPath),
- "git", "remote", "add", "-f", "--tags", "upstream", url); err != nil {
- return repo, errors.New("git remote: " + stderr)
- }
-
- // Push data to local repository.
- if _, stderr, err = process.ExecDir(3*time.Minute,
- tmpDir, fmt.Sprintf("MigrateRepository(git push): %s", repoPath),
- "git", "push", "--tags", "origin", "refs/remotes/upstream/*:refs/heads/*"); err != nil {
- return repo, errors.New("git push: " + stderr)
- }
-
return repo, UpdateRepository(repo)
}
@@ -651,29 +649,54 @@ func RepoPath(userName, repoName string) string {
}
// TransferOwnership transfers all corresponding setting from old user to new one.
-func TransferOwnership(u *User, newOwner string, repo *Repository) (err error) {
+func TransferOwnership(u *User, newOwner string, repo *Repository) error {
newUser, err := GetUserByName(newOwner)
if err != nil {
return err
}
+ // Check if new owner has repository with same name.
+ has, err := IsRepositoryExist(newUser, repo.Name)
+ if err != nil {
+ return err
+ } else if has {
+ return ErrRepoAlreadyExist
+ }
+
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
- if _, err = sess.Where("repo_name = ?", u.LowerName+"/"+repo.LowerName).
- And("user_name = ?", u.LowerName).Update(&Access{UserName: newUser.LowerName}); err != nil {
- sess.Rollback()
- return err
+ owner := repo.Owner
+ oldRepoLink := path.Join(owner.LowerName, repo.LowerName)
+ // Delete all access first if current owner is an organization.
+ if owner.IsOrganization() {
+ if _, err = sess.Where("repo_name=?", oldRepoLink).Delete(new(Access)); err != nil {
+ sess.Rollback()
+ return fmt.Errorf("fail to delete current accesses: %v", err)
+ }
+ } else {
+ // Delete current owner access.
+ if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", owner.LowerName).
+ Delete(new(Access)); err != nil {
+ sess.Rollback()
+ return fmt.Errorf("fail to delete access(owner): %v", err)
+ }
+ // In case new owner has access.
+ if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", newUser.LowerName).
+ Delete(new(Access)); err != nil {
+ sess.Rollback()
+ return fmt.Errorf("fail to delete access(new user): %v", err)
+ }
}
- if _, err = sess.Where("repo_name = ?", u.LowerName+"/"+repo.LowerName).Update(&Access{
- RepoName: newUser.LowerName + "/" + repo.LowerName,
- }); err != nil {
+ // Change accesses to new repository path.
+ if _, err = sess.Where("repo_name=?", oldRepoLink).
+ Update(&Access{RepoName: path.Join(newUser.LowerName, repo.LowerName)}); err != nil {
sess.Rollback()
- return err
+ return fmt.Errorf("fail to update access(change reponame): %v", err)
}
// Update repository.
@@ -689,17 +712,17 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) (err error) {
return err
}
- if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", u.Id); err != nil {
+ if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil {
sess.Rollback()
return err
}
+ mode := WRITABLE
+ if repo.IsMirror {
+ mode = READABLE
+ }
// New owner is organization.
if newUser.IsOrganization() {
- mode := WRITABLE
- if repo.IsMirror {
- mode = READABLE
- }
access := &Access{
RepoName: path.Join(newUser.LowerName, repo.LowerName),
Mode: mode,
@@ -724,12 +747,6 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) (err error) {
}
}
- if _, err = sess.Exec(
- "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil {
- sess.Rollback()
- return err
- }
-
// Update owner team info and count.
t.RepoIds += "$" + com.ToStr(repo.Id) + "|"
t.NumRepos++
@@ -737,10 +754,20 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) (err error) {
sess.Rollback()
return err
}
+ } else {
+ access := &Access{
+ RepoName: path.Join(newUser.LowerName, repo.LowerName),
+ UserName: newUser.LowerName,
+ Mode: mode,
+ }
+ if _, err = sess.Insert(access); err != nil {
+ sess.Rollback()
+ return fmt.Errorf("fail to insert access: %v", err)
+ }
}
// Change repository directory name.
- if err = os.Rename(RepoPath(u.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
+ if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
sess.Rollback()
return err
}
@@ -749,14 +776,8 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) (err error) {
return err
}
- // Add watch of new owner to repository.
- if !newUser.IsOrganization() {
- if err = WatchRepo(newUser.Id, repo.Id, true); err != nil {
- log.Error(4, "WatchRepo", err)
- }
- }
- if err = WatchRepo(u.Id, repo.Id, false); err != nil {
- log.Error(4, "WatchRepo2", err)
+ if err = WatchRepo(newUser.Id, repo.Id, true); err != nil {
+ log.Error(4, "WatchRepo", err)
}
if err = TransferRepoAction(u, newUser, repo); err != nil {
@@ -940,9 +961,9 @@ func GetRepositoryByRef(ref string) (*Repository, error) {
}
// GetRepositoryByName returns the repository by given name under user if exists.
-func GetRepositoryByName(userId int64, repoName string) (*Repository, error) {
+func GetRepositoryByName(uid int64, repoName string) (*Repository, error) {
repo := &Repository{
- OwnerId: userId,
+ OwnerId: uid,
LowerName: strings.ToLower(repoName),
}
has, err := x.Get(repo)
@@ -979,8 +1000,8 @@ func GetRepositories(uid int64, private bool) ([]*Repository, error) {
}
// GetRecentUpdatedRepositories returns the list of repositories that are recently updated.
-func GetRecentUpdatedRepositories() (repos []*Repository, err error) {
- err = x.Where("is_private=?", false).Limit(5).Desc("updated").Find(&repos)
+func GetRecentUpdatedRepositories(num int) (repos []*Repository, err error) {
+ err = x.Where("is_private=?", false).Limit(num).Desc("updated").Find(&repos)
return repos, err
}
@@ -1081,6 +1102,13 @@ func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) {
return repos, err
}
+// __ __ __ .__
+// / \ / \_____ _/ |_ ____ | |__
+// \ \/\/ /\__ \\ __\/ ___\| | \
+// \ / / __ \| | \ \___| Y \
+// \__/\ / (____ /__| \___ >___| /
+// \/ \/ \/ \/
+
// Watch is connection request for receiving repository notifycation.
type Watch struct {
Id int64
@@ -1151,6 +1179,13 @@ func NotifyWatchers(act *Action) error {
return nil
}
+// _________ __
+// / _____// |______ _______
+// \_____ \\ __\__ \\_ __ \
+// / \| | / __ \| | \/
+// /_______ /|__| (____ /__|
+// \/ \/
+
type Star struct {
Id int64
Uid int64 `xorm:"UNIQUE(s)"`
@@ -1165,16 +1200,20 @@ func StarRepo(uid, repoId int64, star bool) (err error) {
}
if _, err = x.Insert(&Star{Uid: uid, RepoId: repoId}); err != nil {
return err
+ } else if _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoId); err != nil {
+ return err
}
- _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoId)
+ _, err = x.Exec("UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", uid)
} else {
if !IsStaring(uid, repoId) {
return nil
}
if _, err = x.Delete(&Star{0, uid, repoId}); err != nil {
return err
+ } else if _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoId); err != nil {
+ return err
}
- _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoId)
+ _, err = x.Exec("UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", uid)
}
return err
}
diff --git a/models/slack.go b/models/slack.go
new file mode 100644
index 00000000..3dd40759
--- /dev/null
+++ b/models/slack.go
@@ -0,0 +1,125 @@
+// 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 models
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+)
+
+const (
+ SLACK_COLOR string = "#dd4b39"
+)
+
+type Slack struct {
+ Domain string `json:"domain"`
+ Token string `json:"token"`
+ Channel string `json:"channel"`
+}
+
+type SlackPayload struct {
+ Channel string `json:"channel"`
+ Text string `json:"text"`
+ Username string `json:"username"`
+ IconUrl string `json:"icon_url"`
+ UnfurlLinks int `json:"unfurl_links"`
+ LinkNames int `json:"link_names"`
+ Attachments []SlackAttachment `json:"attachments"`
+}
+
+type SlackAttachment struct {
+ Color string `json:"color"`
+ Text string `json:"text"`
+}
+
+func GetSlackURL(domain string, token string) string {
+ return fmt.Sprintf(
+ "https://%s.slack.com/services/hooks/incoming-webhook?token=%s",
+ domain,
+ token,
+ )
+}
+
+func (p SlackPayload) GetJSONPayload() ([]byte, error) {
+ data, err := json.Marshal(p)
+ if err != nil {
+ return []byte{}, err
+ }
+ return data, nil
+}
+
+func GetSlackPayload(p *Payload, meta string) (*SlackPayload, error) {
+ slack := &Slack{}
+ slackPayload := &SlackPayload{}
+ if err := json.Unmarshal([]byte(meta), &slack); err != nil {
+ return slackPayload, errors.New("GetSlackPayload meta json:" + err.Error())
+ }
+
+ // TODO: handle different payload types: push, new branch, delete branch etc.
+ // when they are added to gogs. Only handles push now
+ return getSlackPushPayload(p, slack)
+}
+
+func getSlackPushPayload(p *Payload, slack *Slack) (*SlackPayload, error) {
+ // n new commits
+ refSplit := strings.Split(p.Ref, "/")
+ branchName := refSplit[len(refSplit)-1]
+ var commitString string
+
+ if len(p.Commits) == 1 {
+ commitString = "1 new commit"
+ if p.CompareUrl != "" {
+ commitString = SlackLinkFormatter(p.CompareUrl, commitString)
+ }
+ } else {
+ commitString = fmt.Sprintf("%d new commits", len(p.Commits))
+ if p.CompareUrl != "" {
+ commitString = SlackLinkFormatter(p.CompareUrl, commitString)
+ }
+ }
+
+ repoLink := SlackLinkFormatter(p.Repo.Url, p.Repo.Name)
+ branchLink := SlackLinkFormatter(p.Repo.Url+"/src/"+branchName, branchName)
+ text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.Name)
+ var attachmentText string
+
+ // for each commit, generate attachment text
+ for i, commit := range p.Commits {
+ attachmentText += fmt.Sprintf("%s: %s - %s", SlackLinkFormatter(commit.Url, commit.Id[:7]), SlackTextFormatter(commit.Message), SlackTextFormatter(commit.Author.Name))
+ // add linebreak to each commit but the last
+ if i < len(p.Commits)-1 {
+ attachmentText += "\n"
+ }
+ }
+
+ slackAttachments := []SlackAttachment{{Color: SLACK_COLOR, Text: attachmentText}}
+
+ return &SlackPayload{
+ Channel: slack.Channel,
+ Text: text,
+ Username: "gogs",
+ IconUrl: "https://raw.githubusercontent.com/gogits/gogs/master/public/img/favicon.png",
+ UnfurlLinks: 0,
+ LinkNames: 0,
+ Attachments: slackAttachments,
+ }, nil
+}
+
+// see: https://api.slack.com/docs/formatting
+func SlackTextFormatter(s string) string {
+ // take only first line of commit
+ first := strings.Split(s, "\n")[0]
+ // replace & < >
+ first = strings.Replace(first, "&", "&amp;", -1)
+ first = strings.Replace(first, "<", "&lt;", -1)
+ first = strings.Replace(first, ">", "&gt;", -1)
+ return first
+}
+
+func SlackLinkFormatter(url string, text string) string {
+ return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text))
+}
diff --git a/models/update.go b/models/update.go
index 68a92ada..d939a908 100644
--- a/models/update.go
+++ b/models/update.go
@@ -23,6 +23,10 @@ type UpdateTask struct {
NewCommitId string
}
+const (
+ MAX_COMMITS int = 5
+)
+
func AddUpdateTask(task *UpdateTask) error {
_, err := x.Insert(task)
return err
@@ -101,7 +105,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
commit := &base.PushCommits{}
if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
- repos.Id, repoUserName, repoName, refName, commit); err != nil {
+ repos.Id, repoUserName, repoName, refName, commit, oldCommitId, newCommitId); err != nil {
log.GitLogger.Fatal(4, "runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
}
return err
@@ -132,7 +136,6 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
// if commits push
commits := make([]*base.PushCommit, 0)
- var maxCommits = 2
var actEmail string
for e := l.Front(); e != nil; e = e.Next() {
commit := e.Value.(*git.Commit)
@@ -145,14 +148,14 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
commit.Message(),
commit.Author.Email,
commit.Author.Name})
- if len(commits) >= maxCommits {
+ if len(commits) >= MAX_COMMITS {
break
}
}
//commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()})
if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
- repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}); err != nil {
+ repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}, oldCommitId, newCommitId); err != nil {
return fmt.Errorf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
}
return nil
diff --git a/models/user.go b/models/user.go
index 96881ea3..ee8f8586 100644
--- a/models/user.go
+++ b/models/user.go
@@ -5,6 +5,7 @@
package models
import (
+ "container/list"
"crypto/sha256"
"encoding/hex"
"errors"
@@ -82,22 +83,22 @@ type User struct {
// DashboardLink returns the user dashboard page link.
func (u *User) DashboardLink() string {
if u.IsOrganization() {
- return "/org/" + u.Name + "/dashboard/"
+ return setting.AppSubUrl + "/org/" + u.Name + "/dashboard/"
}
- return "/"
+ return setting.AppSubUrl + "/"
}
// HomeLink returns the user home page link.
func (u *User) HomeLink() string {
- return "/user/" + u.Name
+ return setting.AppSubUrl + "/" + u.Name
}
// AvatarLink returns user gravatar link.
func (u *User) AvatarLink() string {
if setting.DisableGravatar {
- return "/img/avatar_default.jpg"
+ return setting.AppSubUrl + "/img/avatar_default.jpg"
} else if setting.Service.EnableCacheAvatar {
- return "/avatar/" + u.Avatar
+ return setting.AppSubUrl + "/avatar/" + u.Avatar
}
return "//1.gravatar.com/avatar/" + u.Avatar
}
@@ -167,6 +168,14 @@ func (u *User) GetOrganizations() error {
return nil
}
+// GetFullNameFallback returns Full Name if set, otherwise username
+func (u *User) GetFullNameFallback() string {
+ if u.FullName == "" {
+ return u.Name
+ }
+ return u.FullName
+}
+
// IsUserExist checks if given user name exist,
// the user name should be noncased unique.
func IsUserExist(name string) (bool, error) {
@@ -505,6 +514,49 @@ func GetUserIdsByNames(names []string) []int64 {
return ids
}
+// UserCommit represtns a commit with validation of user.
+type UserCommit struct {
+ UserName string
+ *git.Commit
+}
+
+// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
+func ValidateCommitWithEmail(c *git.Commit) (uname string) {
+ u, err := GetUserByEmail(c.Author.Email)
+ if err == nil {
+ uname = u.Name
+ }
+ return uname
+}
+
+// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
+func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
+ emails := map[string]string{}
+ newCommits := list.New()
+ e := oldCommits.Front()
+ for e != nil {
+ c := e.Value.(*git.Commit)
+
+ uname := ""
+ if v, ok := emails[c.Author.Email]; !ok {
+ u, err := GetUserByEmail(c.Author.Email)
+ if err == nil {
+ uname = u.Name
+ }
+ emails[c.Author.Email] = uname
+ } else {
+ uname = v
+ }
+
+ newCommits.PushBack(UserCommit{
+ UserName: uname,
+ Commit: c,
+ })
+ e = e.Next()
+ }
+ return newCommits
+}
+
// GetUserByEmail returns the user object by given e-mail if exists.
func GetUserByEmail(email string) (*User, error) {
if len(email) == 0 {
@@ -548,27 +600,27 @@ type Follow struct {
// FollowUser marks someone be another's follower.
func FollowUser(userId int64, followId int64) (err error) {
- session := x.NewSession()
- defer session.Close()
- session.Begin()
+ sess := x.NewSession()
+ defer sess.Close()
+ sess.Begin()
- if _, err = session.Insert(&Follow{UserId: userId, FollowId: followId}); err != nil {
- session.Rollback()
+ if _, err = sess.Insert(&Follow{UserId: userId, FollowId: followId}); err != nil {
+ sess.Rollback()
return err
}
rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?"
- if _, err = session.Exec(rawSql, followId); err != nil {
- session.Rollback()
+ if _, err = sess.Exec(rawSql, followId); err != nil {
+ sess.Rollback()
return err
}
rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?"
- if _, err = session.Exec(rawSql, userId); err != nil {
- session.Rollback()
+ if _, err = sess.Exec(rawSql, userId); err != nil {
+ sess.Rollback()
return err
}
- return session.Commit()
+ return sess.Commit()
}
// UnFollowUser unmarks someone be another's follower.
diff --git a/models/webhook.go b/models/webhook.go
index ced79366..9508c98a 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -7,6 +7,7 @@ package models
import (
"encoding/json"
"errors"
+ "io/ioutil"
"time"
"github.com/gogits/gogs/modules/httplib"
@@ -33,15 +34,18 @@ type HookEvent struct {
// Webhook represents a web hook object.
type Webhook struct {
- Id int64
- RepoId int64
- Url string `xorm:"TEXT"`
- ContentType HookContentType
- Secret string `xorm:"TEXT"`
- Events string `xorm:"TEXT"`
- *HookEvent `xorm:"-"`
- IsSsl bool
- IsActive bool
+ Id int64
+ RepoId int64
+ Url string `xorm:"TEXT"`
+ ContentType HookContentType
+ Secret string `xorm:"TEXT"`
+ Events string `xorm:"TEXT"`
+ *HookEvent `xorm:"-"`
+ IsSsl bool
+ IsActive bool
+ HookTaskType HookTaskType
+ Meta string `xorm:"TEXT"` // store hook-specific attributes
+ OrgId int64
}
// GetEvent handles conversion from Events to HookEvent.
@@ -52,6 +56,14 @@ func (w *Webhook) GetEvent() {
}
}
+func (w *Webhook) GetSlackHook() *Slack {
+ s := &Slack{}
+ if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
+ log.Error(4, "webhook.GetSlackHook(%d): %v", w.Id, err)
+ }
+ return s
+}
+
// UpdateEvent handles conversion from HookEvent to Events.
func (w *Webhook) UpdateEvent() error {
data, err := json.Marshal(w.HookEvent)
@@ -87,7 +99,7 @@ func GetWebhookById(hookId int64) (*Webhook, error) {
// GetActiveWebhooksByRepoId returns all active webhooks of repository.
func GetActiveWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) {
- err = x.Find(&ws, &Webhook{RepoId: repoId, IsActive: true})
+ err = x.Where("repo_id=?", repoId).And("is_active=?", true).Find(&ws)
return ws, err
}
@@ -109,6 +121,18 @@ func DeleteWebhook(hookId int64) error {
return err
}
+// GetWebhooksByOrgId returns all webhooks for an organization.
+func GetWebhooksByOrgId(orgId int64) (ws []*Webhook, err error) {
+ err = x.Find(&ws, &Webhook{OrgId: orgId})
+ return ws, err
+}
+
+// GetActiveWebhooksByOrgId returns all active webhooks for an organization.
+func GetActiveWebhooksByOrgId(orgId int64) (ws []*Webhook, err error) {
+ err = x.Where("org_id=?", orgId).And("is_active=?", true).Find(&ws)
+ return ws, err
+}
+
// ___ ___ __ ___________ __
// / | \ ____ ____ | | _\__ ___/____ _____| | __
// / ~ \/ _ \ / _ \| |/ / | | \__ \ / ___/ |/ /
@@ -119,8 +143,8 @@ func DeleteWebhook(hookId int64) error {
type HookTaskType int
const (
- WEBHOOK HookTaskType = iota + 1
- SERVICE
+ GOGS HookTaskType = iota + 1
+ SLACK
)
type HookEventType string
@@ -130,8 +154,9 @@ const (
)
type PayloadAuthor struct {
- Name string `json:"name"`
- Email string `json:"email"`
+ Name string `json:"name"`
+ Email string `json:"email"`
+ UserName string `json:"username"`
}
type PayloadCommit struct {
@@ -148,17 +173,32 @@ type PayloadRepo struct {
Description string `json:"description"`
Website string `json:"website"`
Watchers int `json:"watchers"`
- Owner *PayloadAuthor `json:"author"`
+ Owner *PayloadAuthor `json:"owner"`
Private bool `json:"private"`
}
+type BasePayload interface {
+ GetJSONPayload() ([]byte, error)
+}
+
// Payload represents a payload information of hook.
type Payload struct {
- Secret string `json:"secret"`
- Ref string `json:"ref"`
- Commits []*PayloadCommit `json:"commits"`
- Repo *PayloadRepo `json:"repository"`
- Pusher *PayloadAuthor `json:"pusher"`
+ Secret string `json:"secret"`
+ Ref string `json:"ref"`
+ Commits []*PayloadCommit `json:"commits"`
+ Repo *PayloadRepo `json:"repository"`
+ Pusher *PayloadAuthor `json:"pusher"`
+ Before string `json:"before"`
+ After string `json:"after"`
+ CompareUrl string `json:"compare_url"`
+}
+
+func (p Payload) GetJSONPayload() ([]byte, error) {
+ data, err := json.Marshal(p)
+ if err != nil {
+ return []byte{}, err
+ }
+ return data, nil
}
// HookTask represents a hook task.
@@ -167,19 +207,19 @@ type HookTask struct {
Uuid string
Type HookTaskType
Url string
- *Payload `xorm:"-"`
+ BasePayload `xorm:"-"`
PayloadContent string `xorm:"TEXT"`
ContentType HookContentType
EventType HookEventType
IsSsl bool
- IsDeliveried bool
+ IsDelivered bool
IsSucceed bool
}
// CreateHookTask creates a new hook task,
// it handles conversion from Payload to PayloadContent.
func CreateHookTask(t *HookTask) error {
- data, err := json.Marshal(t.Payload)
+ data, err := t.BasePayload.GetJSONPayload()
if err != nil {
return err
}
@@ -191,14 +231,15 @@ func CreateHookTask(t *HookTask) error {
// UpdateHookTask updates information of hook task.
func UpdateHookTask(t *HookTask) error {
- _, err := x.AllCols().Update(t)
+ _, err := x.Id(t.Id).AllCols().Update(t)
return err
}
// DeliverHooks checks and delivers undelivered hooks.
func DeliverHooks() {
+ tasks := make([]*HookTask, 0, 10)
timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
- x.Where("is_deliveried=?", false).Iterate(new(HookTask),
+ x.Where("is_delivered=?", false).Iterate(new(HookTask),
func(idx int, bean interface{}) error {
t := bean.(*HookTask)
req := httplib.Post(t.Url).SetTimeout(timeout, timeout).
@@ -212,21 +253,50 @@ func DeliverHooks() {
req.Param("payload", t.PayloadContent)
}
- t.IsDeliveried = true
+ t.IsDelivered = true
// TODO: record response.
- if _, err := req.Response(); err != nil {
- log.Error(4, "Delivery: %v", err)
- } else {
- t.IsSucceed = true
+ switch t.Type {
+ case GOGS:
+ {
+ if _, err := req.Response(); err != nil {
+ log.Error(4, "Delivery: %v", err)
+ } else {
+ t.IsSucceed = true
+ }
+ }
+ case SLACK:
+ {
+ if res, err := req.Response(); err != nil {
+ log.Error(4, "Delivery: %v", err)
+ } else {
+ defer res.Body.Close()
+ contents, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ log.Error(4, "%s", err)
+ } else {
+ if string(contents) != "ok" {
+ log.Error(4, "slack failed with: %s", string(contents))
+ } else {
+ t.IsSucceed = true
+ }
+ }
+ }
+ }
}
- if err := UpdateHookTask(t); err != nil {
- log.Error(4, "UpdateHookTask: %v", err)
- return nil
- }
+ tasks = append(tasks, t)
- log.Trace("Hook delivered(%s): %s", t.Uuid, t.PayloadContent)
+ if t.IsSucceed {
+ log.Trace("Hook delivered(%s): %s", t.Uuid, t.PayloadContent)
+ }
return nil
})
+
+ // Update hook task status.
+ for _, t := range tasks {
+ if err := UpdateHookTask(t); err != nil {
+ log.Error(4, "UpdateHookTask(%d): %v", t.Id, err)
+ }
+ }
}