Batch hook pre- and post-receive calls (#8602)
* make notifyWatchers work on multiple actions * more efficient multiple notifyWatchers * Make CommitRepoAction take advantage of multiple actions * Batch post and pre-receive results * Set batch to 30 * Auto adjust timeout & add logging * adjust processing message * Add some messages to pre-receive * Make any non-200 status code from pre-receive an error * Add missing hookPrintResults * Remove shortcut for single action * mistaken merge fix * oops * Move master branch to the front * If repo was empty and the master branch is pushed ensure that that is set as the default branch * fixup * fixup * Missed HookOptions in setdefaultbranch * Batch PushUpdateAddTag and PushUpdateDelTag Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
114d474f02
commit
7bfb83e064
10 changed files with 1063 additions and 440 deletions
211
cmd/hook.go
211
cmd/hook.go
|
@ -21,6 +21,10 @@ import (
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
hookBatchSize = 30
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// CmdHook represents the available hooks sub-command.
|
// CmdHook represents the available hooks sub-command.
|
||||||
CmdHook = cli.Command{
|
CmdHook = cli.Command{
|
||||||
|
@ -75,12 +79,25 @@ Gitea or set your environment appropriately.`, "")
|
||||||
prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
|
prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
|
||||||
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
hookOptions := private.HookOptions{
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
UserID: userID,
|
||||||
for scanner.Scan() {
|
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||||
buf.Write(scanner.Bytes())
|
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||||
buf.WriteByte('\n')
|
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||||
|
ProtectedBranchID: prID,
|
||||||
|
IsDeployKey: isDeployKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
|
||||||
|
oldCommitIDs := make([]string, hookBatchSize)
|
||||||
|
newCommitIDs := make([]string, hookBatchSize)
|
||||||
|
refFullNames := make([]string, hookBatchSize)
|
||||||
|
count := 0
|
||||||
|
total := 0
|
||||||
|
lastline := 0
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
// TODO: support news feeds for wiki
|
// TODO: support news feeds for wiki
|
||||||
if isWiki {
|
if isWiki {
|
||||||
continue
|
continue
|
||||||
|
@ -94,28 +111,71 @@ Gitea or set your environment appropriately.`, "")
|
||||||
oldCommitID := string(fields[0])
|
oldCommitID := string(fields[0])
|
||||||
newCommitID := string(fields[1])
|
newCommitID := string(fields[1])
|
||||||
refFullName := string(fields[2])
|
refFullName := string(fields[2])
|
||||||
|
total++
|
||||||
|
lastline++
|
||||||
|
|
||||||
// If the ref is a branch, check if it's protected
|
// If the ref is a branch, check if it's protected
|
||||||
if strings.HasPrefix(refFullName, git.BranchPrefix) {
|
if strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||||
statusCode, msg := private.HookPreReceive(username, reponame, private.HookOptions{
|
oldCommitIDs[count] = oldCommitID
|
||||||
OldCommitID: oldCommitID,
|
newCommitIDs[count] = newCommitID
|
||||||
NewCommitID: newCommitID,
|
refFullNames[count] = refFullName
|
||||||
RefFullName: refFullName,
|
count++
|
||||||
UserID: userID,
|
fmt.Fprintf(os.Stdout, "*")
|
||||||
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
os.Stdout.Sync()
|
||||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
|
||||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
if count >= hookBatchSize {
|
||||||
ProtectedBranchID: prID,
|
fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
|
||||||
IsDeployKey: isDeployKey,
|
os.Stdout.Sync()
|
||||||
})
|
|
||||||
|
hookOptions.OldCommitIDs = oldCommitIDs
|
||||||
|
hookOptions.NewCommitIDs = newCommitIDs
|
||||||
|
hookOptions.RefFullNames = refFullNames
|
||||||
|
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
|
||||||
|
switch statusCode {
|
||||||
|
case http.StatusOK:
|
||||||
|
// no-op
|
||||||
|
case http.StatusInternalServerError:
|
||||||
|
fail("Internal Server Error", msg)
|
||||||
|
default:
|
||||||
|
fail(msg, "")
|
||||||
|
}
|
||||||
|
count = 0
|
||||||
|
lastline = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stdout, ".")
|
||||||
|
os.Stdout.Sync()
|
||||||
|
}
|
||||||
|
if lastline >= hookBatchSize {
|
||||||
|
fmt.Fprintf(os.Stdout, "\n")
|
||||||
|
os.Stdout.Sync()
|
||||||
|
lastline = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
hookOptions.OldCommitIDs = oldCommitIDs[:count]
|
||||||
|
hookOptions.NewCommitIDs = newCommitIDs[:count]
|
||||||
|
hookOptions.RefFullNames = refFullNames[:count]
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
|
||||||
|
os.Stdout.Sync()
|
||||||
|
|
||||||
|
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
fail("Internal Server Error", msg)
|
fail("Internal Server Error", msg)
|
||||||
case http.StatusForbidden:
|
case http.StatusForbidden:
|
||||||
fail(msg, "")
|
fail(msg, "")
|
||||||
}
|
}
|
||||||
|
} else if lastline > 0 {
|
||||||
|
fmt.Fprintf(os.Stdout, "\n")
|
||||||
|
os.Stdout.Sync()
|
||||||
|
lastline = 0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fmt.Fprintf(os.Stdout, "Checked %d references in total\n", total)
|
||||||
|
os.Stdout.Sync()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -156,12 +216,24 @@ Gitea or set your environment appropriately.`, "")
|
||||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||||
pusherName := os.Getenv(models.EnvPusherName)
|
pusherName := os.Getenv(models.EnvPusherName)
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
hookOptions := private.HookOptions{
|
||||||
|
UserName: pusherName,
|
||||||
|
UserID: pusherID,
|
||||||
|
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||||
|
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||||
|
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||||
|
}
|
||||||
|
oldCommitIDs := make([]string, hookBatchSize)
|
||||||
|
newCommitIDs := make([]string, hookBatchSize)
|
||||||
|
refFullNames := make([]string, hookBatchSize)
|
||||||
|
count := 0
|
||||||
|
total := 0
|
||||||
|
wasEmpty := false
|
||||||
|
masterPushed := false
|
||||||
|
results := make([]private.HookPostReceiveBranchResult, 0)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
buf.Write(scanner.Bytes())
|
|
||||||
buf.WriteByte('\n')
|
|
||||||
|
|
||||||
// TODO: support news feeds for wiki
|
// TODO: support news feeds for wiki
|
||||||
if isWiki {
|
if isWiki {
|
||||||
continue
|
continue
|
||||||
|
@ -172,36 +244,95 @@ Gitea or set your environment appropriately.`, "")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
oldCommitID := string(fields[0])
|
fmt.Fprintf(os.Stdout, ".")
|
||||||
newCommitID := string(fields[1])
|
oldCommitIDs[count] = string(fields[0])
|
||||||
refFullName := string(fields[2])
|
newCommitIDs[count] = string(fields[1])
|
||||||
|
refFullNames[count] = string(fields[2])
|
||||||
|
if refFullNames[count] == git.BranchPrefix+"master" && newCommitIDs[count] != git.EmptySHA && count == total {
|
||||||
|
masterPushed = true
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
total++
|
||||||
|
os.Stdout.Sync()
|
||||||
|
|
||||||
res, err := private.HookPostReceive(repoUser, repoName, private.HookOptions{
|
if count >= hookBatchSize {
|
||||||
OldCommitID: oldCommitID,
|
fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
|
||||||
NewCommitID: newCommitID,
|
os.Stdout.Sync()
|
||||||
RefFullName: refFullName,
|
hookOptions.OldCommitIDs = oldCommitIDs
|
||||||
UserID: pusherID,
|
hookOptions.NewCommitIDs = newCommitIDs
|
||||||
UserName: pusherName,
|
hookOptions.RefFullNames = refFullNames
|
||||||
})
|
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
|
||||||
|
if resp == nil {
|
||||||
if res == nil {
|
hookPrintResults(results)
|
||||||
fail("Internal Server Error", err)
|
fail("Internal Server Error", err)
|
||||||
}
|
}
|
||||||
|
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||||
|
results = append(results, resp.Results...)
|
||||||
|
count = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if res["message"] == false {
|
if count == 0 {
|
||||||
|
if wasEmpty && masterPushed {
|
||||||
|
// We need to tell the repo to reset the default branch to master
|
||||||
|
err := private.SetDefaultBranch(repoUser, repoName, "master")
|
||||||
|
if err != nil {
|
||||||
|
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
|
||||||
|
os.Stdout.Sync()
|
||||||
|
|
||||||
|
hookPrintResults(results)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hookOptions.OldCommitIDs = oldCommitIDs[:count]
|
||||||
|
hookOptions.NewCommitIDs = newCommitIDs[:count]
|
||||||
|
hookOptions.RefFullNames = refFullNames[:count]
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
|
||||||
|
os.Stdout.Sync()
|
||||||
|
|
||||||
|
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
|
||||||
|
if resp == nil {
|
||||||
|
hookPrintResults(results)
|
||||||
|
fail("Internal Server Error", err)
|
||||||
|
}
|
||||||
|
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||||
|
results = append(results, resp.Results...)
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
|
||||||
|
os.Stdout.Sync()
|
||||||
|
|
||||||
|
if wasEmpty && masterPushed {
|
||||||
|
// We need to tell the repo to reset the default branch to master
|
||||||
|
err := private.SetDefaultBranch(repoUser, repoName, "master")
|
||||||
|
if err != nil {
|
||||||
|
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hookPrintResults(results)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
|
||||||
|
for _, res := range results {
|
||||||
|
if !res.Message {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(os.Stderr, "")
|
fmt.Fprintln(os.Stderr, "")
|
||||||
if res["create"] == true {
|
if res.Create {
|
||||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res["branch"])
|
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res.Branch)
|
||||||
fmt.Fprintf(os.Stderr, " %s\n", res["url"])
|
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||||
fmt.Fprintf(os.Stderr, " %s\n", res["url"])
|
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, "")
|
fmt.Fprintln(os.Stderr, "")
|
||||||
|
os.Stderr.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,12 +164,24 @@ func (repo *Repository) GetWatchers(page int) ([]*User, error) {
|
||||||
return users, sess.Find(&users)
|
return users, sess.Find(&users)
|
||||||
}
|
}
|
||||||
|
|
||||||
func notifyWatchers(e Engine, act *Action) error {
|
func notifyWatchers(e Engine, actions ...*Action) error {
|
||||||
|
var watchers []*Watch
|
||||||
|
var repo *Repository
|
||||||
|
var err error
|
||||||
|
var permCode []bool
|
||||||
|
var permIssue []bool
|
||||||
|
var permPR []bool
|
||||||
|
|
||||||
|
for _, act := range actions {
|
||||||
|
repoChanged := repo == nil || repo.ID != act.RepoID
|
||||||
|
|
||||||
|
if repoChanged {
|
||||||
// Add feeds for user self and all watchers.
|
// Add feeds for user self and all watchers.
|
||||||
watches, err := getWatchers(e, act.RepoID)
|
watchers, err = getWatchers(e, act.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get watchers: %v", err)
|
return fmt.Errorf("get watchers: %v", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add feed for actioner.
|
// Add feed for actioner.
|
||||||
act.UserID = act.ActUserID
|
act.UserID = act.ActUserID
|
||||||
|
@ -177,11 +189,17 @@ func notifyWatchers(e Engine, act *Action) error {
|
||||||
return fmt.Errorf("insert new actioner: %v", err)
|
return fmt.Errorf("insert new actioner: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if repoChanged {
|
||||||
act.loadRepo()
|
act.loadRepo()
|
||||||
|
repo = act.Repo
|
||||||
|
|
||||||
// check repo owner exist.
|
// check repo owner exist.
|
||||||
if err := act.Repo.getOwner(e); err != nil {
|
if err := act.Repo.getOwner(e); err != nil {
|
||||||
return fmt.Errorf("can't get repo owner: %v", err)
|
return fmt.Errorf("can't get repo owner: %v", err)
|
||||||
}
|
}
|
||||||
|
} else if act.Repo == nil {
|
||||||
|
act.Repo = repo
|
||||||
|
}
|
||||||
|
|
||||||
// Add feed for organization
|
// Add feed for organization
|
||||||
if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
|
if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
|
||||||
|
@ -192,26 +210,50 @@ func notifyWatchers(e Engine, act *Action) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range watches {
|
if repoChanged {
|
||||||
if act.ActUserID == watches[i].UserID {
|
permCode = make([]bool, len(watchers))
|
||||||
|
permIssue = make([]bool, len(watchers))
|
||||||
|
permPR = make([]bool, len(watchers))
|
||||||
|
for i, watcher := range watchers {
|
||||||
|
user, err := getUserByID(e, watcher.UserID)
|
||||||
|
if err != nil {
|
||||||
|
permCode[i] = false
|
||||||
|
permIssue[i] = false
|
||||||
|
permPR[i] = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
perm, err := getUserRepoPermission(e, repo, user)
|
||||||
|
if err != nil {
|
||||||
|
permCode[i] = false
|
||||||
|
permIssue[i] = false
|
||||||
|
permPR[i] = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
permCode[i] = perm.CanRead(UnitTypeCode)
|
||||||
|
permIssue[i] = perm.CanRead(UnitTypeIssues)
|
||||||
|
permPR[i] = perm.CanRead(UnitTypePullRequests)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, watcher := range watchers {
|
||||||
|
if act.ActUserID == watcher.UserID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
act.ID = 0
|
act.ID = 0
|
||||||
act.UserID = watches[i].UserID
|
act.UserID = watcher.UserID
|
||||||
act.Repo.Units = nil
|
act.Repo.Units = nil
|
||||||
|
|
||||||
switch act.OpType {
|
switch act.OpType {
|
||||||
case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionDeleteBranch:
|
case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionDeleteBranch:
|
||||||
if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypeCode) {
|
if !permCode[i] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
|
case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
|
||||||
if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypeIssues) {
|
if !permIssue[i] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest:
|
case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest:
|
||||||
if !act.Repo.checkUnitUser(e, act.UserID, false, UnitTypePullRequests) {
|
if !permPR[i] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,12 +262,13 @@ func notifyWatchers(e Engine, act *Action) error {
|
||||||
return fmt.Errorf("insert new action: %v", err)
|
return fmt.Errorf("insert new action: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyWatchers creates batch of actions for every watcher.
|
// NotifyWatchers creates batch of actions for every watcher.
|
||||||
func NotifyWatchers(act *Action) error {
|
func NotifyWatchers(actions ...*Action) error {
|
||||||
return notifyWatchers(x, act)
|
return notifyWatchers(x, actions...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyWatchersActions creates batch of actions for every watcher.
|
// NotifyWatchersActions creates batch of actions for every watcher.
|
||||||
|
|
179
models/update.go
179
models/update.go
|
@ -53,6 +53,66 @@ func ListToPushCommits(l *list.List) *PushCommits {
|
||||||
return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*User)}
|
return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*User)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushUpdateAddDeleteTags updates a number of added and delete tags
|
||||||
|
func PushUpdateAddDeleteTags(repo *Repository, gitRepo *git.Repository, addTags, delTags []string) error {
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return fmt.Errorf("Unable to begin sess in PushUpdateDeleteTags: %v", err)
|
||||||
|
}
|
||||||
|
if err := pushUpdateDeleteTags(sess, repo, delTags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := pushUpdateAddTags(sess, repo, gitRepo, addTags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushUpdateDeleteTags updates a number of delete tags
|
||||||
|
func PushUpdateDeleteTags(repo *Repository, tags []string) error {
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return fmt.Errorf("Unable to begin sess in PushUpdateDeleteTags: %v", err)
|
||||||
|
}
|
||||||
|
if err := pushUpdateDeleteTags(sess, repo, tags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func pushUpdateDeleteTags(e Engine, repo *Repository, tags []string) error {
|
||||||
|
if len(tags) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
lowerTags := make([]string, 0, len(tags))
|
||||||
|
for _, tag := range tags {
|
||||||
|
lowerTags = append(lowerTags, strings.ToLower(tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := e.
|
||||||
|
Where("repo_id = ? AND is_tag = ?", repo.ID, true).
|
||||||
|
In("lower_tag_name", lowerTags).
|
||||||
|
Delete(new(Release)); err != nil {
|
||||||
|
return fmt.Errorf("Delete: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := e.
|
||||||
|
Where("repo_id = ? AND is_tag = ?", repo.ID, false).
|
||||||
|
In("lower_tag_name", lowerTags).
|
||||||
|
SetExpr("is_draft", true).
|
||||||
|
SetExpr("num_commits", 0).
|
||||||
|
SetExpr("sha1", "").
|
||||||
|
Update(new(Release)); err != nil {
|
||||||
|
return fmt.Errorf("Update: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// PushUpdateDeleteTag must be called for any push actions to delete tag
|
// PushUpdateDeleteTag must be called for any push actions to delete tag
|
||||||
func PushUpdateDeleteTag(repo *Repository, tagName string) error {
|
func PushUpdateDeleteTag(repo *Repository, tagName string) error {
|
||||||
rel, err := GetRelease(repo.ID, tagName)
|
rel, err := GetRelease(repo.ID, tagName)
|
||||||
|
@ -78,6 +138,125 @@ func PushUpdateDeleteTag(repo *Repository, tagName string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushUpdateAddTags updates a number of add tags
|
||||||
|
func PushUpdateAddTags(repo *Repository, gitRepo *git.Repository, tags []string) error {
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return fmt.Errorf("Unable to begin sess in PushUpdateAddTags: %v", err)
|
||||||
|
}
|
||||||
|
if err := pushUpdateAddTags(sess, repo, gitRepo, tags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
func pushUpdateAddTags(e Engine, repo *Repository, gitRepo *git.Repository, tags []string) error {
|
||||||
|
if len(tags) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lowerTags := make([]string, 0, len(tags))
|
||||||
|
for _, tag := range tags {
|
||||||
|
lowerTags = append(lowerTags, strings.ToLower(tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
releases := make([]Release, 0, len(tags))
|
||||||
|
if err := e.Where("repo_id = ?", repo.ID).
|
||||||
|
In("lower_tag_name", lowerTags).Find(&releases); err != nil {
|
||||||
|
return fmt.Errorf("GetRelease: %v", err)
|
||||||
|
}
|
||||||
|
relMap := make(map[string]*Release)
|
||||||
|
for _, rel := range releases {
|
||||||
|
relMap[rel.LowerTagName] = &rel
|
||||||
|
}
|
||||||
|
|
||||||
|
newReleases := make([]*Release, 0, len(lowerTags)-len(relMap))
|
||||||
|
|
||||||
|
emailToUser := make(map[string]*User)
|
||||||
|
|
||||||
|
for i, lowerTag := range lowerTags {
|
||||||
|
tag, err := gitRepo.GetTag(tags[i])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetTag: %v", err)
|
||||||
|
}
|
||||||
|
commit, err := tag.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Commit: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := tag.Tagger
|
||||||
|
if sig == nil {
|
||||||
|
sig = commit.Author
|
||||||
|
}
|
||||||
|
if sig == nil {
|
||||||
|
sig = commit.Committer
|
||||||
|
}
|
||||||
|
var author *User
|
||||||
|
var createdAt = time.Unix(1, 0)
|
||||||
|
|
||||||
|
if sig != nil {
|
||||||
|
var ok bool
|
||||||
|
author, ok = emailToUser[sig.Email]
|
||||||
|
if !ok {
|
||||||
|
author, err = GetUserByEmail(sig.Email)
|
||||||
|
if err != nil && !IsErrUserNotExist(err) {
|
||||||
|
return fmt.Errorf("GetUserByEmail: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createdAt = sig.When
|
||||||
|
}
|
||||||
|
|
||||||
|
commitsCount, err := commit.CommitsCount()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CommitsCount: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rel, has := relMap[lowerTag]
|
||||||
|
|
||||||
|
if !has {
|
||||||
|
rel = &Release{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Title: "",
|
||||||
|
TagName: tags[i],
|
||||||
|
LowerTagName: lowerTag,
|
||||||
|
Target: "",
|
||||||
|
Sha1: commit.ID.String(),
|
||||||
|
NumCommits: commitsCount,
|
||||||
|
Note: "",
|
||||||
|
IsDraft: false,
|
||||||
|
IsPrerelease: false,
|
||||||
|
IsTag: true,
|
||||||
|
CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
|
||||||
|
}
|
||||||
|
if author != nil {
|
||||||
|
rel.PublisherID = author.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
newReleases = append(newReleases, rel)
|
||||||
|
} else {
|
||||||
|
rel.Sha1 = commit.ID.String()
|
||||||
|
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
|
||||||
|
rel.NumCommits = commitsCount
|
||||||
|
rel.IsDraft = false
|
||||||
|
if rel.IsTag && author != nil {
|
||||||
|
rel.PublisherID = author.ID
|
||||||
|
}
|
||||||
|
if _, err = e.ID(rel.ID).AllCols().Update(rel); err != nil {
|
||||||
|
return fmt.Errorf("Update: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(newReleases) > 0 {
|
||||||
|
if _, err := e.Insert(newReleases); err != nil {
|
||||||
|
return fmt.Errorf("Insert: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// PushUpdateAddTag must be called for any push actions to add tag
|
// PushUpdateAddTag must be called for any push actions to add tag
|
||||||
func PushUpdateAddTag(repo *Repository, gitRepo *git.Repository, tagName string) error {
|
func PushUpdateAddTag(repo *Repository, gitRepo *git.Repository, tagName string) error {
|
||||||
rel, err := GetRelease(repo.ID, tagName)
|
rel, err := GetRelease(repo.ID, tagName)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
@ -22,9 +23,9 @@ const (
|
||||||
|
|
||||||
// HookOptions represents the options for the Hook calls
|
// HookOptions represents the options for the Hook calls
|
||||||
type HookOptions struct {
|
type HookOptions struct {
|
||||||
OldCommitID string
|
OldCommitIDs []string
|
||||||
NewCommitID string
|
NewCommitIDs []string
|
||||||
RefFullName string
|
RefFullNames []string
|
||||||
UserID int64
|
UserID int64
|
||||||
UserName string
|
UserName string
|
||||||
GitObjectDirectory string
|
GitObjectDirectory string
|
||||||
|
@ -34,23 +35,33 @@ type HookOptions struct {
|
||||||
IsDeployKey bool
|
IsDeployKey bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HookPostReceiveResult represents an individual result from PostReceive
|
||||||
|
type HookPostReceiveResult struct {
|
||||||
|
Results []HookPostReceiveBranchResult
|
||||||
|
RepoWasEmpty bool
|
||||||
|
Err string
|
||||||
|
}
|
||||||
|
|
||||||
|
// HookPostReceiveBranchResult represents an individual branch result from PostReceive
|
||||||
|
type HookPostReceiveBranchResult struct {
|
||||||
|
Message bool
|
||||||
|
Create bool
|
||||||
|
Branch string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
// HookPreReceive check whether the provided commits are allowed
|
// HookPreReceive check whether the provided commits are allowed
|
||||||
func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string) {
|
func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string) {
|
||||||
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s?old=%s&new=%s&ref=%s&userID=%d&gitObjectDirectory=%s&gitAlternativeObjectDirectories=%s&gitQuarantinePath=%s&prID=%d&isDeployKey=%t",
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s",
|
||||||
url.PathEscape(ownerName),
|
url.PathEscape(ownerName),
|
||||||
url.PathEscape(repoName),
|
url.PathEscape(repoName),
|
||||||
url.QueryEscape(opts.OldCommitID),
|
|
||||||
url.QueryEscape(opts.NewCommitID),
|
|
||||||
url.QueryEscape(opts.RefFullName),
|
|
||||||
opts.UserID,
|
|
||||||
url.QueryEscape(opts.GitObjectDirectory),
|
|
||||||
url.QueryEscape(opts.GitAlternativeObjectDirectories),
|
|
||||||
url.QueryEscape(opts.GitQuarantinePath),
|
|
||||||
opts.ProtectedBranchID,
|
|
||||||
opts.IsDeployKey,
|
|
||||||
)
|
)
|
||||||
|
req := newInternalRequest(reqURL, "POST")
|
||||||
resp, err := newInternalRequest(reqURL, "GET").Response()
|
req = req.Header("Content-Type", "application/json")
|
||||||
|
jsonBytes, _ := json.Marshal(opts)
|
||||||
|
req.Body(jsonBytes)
|
||||||
|
req.SetTimeout(60*time.Second, time.Duration(60+len(opts.OldCommitIDs))*time.Second)
|
||||||
|
resp, err := req.Response()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
@ -64,17 +75,18 @@ func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HookPostReceive updates services and users
|
// HookPostReceive updates services and users
|
||||||
func HookPostReceive(ownerName, repoName string, opts HookOptions) (map[string]interface{}, string) {
|
func HookPostReceive(ownerName, repoName string, opts HookOptions) (*HookPostReceiveResult, string) {
|
||||||
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s?old=%s&new=%s&ref=%s&userID=%d&username=%s",
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s",
|
||||||
url.PathEscape(ownerName),
|
url.PathEscape(ownerName),
|
||||||
url.PathEscape(repoName),
|
url.PathEscape(repoName),
|
||||||
url.QueryEscape(opts.OldCommitID),
|
)
|
||||||
url.QueryEscape(opts.NewCommitID),
|
|
||||||
url.QueryEscape(opts.RefFullName),
|
|
||||||
opts.UserID,
|
|
||||||
url.QueryEscape(opts.UserName))
|
|
||||||
|
|
||||||
resp, err := newInternalRequest(reqURL, "GET").Response()
|
req := newInternalRequest(reqURL, "POST")
|
||||||
|
req = req.Header("Content-Type", "application/json")
|
||||||
|
req.SetTimeout(60*time.Second, time.Duration(60+len(opts.OldCommitIDs))*time.Second)
|
||||||
|
jsonBytes, _ := json.Marshal(opts)
|
||||||
|
req.Body(jsonBytes)
|
||||||
|
resp, err := req.Response()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
return nil, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
@ -83,8 +95,30 @@ func HookPostReceive(ownerName, repoName string, opts HookOptions) (map[string]i
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, decodeJSONError(resp).Err
|
return nil, decodeJSONError(resp).Err
|
||||||
}
|
}
|
||||||
res := map[string]interface{}{}
|
res := &HookPostReceiveResult{}
|
||||||
_ = json.NewDecoder(resp.Body).Decode(&res)
|
_ = json.NewDecoder(resp.Body).Decode(res)
|
||||||
|
|
||||||
return res, ""
|
return res, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDefaultBranch will set the default branch to the provided branch for the provided repository
|
||||||
|
func SetDefaultBranch(ownerName, repoName, branch string) error {
|
||||||
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/set-default-branch/%s/%s/%s",
|
||||||
|
url.PathEscape(ownerName),
|
||||||
|
url.PathEscape(repoName),
|
||||||
|
url.PathEscape(branch),
|
||||||
|
)
|
||||||
|
req := newInternalRequest(reqURL, "POST")
|
||||||
|
req = req.Header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
req.SetTimeout(60*time.Second, 60*time.Second)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to contact gitea: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("Error returned from gitea: %v", decodeJSONError(resp).Err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -159,17 +159,33 @@ type CommitRepoActionOptions struct {
|
||||||
|
|
||||||
// CommitRepoAction adds new commit action to the repository, and prepare
|
// CommitRepoAction adds new commit action to the repository, and prepare
|
||||||
// corresponding webhooks.
|
// corresponding webhooks.
|
||||||
func CommitRepoAction(opts CommitRepoActionOptions) error {
|
func CommitRepoAction(optsList ...*CommitRepoActionOptions) error {
|
||||||
pusher, err := models.GetUserByName(opts.PusherName)
|
var pusher *models.User
|
||||||
|
var repo *models.Repository
|
||||||
|
actions := make([]*models.Action, len(optsList))
|
||||||
|
|
||||||
|
for i, opts := range optsList {
|
||||||
|
if pusher == nil || pusher.Name != opts.PusherName {
|
||||||
|
var err error
|
||||||
|
pusher, err = models.GetUserByName(opts.PusherName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err)
|
return fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
repo, err := models.GetRepositoryByName(opts.RepoOwnerID, opts.RepoName)
|
if repo == nil || repo.OwnerID != opts.RepoOwnerID || repo.Name != opts.RepoName {
|
||||||
|
var err error
|
||||||
|
if repo != nil {
|
||||||
|
// Change repository empty status and update last updated time.
|
||||||
|
if err := models.UpdateRepository(repo, false); err != nil {
|
||||||
|
return fmt.Errorf("UpdateRepository: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repo, err = models.GetRepositoryByName(opts.RepoOwnerID, opts.RepoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
|
return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
refName := git.RefEndName(opts.RefFullName)
|
refName := git.RefEndName(opts.RefFullName)
|
||||||
|
|
||||||
// Change default branch and empty status only if pushed ref is non-empty branch.
|
// Change default branch and empty status only if pushed ref is non-empty branch.
|
||||||
|
@ -191,13 +207,9 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change repository empty status and update last updated time.
|
|
||||||
if err = models.UpdateRepository(repo, false); err != nil {
|
|
||||||
return fmt.Errorf("UpdateRepository: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
isNewBranch := false
|
isNewBranch := false
|
||||||
opType := models.ActionCommitRepo
|
opType := models.ActionCommitRepo
|
||||||
|
|
||||||
// Check it's tag push or branch.
|
// Check it's tag push or branch.
|
||||||
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||||
opType = models.ActionPushTag
|
opType = models.ActionPushTag
|
||||||
|
@ -216,7 +228,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||||
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits, refName); err != nil {
|
if err := UpdateIssuesCommit(pusher, repo, opts.Commits.Commits, refName); err != nil {
|
||||||
log.Error("updateIssuesCommit: %v", err)
|
log.Error("updateIssuesCommit: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +242,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||||
return fmt.Errorf("Marshal: %v", err)
|
return fmt.Errorf("Marshal: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = models.NotifyWatchers(&models.Action{
|
actions[i] = &models.Action{
|
||||||
ActUserID: pusher.ID,
|
ActUserID: pusher.ID,
|
||||||
ActUser: pusher,
|
ActUser: pusher,
|
||||||
OpType: opType,
|
OpType: opType,
|
||||||
|
@ -239,8 +251,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
RefName: refName,
|
RefName: refName,
|
||||||
IsPrivate: repo.IsPrivate,
|
IsPrivate: repo.IsPrivate,
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("NotifyWatchers: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var isHookEventPush = true
|
var isHookEventPush = true
|
||||||
|
@ -249,7 +259,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||||
if isNewBranch {
|
if isNewBranch {
|
||||||
notification.NotifyCreateRef(pusher, repo, "branch", opts.RefFullName)
|
notification.NotifyCreateRef(pusher, repo, "branch", opts.RefFullName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case models.ActionDeleteBranch: // Delete Branch
|
case models.ActionDeleteBranch: // Delete Branch
|
||||||
notification.NotifyDeleteRef(pusher, repo, "branch", opts.RefFullName)
|
notification.NotifyDeleteRef(pusher, repo, "branch", opts.RefFullName)
|
||||||
|
|
||||||
|
@ -265,6 +274,17 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||||
if isHookEventPush {
|
if isHookEventPush {
|
||||||
notification.NotifyPushCommits(pusher, repo, opts.RefFullName, opts.OldCommitID, opts.NewCommitID, opts.Commits)
|
notification.NotifyPushCommits(pusher, repo, opts.RefFullName, opts.OldCommitID, opts.NewCommitID, opts.Commits)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo != nil {
|
||||||
|
// Change repository empty status and update last updated time.
|
||||||
|
if err := models.UpdateRepository(repo, false); err != nil {
|
||||||
|
return fmt.Errorf("UpdateRepository: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := models.NotifyWatchers(actions...); err != nil {
|
||||||
|
return fmt.Errorf("NotifyWatchers: %v", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testCorrectRepoAction(t *testing.T, opts CommitRepoActionOptions, actionBean *models.Action) {
|
func testCorrectRepoAction(t *testing.T, opts *CommitRepoActionOptions, actionBean *models.Action) {
|
||||||
models.AssertNotExistsBean(t, actionBean)
|
models.AssertNotExistsBean(t, actionBean)
|
||||||
assert.NoError(t, CommitRepoAction(opts))
|
assert.NoError(t, CommitRepoAction(opts))
|
||||||
models.AssertExistsAndLoadBean(t, actionBean)
|
models.AssertExistsAndLoadBean(t, actionBean)
|
||||||
|
@ -121,7 +121,7 @@ func TestCommitRepoAction(t *testing.T) {
|
||||||
s.action.Repo = repo
|
s.action.Repo = repo
|
||||||
s.action.IsPrivate = repo.IsPrivate
|
s.action.IsPrivate = repo.IsPrivate
|
||||||
|
|
||||||
testCorrectRepoAction(t, s.commitRepoActionOptions, &s.action)
|
testCorrectRepoAction(t, &s.commitRepoActionOptions, &s.action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -432,6 +432,7 @@ type PushUpdateOptions struct {
|
||||||
RefFullName string
|
RefFullName string
|
||||||
OldCommitID string
|
OldCommitID string
|
||||||
NewCommitID string
|
NewCommitID string
|
||||||
|
Branch string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushUpdate must be called for any push actions in order to
|
// PushUpdate must be called for any push actions in order to
|
||||||
|
@ -460,60 +461,12 @@ func PushUpdate(repo *models.Repository, branch string, opts PushUpdateOptions)
|
||||||
log.Error("Failed to update size for repository: %v", err)
|
log.Error("Failed to update size for repository: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var commits = &models.PushCommits{}
|
commitRepoActionOptions, err := createCommitRepoActionOption(repo, gitRepo, &opts)
|
||||||
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
|
||||||
// If is tag reference
|
|
||||||
tagName := opts.RefFullName[len(git.TagPrefix):]
|
|
||||||
if isDelRef {
|
|
||||||
err = models.PushUpdateDeleteTag(repo, tagName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("PushUpdateDeleteTag: %v", err)
|
return err
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Clear cache for tag commit count
|
|
||||||
cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
|
|
||||||
err = models.PushUpdateAddTag(repo, gitRepo, tagName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("PushUpdateAddTag: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if !isDelRef {
|
|
||||||
// If is branch reference
|
|
||||||
|
|
||||||
// Clear cache for branch commit count
|
|
||||||
cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
|
|
||||||
|
|
||||||
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("gitRepo.GetCommit: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push new branch.
|
if err := CommitRepoAction(commitRepoActionOptions); err != nil {
|
||||||
var l *list.List
|
|
||||||
if isNewRef {
|
|
||||||
l, err = newCommit.CommitsBeforeLimit(10)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commits = models.ListToPushCommits(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
|
||||||
PusherName: opts.PusherName,
|
|
||||||
RepoOwnerID: repo.OwnerID,
|
|
||||||
RepoName: repo.Name,
|
|
||||||
RefFullName: opts.RefFullName,
|
|
||||||
OldCommitID: opts.OldCommitID,
|
|
||||||
NewCommitID: opts.NewCommitID,
|
|
||||||
Commits: commits,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("CommitRepoAction: %v", err)
|
return fmt.Errorf("CommitRepoAction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,3 +485,174 @@ func PushUpdate(repo *models.Repository, branch string, opts PushUpdateOptions)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushUpdates generates push action history feeds for push updating multiple refs
|
||||||
|
func PushUpdates(repo *models.Repository, optsList []*PushUpdateOptions) error {
|
||||||
|
repoPath := repo.RepoPath()
|
||||||
|
_, err := git.NewCommand("update-server-info").RunInDir(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
|
||||||
|
}
|
||||||
|
gitRepo, err := git.OpenRepository(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("OpenRepository: %v", err)
|
||||||
|
}
|
||||||
|
if err = repo.UpdateSize(); err != nil {
|
||||||
|
log.Error("Failed to update size for repository: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actions, err := createCommitRepoActions(repo, gitRepo, optsList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := CommitRepoAction(actions...); err != nil {
|
||||||
|
return fmt.Errorf("CommitRepoAction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pusher *models.User
|
||||||
|
|
||||||
|
for _, opts := range optsList {
|
||||||
|
if pusher == nil || pusher.ID != opts.PusherID {
|
||||||
|
var err error
|
||||||
|
pusher, err = models.GetUserByID(opts.PusherID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, opts.Branch, pusher.Name)
|
||||||
|
|
||||||
|
go pull_service.AddTestPullRequestTask(pusher, repo.ID, opts.Branch, true)
|
||||||
|
|
||||||
|
if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
|
||||||
|
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createCommitRepoActions(repo *models.Repository, gitRepo *git.Repository, optsList []*PushUpdateOptions) ([]*CommitRepoActionOptions, error) {
|
||||||
|
addTags := make([]string, 0, len(optsList))
|
||||||
|
delTags := make([]string, 0, len(optsList))
|
||||||
|
actions := make([]*CommitRepoActionOptions, 0, len(optsList))
|
||||||
|
|
||||||
|
for _, opts := range optsList {
|
||||||
|
isNewRef := opts.OldCommitID == git.EmptySHA
|
||||||
|
isDelRef := opts.NewCommitID == git.EmptySHA
|
||||||
|
if isNewRef && isDelRef {
|
||||||
|
return nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
|
||||||
|
}
|
||||||
|
var commits = &models.PushCommits{}
|
||||||
|
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||||
|
// If is tag reference
|
||||||
|
tagName := opts.RefFullName[len(git.TagPrefix):]
|
||||||
|
if isDelRef {
|
||||||
|
delTags = append(delTags, tagName)
|
||||||
|
} else {
|
||||||
|
cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
|
||||||
|
addTags = append(addTags, tagName)
|
||||||
|
}
|
||||||
|
} else if !isDelRef {
|
||||||
|
// If is branch reference
|
||||||
|
|
||||||
|
// Clear cache for branch commit count
|
||||||
|
cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
|
||||||
|
|
||||||
|
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("gitRepo.GetCommit: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push new branch.
|
||||||
|
var l *list.List
|
||||||
|
if isNewRef {
|
||||||
|
l, err = newCommit.CommitsBeforeLimit(10)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commits = models.ListToPushCommits(l)
|
||||||
|
}
|
||||||
|
actions = append(actions, &CommitRepoActionOptions{
|
||||||
|
PusherName: opts.PusherName,
|
||||||
|
RepoOwnerID: repo.OwnerID,
|
||||||
|
RepoName: repo.Name,
|
||||||
|
RefFullName: opts.RefFullName,
|
||||||
|
OldCommitID: opts.OldCommitID,
|
||||||
|
NewCommitID: opts.NewCommitID,
|
||||||
|
Commits: commits,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := models.PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil {
|
||||||
|
return nil, fmt.Errorf("PushUpdateAddDeleteTags: %v", err)
|
||||||
|
}
|
||||||
|
return actions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createCommitRepoActionOption(repo *models.Repository, gitRepo *git.Repository, opts *PushUpdateOptions) (*CommitRepoActionOptions, error) {
|
||||||
|
isNewRef := opts.OldCommitID == git.EmptySHA
|
||||||
|
isDelRef := opts.NewCommitID == git.EmptySHA
|
||||||
|
if isNewRef && isDelRef {
|
||||||
|
return nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
|
||||||
|
}
|
||||||
|
|
||||||
|
var commits = &models.PushCommits{}
|
||||||
|
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||||
|
// If is tag reference
|
||||||
|
tagName := opts.RefFullName[len(git.TagPrefix):]
|
||||||
|
if isDelRef {
|
||||||
|
if err := models.PushUpdateDeleteTag(repo, tagName); err != nil {
|
||||||
|
return nil, fmt.Errorf("PushUpdateDeleteTag: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Clear cache for tag commit count
|
||||||
|
cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
|
||||||
|
if err := models.PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
||||||
|
return nil, fmt.Errorf("PushUpdateAddTag: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !isDelRef {
|
||||||
|
// If is branch reference
|
||||||
|
|
||||||
|
// Clear cache for branch commit count
|
||||||
|
cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
|
||||||
|
|
||||||
|
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("gitRepo.GetCommit: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push new branch.
|
||||||
|
var l *list.List
|
||||||
|
if isNewRef {
|
||||||
|
l, err = newCommit.CommitsBeforeLimit(10)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commits = models.ListToPushCommits(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CommitRepoActionOptions{
|
||||||
|
PusherName: opts.PusherName,
|
||||||
|
RepoOwnerID: repo.OwnerID,
|
||||||
|
RepoName: repo.Name,
|
||||||
|
RefFullName: opts.RefFullName,
|
||||||
|
OldCommitID: opts.OldCommitID,
|
||||||
|
NewCommitID: opts.NewCommitID,
|
||||||
|
Commits: commits,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -197,11 +197,11 @@ func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) erro
|
||||||
}
|
}
|
||||||
commitID, err := gitRepo.GetTagCommitID(rel.TagName)
|
commitID, err := gitRepo.GetTagCommitID(rel.TagName)
|
||||||
if err != nil && !git.IsErrNotExist(err) {
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
return fmt.Errorf("GetTagCommitID: %v", err)
|
return fmt.Errorf("GetTagCommitID: %s: %v", rel.TagName, err)
|
||||||
}
|
}
|
||||||
if git.IsErrNotExist(err) || commitID != rel.Sha1 {
|
if git.IsErrNotExist(err) || commitID != rel.Sha1 {
|
||||||
if err := models.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
|
if err := models.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
|
||||||
return fmt.Errorf("PushUpdateDeleteTag: %v", err)
|
return fmt.Errorf("PushUpdateDeleteTag: %s: %v", rel.TagName, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
existingRelTags[strings.ToLower(rel.TagName)] = struct{}{}
|
existingRelTags[strings.ToLower(rel.TagName)] = struct{}{}
|
||||||
|
@ -215,7 +215,7 @@ func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) erro
|
||||||
for _, tagName := range tags {
|
for _, tagName := range tags {
|
||||||
if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok {
|
if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok {
|
||||||
if err := models.PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
if err := models.PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
||||||
return fmt.Errorf("pushUpdateAddTag: %v", err)
|
return fmt.Errorf("pushUpdateAddTag: %s: %v", tagName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,20 +22,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// HookPreReceive checks whether a individual commit is acceptable
|
// HookPreReceive checks whether a individual commit is acceptable
|
||||||
func HookPreReceive(ctx *macaron.Context) {
|
func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) {
|
||||||
ownerName := ctx.Params(":owner")
|
ownerName := ctx.Params(":owner")
|
||||||
repoName := ctx.Params(":repo")
|
repoName := ctx.Params(":repo")
|
||||||
oldCommitID := ctx.QueryTrim("old")
|
|
||||||
newCommitID := ctx.QueryTrim("new")
|
|
||||||
refFullName := ctx.QueryTrim("ref")
|
|
||||||
userID := ctx.QueryInt64("userID")
|
|
||||||
gitObjectDirectory := ctx.QueryTrim("gitObjectDirectory")
|
|
||||||
gitAlternativeObjectDirectories := ctx.QueryTrim("gitAlternativeObjectDirectories")
|
|
||||||
gitQuarantinePath := ctx.QueryTrim("gitQuarantinePath")
|
|
||||||
prID := ctx.QueryInt64("prID")
|
|
||||||
isDeployKey := ctx.QueryBool("isDeployKey")
|
|
||||||
|
|
||||||
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
|
||||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
log.Error("Unable to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
||||||
|
@ -45,6 +34,13 @@ func HookPreReceive(ctx *macaron.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
repo.OwnerName = ownerName
|
repo.OwnerName = ownerName
|
||||||
|
|
||||||
|
for i := range opts.OldCommitIDs {
|
||||||
|
oldCommitID := opts.OldCommitIDs[i]
|
||||||
|
newCommitID := opts.NewCommitIDs[i]
|
||||||
|
refFullName := opts.RefFullNames[i]
|
||||||
|
|
||||||
|
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||||
protectBranch, err := models.GetProtectedBranchBy(repo.ID, branchName)
|
protectBranch, err := models.GetProtectedBranchBy(repo.ID, branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to get protected branch: %s in %-v Error: %v", branchName, repo, err)
|
log.Error("Unable to get protected branch: %s in %-v Error: %v", branchName, repo, err)
|
||||||
|
@ -66,17 +62,17 @@ func HookPreReceive(ctx *macaron.Context) {
|
||||||
// detect force push
|
// detect force push
|
||||||
if git.EmptySHA != oldCommitID {
|
if git.EmptySHA != oldCommitID {
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
if gitAlternativeObjectDirectories != "" {
|
if opts.GitAlternativeObjectDirectories != "" {
|
||||||
env = append(env,
|
env = append(env,
|
||||||
private.GitAlternativeObjectDirectories+"="+gitAlternativeObjectDirectories)
|
private.GitAlternativeObjectDirectories+"="+opts.GitAlternativeObjectDirectories)
|
||||||
}
|
}
|
||||||
if gitObjectDirectory != "" {
|
if opts.GitObjectDirectory != "" {
|
||||||
env = append(env,
|
env = append(env,
|
||||||
private.GitObjectDirectory+"="+gitObjectDirectory)
|
private.GitObjectDirectory+"="+opts.GitObjectDirectory)
|
||||||
}
|
}
|
||||||
if gitQuarantinePath != "" {
|
if opts.GitQuarantinePath != "" {
|
||||||
env = append(env,
|
env = append(env,
|
||||||
private.GitQuarantinePath+"="+gitQuarantinePath)
|
private.GitQuarantinePath+"="+opts.GitQuarantinePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDirWithEnv(repo.RepoPath(), env)
|
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDirWithEnv(repo.RepoPath(), env)
|
||||||
|
@ -95,55 +91,57 @@ func HookPreReceive(ctx *macaron.Context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canPush := false
|
canPush := false
|
||||||
if isDeployKey {
|
if opts.IsDeployKey {
|
||||||
canPush = protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
|
canPush = protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
|
||||||
} else {
|
} else {
|
||||||
canPush = protectBranch.CanUserPush(userID)
|
canPush = protectBranch.CanUserPush(opts.UserID)
|
||||||
}
|
}
|
||||||
if !canPush && prID > 0 {
|
if !canPush && opts.ProtectedBranchID > 0 {
|
||||||
pr, err := models.GetPullRequestByID(prID)
|
pr, err := models.GetPullRequestByID(opts.ProtectedBranchID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to get PullRequest %d Error: %v", prID, err)
|
log.Error("Unable to get PullRequest %d Error: %v", opts.ProtectedBranchID, err)
|
||||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
"err": fmt.Sprintf("Unable to get PullRequest %d Error: %v", prID, err),
|
"err": fmt.Sprintf("Unable to get PullRequest %d Error: %v", opts.ProtectedBranchID, err),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !protectBranch.HasEnoughApprovals(pr) {
|
if !protectBranch.HasEnoughApprovals(pr) {
|
||||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v and pr #%d does not have enough approvals", userID, branchName, repo, pr.Index)
|
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v and pr #%d does not have enough approvals", opts.UserID, branchName, repo, pr.Index)
|
||||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||||
"err": fmt.Sprintf("protected branch %s can not be pushed to and pr #%d does not have enough approvals", branchName, prID),
|
"err": fmt.Sprintf("protected branch %s can not be pushed to and pr #%d does not have enough approvals", branchName, opts.ProtectedBranchID),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if !canPush {
|
} else if !canPush {
|
||||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v", userID, branchName, repo)
|
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v", opts.UserID, branchName, repo)
|
||||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||||
"err": fmt.Sprintf("protected branch %s can not be pushed to", branchName),
|
"err": fmt.Sprintf("protected branch %s can not be pushed to", branchName),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.PlainText(http.StatusOK, []byte("ok"))
|
ctx.PlainText(http.StatusOK, []byte("ok"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HookPostReceive updates services and users
|
// HookPostReceive updates services and users
|
||||||
func HookPostReceive(ctx *macaron.Context) {
|
func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) {
|
||||||
ownerName := ctx.Params(":owner")
|
ownerName := ctx.Params(":owner")
|
||||||
repoName := ctx.Params(":repo")
|
repoName := ctx.Params(":repo")
|
||||||
oldCommitID := ctx.Query("old")
|
|
||||||
newCommitID := ctx.Query("new")
|
|
||||||
refFullName := ctx.Query("ref")
|
|
||||||
userID := ctx.QueryInt64("userID")
|
|
||||||
userName := ctx.Query("username")
|
|
||||||
|
|
||||||
branch := refFullName
|
var repo *models.Repository
|
||||||
if strings.HasPrefix(refFullName, git.BranchPrefix) {
|
updates := make([]*repofiles.PushUpdateOptions, 0, len(opts.OldCommitIDs))
|
||||||
branch = strings.TrimPrefix(refFullName, git.BranchPrefix)
|
wasEmpty := false
|
||||||
} else if strings.HasPrefix(refFullName, git.TagPrefix) {
|
|
||||||
branch = strings.TrimPrefix(refFullName, git.TagPrefix)
|
for i := range opts.OldCommitIDs {
|
||||||
|
refFullName := opts.RefFullNames[i]
|
||||||
|
branch := opts.RefFullNames[i]
|
||||||
|
if strings.HasPrefix(branch, git.BranchPrefix) {
|
||||||
|
branch = strings.TrimPrefix(branch, git.BranchPrefix)
|
||||||
|
} else {
|
||||||
|
branch = strings.TrimPrefix(branch, git.TagPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only trigger activity updates for changes to branches or
|
// Only trigger activity updates for changes to branches or
|
||||||
|
@ -151,75 +149,118 @@ func HookPostReceive(ctx *macaron.Context) {
|
||||||
// or other less-standard refs spaces are ignored since there
|
// or other less-standard refs spaces are ignored since there
|
||||||
// may be a very large number of them).
|
// may be a very large number of them).
|
||||||
if strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
|
if strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
|
||||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
if repo == nil {
|
||||||
|
var err error
|
||||||
|
repo, err = models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
||||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||||
"err": fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
|
Err: fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := repofiles.PushUpdate(repo, branch, repofiles.PushUpdateOptions{
|
if repo.OwnerName == "" {
|
||||||
|
repo.OwnerName = ownerName
|
||||||
|
}
|
||||||
|
wasEmpty = repo.IsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
option := repofiles.PushUpdateOptions{
|
||||||
RefFullName: refFullName,
|
RefFullName: refFullName,
|
||||||
OldCommitID: oldCommitID,
|
OldCommitID: opts.OldCommitIDs[i],
|
||||||
NewCommitID: newCommitID,
|
NewCommitID: opts.NewCommitIDs[i],
|
||||||
PusherID: userID,
|
Branch: branch,
|
||||||
PusherName: userName,
|
PusherID: opts.UserID,
|
||||||
|
PusherName: opts.UserName,
|
||||||
RepoUserName: ownerName,
|
RepoUserName: ownerName,
|
||||||
RepoName: repoName,
|
RepoName: repoName,
|
||||||
}); err != nil {
|
}
|
||||||
log.Error("Failed to Update: %s/%s Branch: %s Error: %v", ownerName, repoName, branch, err)
|
updates = append(updates, &option)
|
||||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
if repo.IsEmpty && branch == "master" && strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||||
"err": fmt.Sprintf("Failed to Update: %s/%s Branch: %s Error: %v", ownerName, repoName, branch, err),
|
// put the master branch first
|
||||||
|
copy(updates[1:], updates)
|
||||||
|
updates[0] = &option
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo != nil && len(updates) > 0 {
|
||||||
|
if err := repofiles.PushUpdates(repo, updates); err != nil {
|
||||||
|
log.Error("Failed to Update: %s/%s Total Updates: %d", ownerName, repoName, len(updates))
|
||||||
|
for i, update := range updates {
|
||||||
|
log.Error("Failed to Update: %s/%s Update: %d/%d: Branch: %s", ownerName, repoName, i, len(updates), update.Branch)
|
||||||
|
}
|
||||||
|
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||||
|
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results := make([]private.HookPostReceiveBranchResult, 0, len(opts.OldCommitIDs))
|
||||||
|
|
||||||
|
// We have to reload the repo in case its state is changed above
|
||||||
|
repo = nil
|
||||||
|
var baseRepo *models.Repository
|
||||||
|
|
||||||
|
for i := range opts.OldCommitIDs {
|
||||||
|
refFullName := opts.RefFullNames[i]
|
||||||
|
newCommitID := opts.NewCommitIDs[i]
|
||||||
|
|
||||||
|
branch := git.RefEndName(opts.RefFullNames[i])
|
||||||
|
|
||||||
if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) {
|
if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
if repo == nil {
|
||||||
|
var err error
|
||||||
|
repo, err = models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
||||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||||
"err": fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
|
Err: fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||||
|
RepoWasEmpty: wasEmpty,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if repo.OwnerName == "" {
|
||||||
repo.OwnerName = ownerName
|
repo.OwnerName = ownerName
|
||||||
|
}
|
||||||
|
|
||||||
pullRequestAllowed := repo.AllowsPulls()
|
if !repo.AllowsPulls() {
|
||||||
if !pullRequestAllowed {
|
// We can stop there's no need to go any further
|
||||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
ctx.JSON(http.StatusOK, private.HookPostReceiveResult{
|
||||||
"message": false,
|
RepoWasEmpty: wasEmpty,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
baseRepo = repo
|
||||||
|
|
||||||
baseRepo := repo
|
|
||||||
if repo.IsFork {
|
if repo.IsFork {
|
||||||
if err := repo.GetBaseRepo(); err != nil {
|
if err := repo.GetBaseRepo(); err != nil {
|
||||||
log.Error("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err)
|
log.Error("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err)
|
||||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||||
"err": fmt.Sprintf("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err),
|
Err: fmt.Sprintf("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err),
|
||||||
|
RepoWasEmpty: wasEmpty,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
baseRepo = repo.BaseRepo
|
baseRepo = repo.BaseRepo
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !repo.IsFork && branch == baseRepo.DefaultBranch {
|
if !repo.IsFork && branch == baseRepo.DefaultBranch {
|
||||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
results = append(results, private.HookPostReceiveBranchResult{})
|
||||||
"message": false,
|
continue
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, err := models.GetUnmergedPullRequest(repo.ID, baseRepo.ID, branch, baseRepo.DefaultBranch)
|
pr, err := models.GetUnmergedPullRequest(repo.ID, baseRepo.ID, branch, baseRepo.DefaultBranch)
|
||||||
if err != nil && !models.IsErrPullRequestNotExist(err) {
|
if err != nil && !models.IsErrPullRequestNotExist(err) {
|
||||||
log.Error("Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err)
|
log.Error("Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err)
|
||||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||||
"err": fmt.Sprintf(
|
Err: fmt.Sprintf(
|
||||||
"Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err),
|
"Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err),
|
||||||
|
RepoWasEmpty: wasEmpty,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -228,23 +269,69 @@ func HookPostReceive(ctx *macaron.Context) {
|
||||||
if repo.IsFork {
|
if repo.IsFork {
|
||||||
branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
|
branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
|
||||||
}
|
}
|
||||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
results = append(results, private.HookPostReceiveBranchResult{
|
||||||
"message": true,
|
Message: true,
|
||||||
"create": true,
|
Create: true,
|
||||||
"branch": branch,
|
Branch: branch,
|
||||||
"url": fmt.Sprintf("%s/compare/%s...%s", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch)),
|
URL: fmt.Sprintf("%s/compare/%s...%s", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch)),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
results = append(results, private.HookPostReceiveBranchResult{
|
||||||
"message": true,
|
Message: true,
|
||||||
"create": false,
|
Create: false,
|
||||||
"branch": branch,
|
Branch: branch,
|
||||||
"url": fmt.Sprintf("%s/pulls/%d", baseRepo.HTMLURL(), pr.Index),
|
URL: fmt.Sprintf("%s/pulls/%d", baseRepo.HTMLURL(), pr.Index),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, private.HookPostReceiveResult{
|
||||||
|
Results: results,
|
||||||
|
RepoWasEmpty: wasEmpty,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaultBranch updates the default branch
|
||||||
|
func SetDefaultBranch(ctx *macaron.Context) {
|
||||||
|
ownerName := ctx.Params(":owner")
|
||||||
|
repoName := ctx.Params(":repo")
|
||||||
|
branch := ctx.Params(":branch")
|
||||||
|
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"Err": fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
if repo.OwnerName == "" {
|
||||||
"message": false,
|
repo.OwnerName = ownerName
|
||||||
})
|
}
|
||||||
|
|
||||||
|
repo.DefaultBranch = branch
|
||||||
|
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"Err": fmt.Sprintf("Failed to get git repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
|
||||||
|
if !git.IsErrUnsupportedVersion(err) {
|
||||||
|
gitRepo.Close()
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"Err": fmt.Sprintf("Unable to set default branch onrepository: %s/%s Error: %v", ownerName, repoName, err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gitRepo.Close()
|
||||||
|
|
||||||
|
if err := repo.UpdateDefaultBranch(); err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"Err": fmt.Sprintf("Unable to set default branch onrepository: %s/%s Error: %v", ownerName, repoName, err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.PlainText(200, []byte("success"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,10 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"gitea.com/macaron/binding"
|
||||||
"gitea.com/macaron/macaron"
|
"gitea.com/macaron/macaron"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -77,11 +79,14 @@ func CheckUnitUser(ctx *macaron.Context) {
|
||||||
// RegisterRoutes registers all internal APIs routes to web application.
|
// RegisterRoutes registers all internal APIs routes to web application.
|
||||||
// These APIs will be invoked by internal commands for example `gitea serv` and etc.
|
// These APIs will be invoked by internal commands for example `gitea serv` and etc.
|
||||||
func RegisterRoutes(m *macaron.Macaron) {
|
func RegisterRoutes(m *macaron.Macaron) {
|
||||||
|
bind := binding.Bind
|
||||||
|
|
||||||
m.Group("/", func() {
|
m.Group("/", func() {
|
||||||
m.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent)
|
m.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent)
|
||||||
m.Post("/ssh/:id/update/:repoid", UpdatePublicKeyInRepo)
|
m.Post("/ssh/:id/update/:repoid", UpdatePublicKeyInRepo)
|
||||||
m.Get("/hook/pre-receive/:owner/:repo", HookPreReceive)
|
m.Post("/hook/pre-receive/:owner/:repo", bind(private.HookOptions{}), HookPreReceive)
|
||||||
m.Get("/hook/post-receive/:owner/:repo", HookPostReceive)
|
m.Post("/hook/post-receive/:owner/:repo", bind(private.HookOptions{}), HookPostReceive)
|
||||||
|
m.Post("/hook/set-default-branch/:owner/:repo/:branch", SetDefaultBranch)
|
||||||
m.Get("/serv/none/:keyid", ServNoCommand)
|
m.Get("/serv/none/:keyid", ServNoCommand)
|
||||||
m.Get("/serv/command/:keyid/:owner/:repo", ServCommand)
|
m.Get("/serv/command/:keyid/:owner/:repo", ServCommand)
|
||||||
}, CheckInternalToken)
|
}, CheckInternalToken)
|
||||||
|
|
Loading…
Reference in a new issue