From 73e649e0f500eb2eee82f42e6cd7819292c08415 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Oct 2018 13:51:50 +0800 Subject: [PATCH 01/13] refactor action notification --- models/action.go | 191 ++------- models/action_test.go | 74 ---- models/issue.go | 12 - models/issue_comment.go | 7 +- models/pull.go | 16 - models/repo.go | 18 +- models/repo_editor.go | 572 ++++++++++++++++++++++++++ models/repo_mirror.go | 292 +------------ models/update.go | 41 +- modules/cron/cron.go | 5 +- modules/mirror/action.go | 67 +++ modules/mirror/sync.go | 317 ++++++++++++++ modules/notification/action/action.go | 206 ++++++++++ modules/notification/base/notifier.go | 5 + modules/notification/base/null.go | 16 + modules/notification/notification.go | 32 +- routers/api/v1/repo/fork.go | 4 + routers/api/v1/repo/pull.go | 2 + routers/api/v1/repo/repo.go | 8 +- routers/init.go | 3 +- routers/private/push_update.go | 6 +- routers/repo/branch.go | 2 +- routers/repo/editor.go | 22 +- routers/repo/pull.go | 2 + routers/repo/repo.go | 5 + routers/repo/setting.go | 9 +- 26 files changed, 1332 insertions(+), 602 deletions(-) create mode 100644 models/repo_editor.go create mode 100644 modules/mirror/action.go create mode 100644 modules/mirror/sync.go create mode 100644 modules/notification/action/action.go diff --git a/models/action.go b/models/action.go index d4cc4086dbe0d..4c3619ab54e32 100644 --- a/models/action.go +++ b/models/action.go @@ -274,49 +274,6 @@ func (a *Action) GetIssueContent() string { return issue.Content } -func newRepoAction(e Engine, u *User, repo *Repository) (err error) { - if err = notifyWatchers(e, &Action{ - ActUserID: u.ID, - ActUser: u, - OpType: ActionCreateRepo, - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - }); err != nil { - return fmt.Errorf("notify watchers '%d/%d': %v", u.ID, repo.ID, err) - } - - log.Trace("action.newRepoAction: %s/%s", u.Name, repo.Name) - return err -} - -// NewRepoAction adds new action for creating repository. -func NewRepoAction(u *User, repo *Repository) (err error) { - return newRepoAction(x, u, repo) -} - -func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) { - if err = notifyWatchers(e, &Action{ - ActUserID: actUser.ID, - ActUser: actUser, - OpType: ActionRenameRepo, - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - Content: oldRepoName, - }); err != nil { - return fmt.Errorf("notify watchers: %v", err) - } - - log.Trace("action.renameRepoAction: %s/%s", actUser.Name, repo.Name) - return nil -} - -// RenameRepoAction adds new action for renaming a repository. -func RenameRepoAction(actUser *User, oldRepoName string, repo *Repository) error { - return renameRepoAction(x, actUser, oldRepoName, repo) -} - func issueIndexTrimRight(c rune) bool { return !unicode.IsDigit(c) } @@ -572,17 +529,26 @@ type CommitRepoActionOptions struct { Commits *PushCommits } +// CommitRepoEvent represent commits finished event +type CommitRepoEvent struct { + Pusher *User + Repo *Repository + OpType ActionType + RefName string + Data []byte +} + // CommitRepoAction adds new commit action to the repository, and prepare // corresponding webhooks. -func CommitRepoAction(opts CommitRepoActionOptions) error { +func CommitRepoAction(opts CommitRepoActionOptions) (*CommitRepoEvent, error) { pusher, err := GetUserByName(opts.PusherName) if err != nil { - return fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err) + return nil, fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err) } repo, err := GetRepositoryByName(opts.RepoOwnerID, opts.RepoName) if err != nil { - return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err) + return nil, fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err) } refName := git.RefEndName(opts.RefFullName) @@ -595,7 +561,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { // Change repository empty status and update last updated time. if err = UpdateRepository(repo, false); err != nil { - return fmt.Errorf("UpdateRepository: %v", err) + return nil, fmt.Errorf("UpdateRepository: %v", err) } isNewBranch := false @@ -629,20 +595,15 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { data, err := json.Marshal(opts.Commits) if err != nil { - return fmt.Errorf("Marshal: %v", err) + return nil, fmt.Errorf("Marshal: %v", err) } - if err = NotifyWatchers(&Action{ - ActUserID: pusher.ID, - ActUser: pusher, - OpType: opType, - Content: string(data), - RepoID: repo.ID, - Repo: repo, - RefName: refName, - IsPrivate: repo.IsPrivate, - }); err != nil { - return fmt.Errorf("NotifyWatchers: %v", err) + var event = CommitRepoEvent{ + Pusher: pusher, + OpType: opType, + Data: data, + Repo: repo, + RefName: refName, } defer func() { @@ -675,7 +636,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { Repo: apiRepo, Sender: apiPusher, }); err != nil { - return fmt.Errorf("PrepareWebhooks: %v", err) + return nil, fmt.Errorf("PrepareWebhooks: %v", err) } } @@ -689,7 +650,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { Repo: apiRepo, Sender: apiPusher, }); err != nil { - return fmt.Errorf("PrepareWebhooks.(delete branch): %v", err) + return nil, fmt.Errorf("PrepareWebhooks.(delete branch): %v", err) } case ActionPushTag: // Create @@ -710,7 +671,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { Repo: apiRepo, Sender: apiPusher, }); err != nil { - return fmt.Errorf("PrepareWebhooks: %v", err) + return nil, fmt.Errorf("PrepareWebhooks: %v", err) } case ActionDeleteTag: // Delete Tag isHookEventPush = true @@ -722,7 +683,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { Repo: apiRepo, Sender: apiPusher, }); err != nil { - return fmt.Errorf("PrepareWebhooks.(delete tag): %v", err) + return nil, fmt.Errorf("PrepareWebhooks.(delete tag): %v", err) } } @@ -737,26 +698,14 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { Pusher: apiPusher, Sender: apiPusher, }); err != nil { - return fmt.Errorf("PrepareWebhooks: %v", err) + return nil, fmt.Errorf("PrepareWebhooks: %v", err) } } - return nil + return &event, nil } func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) { - if err = notifyWatchers(e, &Action{ - ActUserID: doer.ID, - ActUser: doer, - OpType: ActionTransferRepo, - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - Content: path.Join(oldOwner.Name, repo.Name), - }); err != nil { - return fmt.Errorf("notifyWatchers: %v", err) - } - // Remove watch for organization. if oldOwner.IsOrganization() { if err = watchRepo(e, oldOwner.ID, repo.ID, false); err != nil { @@ -767,94 +716,6 @@ func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err e return nil } -// TransferRepoAction adds new action for transferring repository, -// the Owner field of repository is assumed to be new owner. -func TransferRepoAction(doer, oldOwner *User, repo *Repository) error { - return transferRepoAction(x, doer, oldOwner, repo) -} - -func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error { - return notifyWatchers(e, &Action{ - ActUserID: doer.ID, - ActUser: doer, - OpType: ActionMergePullRequest, - Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - }) -} - -// MergePullRequestAction adds new action for merging pull request. -func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error { - return mergePullRequestAction(x, actUser, repo, pull) -} - -func mirrorSyncAction(e Engine, opType ActionType, repo *Repository, refName string, data []byte) error { - if err := notifyWatchers(e, &Action{ - ActUserID: repo.OwnerID, - ActUser: repo.MustOwner(), - OpType: opType, - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - RefName: refName, - Content: string(data), - }); err != nil { - return fmt.Errorf("notifyWatchers: %v", err) - } - return nil -} - -// MirrorSyncPushActionOptions mirror synchronization action options. -type MirrorSyncPushActionOptions struct { - RefName string - OldCommitID string - NewCommitID string - Commits *PushCommits -} - -// MirrorSyncPushAction adds new action for mirror synchronization of pushed commits. -func MirrorSyncPushAction(repo *Repository, opts MirrorSyncPushActionOptions) error { - if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum { - opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum] - } - - apiCommits := opts.Commits.ToAPIPayloadCommits(repo.HTMLURL()) - - opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID) - apiPusher := repo.MustOwner().APIFormat() - if err := PrepareWebhooks(repo, HookEventPush, &api.PushPayload{ - Ref: opts.RefName, - Before: opts.OldCommitID, - After: opts.NewCommitID, - CompareURL: setting.AppURL + opts.Commits.CompareURL, - Commits: apiCommits, - Repo: repo.APIFormat(AccessModeOwner), - Pusher: apiPusher, - Sender: apiPusher, - }); err != nil { - return fmt.Errorf("PrepareWebhooks: %v", err) - } - - data, err := json.Marshal(opts.Commits) - if err != nil { - return err - } - - return mirrorSyncAction(x, ActionMirrorSyncPush, repo, opts.RefName, data) -} - -// MirrorSyncCreateAction adds new action for mirror synchronization of new reference. -func MirrorSyncCreateAction(repo *Repository, refName string) error { - return mirrorSyncAction(x, ActionMirrorSyncCreate, repo, refName, nil) -} - -// MirrorSyncDeleteAction adds new action for mirror synchronization of delete reference. -func MirrorSyncDeleteAction(repo *Repository, refName string) error { - return mirrorSyncAction(x, ActionMirrorSyncDelete, repo, refName, nil) -} - // GetFeedsOptions options for retrieving feeds type GetFeedsOptions struct { RequestedUser *User diff --git a/models/action_test.go b/models/action_test.go index e30b706809e5c..ea4c78ce1183c 100644 --- a/models/action_test.go +++ b/models/action_test.go @@ -3,7 +3,6 @@ package models import ( "fmt" "path" - "strings" "testing" "code.gitea.io/gitea/modules/git" @@ -30,58 +29,6 @@ func TestAction_GetRepoLink(t *testing.T) { assert.Equal(t, expected, action.GetRepoLink()) } -func TestNewRepoAction(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) - - user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{OwnerID: user.ID}).(*Repository) - repo.Owner = user - - actionBean := &Action{ - OpType: ActionCreateRepo, - ActUserID: user.ID, - RepoID: repo.ID, - ActUser: user, - Repo: repo, - IsPrivate: repo.IsPrivate, - } - - AssertNotExistsBean(t, actionBean) - assert.NoError(t, NewRepoAction(user, repo)) - AssertExistsAndLoadBean(t, actionBean) - CheckConsistencyFor(t, &Action{}) -} - -func TestRenameRepoAction(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) - - user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{OwnerID: user.ID}).(*Repository) - repo.Owner = user - - oldRepoName := repo.Name - const newRepoName = "newRepoName" - repo.Name = newRepoName - repo.LowerName = strings.ToLower(newRepoName) - - actionBean := &Action{ - OpType: ActionRenameRepo, - ActUserID: user.ID, - ActUser: user, - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - Content: oldRepoName, - } - AssertNotExistsBean(t, actionBean) - assert.NoError(t, RenameRepoAction(user, oldRepoName, repo)) - AssertExistsAndLoadBean(t, actionBean) - - _, err := x.ID(repo.ID).Cols("name", "lower_name").Update(repo) - assert.NoError(t, err) - CheckConsistencyFor(t, &Action{}) -} - func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { pushCommits := NewPushCommits() pushCommits.Commits = []*PushCommit{ @@ -434,27 +381,6 @@ func TestTransferRepoAction(t *testing.T) { CheckConsistencyFor(t, &Action{}) } -func TestMergePullRequestAction(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) - user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{ID: 1, OwnerID: user.ID}).(*Repository) - repo.Owner = user - issue := AssertExistsAndLoadBean(t, &Issue{ID: 3, RepoID: repo.ID}).(*Issue) - - actionBean := &Action{ - OpType: ActionMergePullRequest, - ActUserID: user.ID, - ActUser: user, - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - } - AssertNotExistsBean(t, actionBean) - assert.NoError(t, MergePullRequestAction(user, repo, issue)) - AssertExistsAndLoadBean(t, actionBean) - CheckConsistencyFor(t, &Action{}) -} - func TestGetFeeds(t *testing.T) { // test with an individual user assert.NoError(t, PrepareTestDatabase()) diff --git a/models/issue.go b/models/issue.go index 8a6b20727aba1..f27fb39199c86 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1160,18 +1160,6 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in return fmt.Errorf("Commit: %v", err) } - if err = NotifyWatchers(&Action{ - ActUserID: issue.Poster.ID, - ActUser: issue.Poster, - OpType: ActionCreateIssue, - Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - }); err != nil { - log.Error(4, "NotifyWatchers: %v", err) - } - mode, _ := AccessLevel(issue.Poster, issue.Repo) if err = PrepareWebhooks(repo, HookEventIssues, &api.IssuePayload{ Action: api.HookIssueOpened, diff --git a/models/issue_comment.go b/models/issue_comment.go index d4a3d26a782aa..444a46ea828b4 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -635,12 +635,7 @@ func sendCreateCommentAction(e *xorm.Session, opts *CreateCommentOptions, commen if err = updateIssueCols(e, opts.Issue, "updated_unix"); err != nil { return err } - // Notify watchers for whatever action comes in, ignore if no action type. - if act.OpType > 0 { - if err = notifyWatchers(e, act); err != nil { - log.Error(4, "notifyWatchers: %v", err) - } - } + return nil } diff --git a/models/pull.go b/models/pull.go index ef3ec8e1453e9..668cb1f3fd678 100644 --- a/models/pull.go +++ b/models/pull.go @@ -584,10 +584,6 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle log.Error(4, "setMerged [%d]: %v", pr.ID, err) } - if err = MergePullRequestAction(doer, pr.Issue.Repo, pr.Issue); err != nil { - log.Error(4, "MergePullRequestAction [%d]: %v", pr.ID, err) - } - // Reset cached commit count cache.Remove(pr.Issue.Repo.GetCommitsCountCacheKey(pr.BaseBranch, true)) @@ -927,18 +923,6 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str return fmt.Errorf("Commit: %v", err) } - if err = NotifyWatchers(&Action{ - ActUserID: pull.Poster.ID, - ActUser: pull.Poster, - OpType: ActionCreatePullRequest, - Content: fmt.Sprintf("%d|%s", pull.Index, pull.Title), - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - }); err != nil { - log.Error(4, "NotifyWatchers: %v", err) - } - pr.Issue = pull pull.PullRequest = pr mode, _ := AccessLevel(pull.Poster, repo) diff --git a/models/repo.go b/models/repo.go index 19526fb17c94e..920f8721849bf 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1362,9 +1362,6 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err return fmt.Errorf("watchRepo: %v", err) } } - if err = newRepoAction(e, doer, repo); err != nil { - return fmt.Errorf("newRepoAction: %v", err) - } if err = copyDefaultWebhooksToRepo(e, repo.ID); err != nil { return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err) @@ -1734,6 +1731,12 @@ func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) { return sess.Commit() } +// UpdateRepositoryCols updates repository special columns +func UpdateRepositoryCols(repo *Repository, cols ...string) error { + _, err := x.ID(repo.ID).Cols(cols...).Update(repo) + return err +} + // UpdateRepositoryUnits updates a repository's units func UpdateRepositoryUnits(repo *Repository, units []RepoUnit) (err error) { sess := x.NewSession() @@ -2217,12 +2220,19 @@ func SyncRepositoryHooks() error { var taskStatusTable = sync.NewStatusTable() const ( - mirrorUpdate = "mirror_update" gitFsck = "git_fsck" checkRepos = "check_repos" archiveCleanup = "archive_cleanup" ) +func TaskStartIfNotRunning(name string) bool { + return taskStatusTable.StartIfNotRunning(name) +} + +func TaskStop(name string) { + taskStatusTable.Stop(name) +} + // GitFsck calls 'git fsck' to check repository health. func GitFsck() { if !taskStatusTable.StartIfNotRunning(gitFsck) { diff --git a/models/repo_editor.go b/models/repo_editor.go new file mode 100644 index 0000000000000..2d877e3ad38a2 --- /dev/null +++ b/models/repo_editor.go @@ -0,0 +1,572 @@ +// Copyright 2016 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 ( + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "os" + "os/exec" + "path" + "path/filepath" + "time" + + "github.com/Unknwon/com" + gouuid "github.com/satori/go.uuid" + + "code.gitea.io/git" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/process" + "code.gitea.io/gitea/modules/setting" +) + +// ___________ .___.__ __ ___________.__.__ +// \_ _____/ __| _/|__|/ |_ \_ _____/|__| | ____ +// | __)_ / __ | | \ __\ | __) | | | _/ __ \ +// | \/ /_/ | | || | | \ | | |_\ ___/ +// /_______ /\____ | |__||__| \___ / |__|____/\___ > +// \/ \/ \/ \/ + +// discardLocalRepoBranchChanges discards local commits/changes of +// given branch to make sure it is even to remote branch. +func discardLocalRepoBranchChanges(localPath, branch string) error { + if !com.IsExist(localPath) { + return nil + } + // No need to check if nothing in the repository. + if !git.IsBranchExist(localPath, branch) { + return nil + } + + refName := "origin/" + branch + if err := git.ResetHEAD(localPath, true, refName); err != nil { + return fmt.Errorf("git reset --hard %s: %v", refName, err) + } + return nil +} + +// DiscardLocalRepoBranchChanges discards the local repository branch changes +func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error { + return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch) +} + +// checkoutNewBranch checks out to a new branch from the a branch name. +func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error { + if err := git.Checkout(localPath, git.CheckoutOptions{ + Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, + Branch: newBranch, + OldBranch: oldBranch, + }); err != nil { + return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err) + } + return nil +} + +// CheckoutNewBranch checks out a new branch +func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error { + return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch) +} + +// UpdateRepoFileOptions holds the repository file update options +type UpdateRepoFileOptions struct { + LastCommitID string + OldBranch string + NewBranch string + OldTreeName string + NewTreeName string + Message string + Content string + IsNewFile bool +} + +// UpdateRepoFile adds or updates a file in repository. +func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (event *CommitRepoEvent, err error) { + repoWorkingPool.CheckIn(com.ToStr(repo.ID)) + defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) + + if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { + return nil, fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err) + } else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil { + return nil, fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err) + } + + if opts.OldBranch != opts.NewBranch { + if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { + return nil, fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err) + } + } + + localPath := repo.LocalCopyPath() + oldFilePath := path.Join(localPath, opts.OldTreeName) + filePath := path.Join(localPath, opts.NewTreeName) + dir := path.Dir(filePath) + + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return nil, fmt.Errorf("Failed to create dir %s: %v", dir, err) + } + + // If it's meant to be a new file, make sure it doesn't exist. + if opts.IsNewFile { + if com.IsExist(filePath) { + return nil, ErrRepoFileAlreadyExist{filePath} + } + } + + // Ignore move step if it's a new file under a directory. + // Otherwise, move the file when name changed. + if com.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName { + if err = git.MoveFile(localPath, opts.OldTreeName, opts.NewTreeName); err != nil { + return nil, fmt.Errorf("git mv %s %s: %v", opts.OldTreeName, opts.NewTreeName, err) + } + } + + if err = ioutil.WriteFile(filePath, []byte(opts.Content), 0666); err != nil { + return nil, fmt.Errorf("WriteFile: %v", err) + } + + if err = git.AddChanges(localPath, true); err != nil { + return nil, fmt.Errorf("git add --all: %v", err) + } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ + Committer: doer.NewGitSig(), + Message: opts.Message, + }); err != nil { + return nil, fmt.Errorf("CommitChanges: %v", err) + } else if err = git.Push(localPath, git.PushOptions{ + Remote: "origin", + Branch: opts.NewBranch, + }); err != nil { + return nil, fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) + } + + gitRepo, err := git.OpenRepository(repo.RepoPath()) + if err != nil { + log.Error(4, "OpenRepository: %v", err) + return nil, nil + } + commit, err := gitRepo.GetBranchCommit(opts.NewBranch) + if err != nil { + log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err) + return nil, nil + } + + // Simulate push event. + oldCommitID := opts.LastCommitID + if opts.NewBranch != opts.OldBranch { + oldCommitID = git.EmptySHA + } + + if err = repo.GetOwner(); err != nil { + return nil, fmt.Errorf("GetOwner: %v", err) + } + event, err = PushUpdate( + opts.NewBranch, + PushUpdateOptions{ + PusherID: doer.ID, + PusherName: doer.Name, + RepoUserName: repo.Owner.Name, + RepoName: repo.Name, + RefFullName: git.BranchPrefix + opts.NewBranch, + OldCommitID: oldCommitID, + NewCommitID: commit.ID.String(), + }, + ) + if err != nil { + return nil, fmt.Errorf("PushUpdate: %v", err) + } + UpdateRepoIndexer(repo) + + return event, nil +} + +// GetDiffPreview produces and returns diff result of a file which is not yet committed. +func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *Diff, err error) { + repoWorkingPool.CheckIn(com.ToStr(repo.ID)) + defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) + + if err = repo.DiscardLocalRepoBranchChanges(branch); err != nil { + return nil, fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", branch, err) + } else if err = repo.UpdateLocalCopyBranch(branch); err != nil { + return nil, fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", branch, err) + } + + localPath := repo.LocalCopyPath() + filePath := path.Join(localPath, treePath) + dir := filepath.Dir(filePath) + + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return nil, fmt.Errorf("Failed to create dir %s: %v", dir, err) + } + + if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil { + return nil, fmt.Errorf("WriteFile: %v", err) + } + + cmd := exec.Command("git", "diff", treePath) + cmd.Dir = localPath + cmd.Stderr = os.Stderr + + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("StdoutPipe: %v", err) + } + + if err = cmd.Start(); err != nil { + return nil, fmt.Errorf("Start: %v", err) + } + + pid := process.GetManager().Add(fmt.Sprintf("GetDiffPreview [repo_path: %s]", repo.RepoPath()), cmd) + defer process.GetManager().Remove(pid) + + diff, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdout) + if err != nil { + return nil, fmt.Errorf("ParsePatch: %v", err) + } + + if err = cmd.Wait(); err != nil { + return nil, fmt.Errorf("Wait: %v", err) + } + + return diff, nil +} + +// ________ .__ __ ___________.__.__ +// \______ \ ____ | | _____/ |_ ____ \_ _____/|__| | ____ +// | | \_/ __ \| | _/ __ \ __\/ __ \ | __) | | | _/ __ \ +// | ` \ ___/| |_\ ___/| | \ ___/ | \ | | |_\ ___/ +// /_______ /\___ >____/\___ >__| \___ > \___ / |__|____/\___ > +// \/ \/ \/ \/ \/ \/ +// + +// DeleteRepoFileOptions holds the repository delete file options +type DeleteRepoFileOptions struct { + LastCommitID string + OldBranch string + NewBranch string + TreePath string + Message string +} + +// DeleteRepoFile deletes a repository file +func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (event *CommitRepoEvent, err error) { + repoWorkingPool.CheckIn(com.ToStr(repo.ID)) + defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) + + if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { + return nil, fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err) + } else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil { + return nil, fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err) + } + + if opts.OldBranch != opts.NewBranch { + if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { + return nil, fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err) + } + } + + localPath := repo.LocalCopyPath() + if err = os.Remove(path.Join(localPath, opts.TreePath)); err != nil { + return nil, fmt.Errorf("Remove: %v", err) + } + + if err = git.AddChanges(localPath, true); err != nil { + return nil, fmt.Errorf("git add --all: %v", err) + } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ + Committer: doer.NewGitSig(), + Message: opts.Message, + }); err != nil { + return nil, fmt.Errorf("CommitChanges: %v", err) + } else if err = git.Push(localPath, git.PushOptions{ + Remote: "origin", + Branch: opts.NewBranch, + }); err != nil { + return nil, fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) + } + + gitRepo, err := git.OpenRepository(repo.RepoPath()) + if err != nil { + log.Error(4, "OpenRepository: %v", err) + return nil, nil + } + commit, err := gitRepo.GetBranchCommit(opts.NewBranch) + if err != nil { + log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err) + return nil, nil + } + + // Simulate push event. + oldCommitID := opts.LastCommitID + if opts.NewBranch != opts.OldBranch { + oldCommitID = git.EmptySHA + } + + if err = repo.GetOwner(); err != nil { + return nil, fmt.Errorf("GetOwner: %v", err) + } + event, err = PushUpdate( + opts.NewBranch, + PushUpdateOptions{ + PusherID: doer.ID, + PusherName: doer.Name, + RepoUserName: repo.Owner.Name, + RepoName: repo.Name, + RefFullName: git.BranchPrefix + opts.NewBranch, + OldCommitID: oldCommitID, + NewCommitID: commit.ID.String(), + }, + ) + if err != nil { + return nil, fmt.Errorf("PushUpdate: %v", err) + } + return event, nil +} + +// ____ ___ .__ .___ ___________.___.__ +// | | \______ | | _________ __| _/ \_ _____/| | | ____ ______ +// | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/ +// | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \ +// |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ > +// |__| \/ \/ \/ \/ \/ +// + +// Upload represent a uploaded file to a repo to be deleted when moved +type Upload struct { + ID int64 `xorm:"pk autoincr"` + UUID string `xorm:"uuid UNIQUE"` + Name string +} + +// UploadLocalPath returns where uploads is stored in local file system based on given UUID. +func UploadLocalPath(uuid string) string { + return path.Join(setting.Repository.Upload.TempPath, uuid[0:1], uuid[1:2], uuid) +} + +// LocalPath returns where uploads are temporarily stored in local file system. +func (upload *Upload) LocalPath() string { + return UploadLocalPath(upload.UUID) +} + +// NewUpload creates a new upload object. +func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err error) { + upload := &Upload{ + UUID: gouuid.NewV4().String(), + Name: name, + } + + localPath := upload.LocalPath() + if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil { + return nil, fmt.Errorf("MkdirAll: %v", err) + } + + fw, err := os.Create(localPath) + if err != nil { + return nil, fmt.Errorf("Create: %v", err) + } + defer fw.Close() + + if _, err = fw.Write(buf); err != nil { + return nil, fmt.Errorf("Write: %v", err) + } else if _, err = io.Copy(fw, file); err != nil { + return nil, fmt.Errorf("Copy: %v", err) + } + + if _, err := x.Insert(upload); err != nil { + return nil, err + } + + return upload, nil +} + +// GetUploadByUUID returns the Upload by UUID +func GetUploadByUUID(uuid string) (*Upload, error) { + upload := &Upload{UUID: uuid} + has, err := x.Get(upload) + if err != nil { + return nil, err + } else if !has { + return nil, ErrUploadNotExist{0, uuid} + } + return upload, nil +} + +// GetUploadsByUUIDs returns multiple uploads by UUIDS +func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) { + if len(uuids) == 0 { + return []*Upload{}, nil + } + + // Silently drop invalid uuids. + uploads := make([]*Upload, 0, len(uuids)) + return uploads, x.In("uuid", uuids).Find(&uploads) +} + +// DeleteUploads deletes multiple uploads +func DeleteUploads(uploads ...*Upload) (err error) { + if len(uploads) == 0 { + return nil + } + + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + + ids := make([]int64, len(uploads)) + for i := 0; i < len(uploads); i++ { + ids[i] = uploads[i].ID + } + if _, err = sess. + In("id", ids). + Delete(new(Upload)); err != nil { + return fmt.Errorf("delete uploads: %v", err) + } + + for _, upload := range uploads { + localPath := upload.LocalPath() + if !com.IsFile(localPath) { + continue + } + + if err := os.Remove(localPath); err != nil { + return fmt.Errorf("remove upload: %v", err) + } + } + + return sess.Commit() +} + +// DeleteUpload delete a upload +func DeleteUpload(u *Upload) error { + return DeleteUploads(u) +} + +// DeleteUploadByUUID deletes a upload by UUID +func DeleteUploadByUUID(uuid string) error { + upload, err := GetUploadByUUID(uuid) + if err != nil { + if IsErrUploadNotExist(err) { + return nil + } + return fmt.Errorf("GetUploadByUUID: %v", err) + } + + if err := DeleteUpload(upload); err != nil { + return fmt.Errorf("DeleteUpload: %v", err) + } + + return nil +} + +// UploadRepoFileOptions contains the uploaded repository file options +type UploadRepoFileOptions struct { + LastCommitID string + OldBranch string + NewBranch string + TreePath string + Message string + Files []string // In UUID format. +} + +// UploadRepoFiles uploads files to a repository +func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions) (event *CommitRepoEvent, err error) { + if len(opts.Files) == 0 { + return nil, nil + } + + uploads, err := GetUploadsByUUIDs(opts.Files) + if err != nil { + return nil, fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err) + } + + repoWorkingPool.CheckIn(com.ToStr(repo.ID)) + defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) + + if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { + return nil, fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err) + } else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil { + return nil, fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err) + } + + if opts.OldBranch != opts.NewBranch { + if err = repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { + return nil, fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err) + } + } + + localPath := repo.LocalCopyPath() + dirPath := path.Join(localPath, opts.TreePath) + + if err := os.MkdirAll(dirPath, os.ModePerm); err != nil { + return nil, fmt.Errorf("Failed to create dir %s: %v", dirPath, err) + } + + // Copy uploaded files into repository. + for _, upload := range uploads { + tmpPath := upload.LocalPath() + targetPath := path.Join(dirPath, upload.Name) + if !com.IsFile(tmpPath) { + continue + } + + if err = com.Copy(tmpPath, targetPath); err != nil { + return nil, fmt.Errorf("Copy: %v", err) + } + } + + if err = git.AddChanges(localPath, true); err != nil { + return nil, fmt.Errorf("git add --all: %v", err) + } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ + Committer: doer.NewGitSig(), + Message: opts.Message, + }); err != nil { + return nil, fmt.Errorf("CommitChanges: %v", err) + } else if err = git.Push(localPath, git.PushOptions{ + Remote: "origin", + Branch: opts.NewBranch, + }); err != nil { + return nil, fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) + } + + gitRepo, err := git.OpenRepository(repo.RepoPath()) + if err != nil { + log.Error(4, "OpenRepository: %v", err) + return nil, nil + } + commit, err := gitRepo.GetBranchCommit(opts.NewBranch) + if err != nil { + log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err) + return nil, nil + } + + // Simulate push event. + oldCommitID := opts.LastCommitID + if opts.NewBranch != opts.OldBranch { + oldCommitID = git.EmptySHA + } + + if err = repo.GetOwner(); err != nil { + return nil, fmt.Errorf("GetOwner: %v", err) + } + event, err = PushUpdate( + opts.NewBranch, + PushUpdateOptions{ + PusherID: doer.ID, + PusherName: doer.Name, + RepoUserName: repo.Owner.Name, + RepoName: repo.Name, + RefFullName: git.BranchPrefix + opts.NewBranch, + OldCommitID: oldCommitID, + NewCommitID: commit.ID.String(), + }, + ) + if err != nil { + return nil, fmt.Errorf("PushUpdate: %v", err) + } + + return event, DeleteUploads(uploads...) +} diff --git a/models/repo_mirror.go b/models/repo_mirror.go index c3fc9a43dbff9..68cd4ba6750ee 100644 --- a/models/repo_mirror.go +++ b/models/repo_mirror.go @@ -7,25 +7,15 @@ package models import ( "fmt" - "strings" "time" - "code.gitea.io/gitea/modules/cache" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/util" - "github.com/Unknwon/com" "github.com/go-xorm/xorm" "gopkg.in/ini.v1" ) -// MirrorQueue holds an UniqueQueue object of the mirror -var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength) - // Mirror represents mirror information of a repository. type Mirror struct { ID int64 `xorm:"pk autoincr"` @@ -89,18 +79,6 @@ func (m *Mirror) readAddress() { } } -// sanitizeOutput sanitizes output of a command, replacing occurrences of the -// repository's remote address with a sanitized version. -func sanitizeOutput(output, repoPath string) (string, error) { - remoteAddr, err := remoteAddress(repoPath) - if err != nil { - // if we're unable to load the remote address, then we're unable to - // sanitize. - return "", err - } - return util.SanitizeMessage(output, remoteAddr), nil -} - // Address returns mirror address from Git repository config without credentials. func (m *Mirror) Address() string { m.readAddress() @@ -125,144 +103,6 @@ func (m *Mirror) SaveAddress(addr string) error { return cfg.SaveToIndent(configPath, "\t") } -// gitShortEmptySha Git short empty SHA -const gitShortEmptySha = "0000000" - -// mirrorSyncResult contains information of a updated reference. -// If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. -// If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty. -type mirrorSyncResult struct { - refName string - oldCommitID string - newCommitID string -} - -// parseRemoteUpdateOutput detects create, update and delete operations of references from upstream. -func parseRemoteUpdateOutput(output string) []*mirrorSyncResult { - results := make([]*mirrorSyncResult, 0, 3) - lines := strings.Split(output, "\n") - for i := range lines { - // Make sure reference name is presented before continue - idx := strings.Index(lines[i], "-> ") - if idx == -1 { - continue - } - - refName := lines[i][idx+3:] - - switch { - case strings.HasPrefix(lines[i], " * "): // New reference - results = append(results, &mirrorSyncResult{ - refName: refName, - oldCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " - "): // Delete reference - results = append(results, &mirrorSyncResult{ - refName: refName, - newCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " "): // New commits of a reference - delimIdx := strings.Index(lines[i][3:], " ") - if delimIdx == -1 { - log.Error(2, "SHA delimiter not found: %q", lines[i]) - continue - } - shas := strings.Split(lines[i][3:delimIdx+3], "..") - if len(shas) != 2 { - log.Error(2, "Expect two SHAs but not what found: %q", lines[i]) - continue - } - results = append(results, &mirrorSyncResult{ - refName: refName, - oldCommitID: shas[0], - newCommitID: shas[1], - }) - - default: - log.Warn("parseRemoteUpdateOutput: unexpected update line %q", lines[i]) - } - } - return results -} - -// runSync returns true if sync finished without error. -func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { - repoPath := m.Repo.RepoPath() - wikiPath := m.Repo.WikiPath() - timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second - - gitArgs := []string{"remote", "update"} - if m.EnablePrune { - gitArgs = append(gitArgs, "--prune") - } - - _, stderr, err := process.GetManager().ExecDir( - timeout, repoPath, fmt.Sprintf("Mirror.runSync: %s", repoPath), - "git", gitArgs...) - if err != nil { - // sanitize the output, since it may contain the remote address, which may - // contain a password - message, err := sanitizeOutput(stderr, repoPath) - if err != nil { - log.Error(4, "sanitizeOutput: %v", err) - return nil, false - } - desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, message) - log.Error(4, desc) - if err = CreateRepositoryNotice(desc); err != nil { - log.Error(4, "CreateRepositoryNotice: %v", err) - } - return nil, false - } - output := stderr - - gitRepo, err := git.OpenRepository(repoPath) - if err != nil { - log.Error(4, "OpenRepository: %v", err) - return nil, false - } - if err = SyncReleasesWithTags(m.Repo, gitRepo); err != nil { - log.Error(4, "Failed to synchronize tags to releases for repository: %v", err) - } - - if err := m.Repo.UpdateSize(); err != nil { - log.Error(4, "Failed to update size for mirror repository: %v", err) - } - - if m.Repo.HasWiki() { - if _, stderr, err := process.GetManager().ExecDir( - timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath), - "git", "remote", "update", "--prune"); err != nil { - // sanitize the output, since it may contain the remote address, which may - // contain a password - message, err := sanitizeOutput(stderr, wikiPath) - if err != nil { - log.Error(4, "sanitizeOutput: %v", err) - return nil, false - } - desc := fmt.Sprintf("Failed to update mirror wiki repository '%s': %s", wikiPath, message) - log.Error(4, desc) - if err = CreateRepositoryNotice(desc); err != nil { - log.Error(4, "CreateRepositoryNotice: %v", err) - } - return nil, false - } - } - - branches, err := m.Repo.GetBranches() - if err != nil { - log.Error(4, "GetBranches: %v", err) - return nil, false - } - - for i := range branches { - cache.Remove(m.Repo.GetCommitsCountCacheKey(branches[i].Name, true)) - } - - m.UpdatedUnix = util.TimeStampNow() - return parseRemoteUpdateOutput(output), true -} - func getMirrorByRepoID(e Engine, repoID int64) (*Mirror, error) { m := &Mirror{RepoID: repoID} has, err := e.Get(m) @@ -295,134 +135,10 @@ func DeleteMirrorByRepoID(repoID int64) error { return err } -// MirrorUpdate checks and updates mirror repositories. -func MirrorUpdate() { - if !taskStatusTable.StartIfNotRunning(mirrorUpdate) { - return - } - defer taskStatusTable.Stop(mirrorUpdate) - - log.Trace("Doing: MirrorUpdate") - - if err := x. +// IterateNextMirrors iterates mirrors needs to updated +func IterateNextMirrors(f func(idx int, bean interface{}) error) error { + return x. Where("next_update_unix<=?", time.Now().Unix()). And("next_update_unix!=0"). - Iterate(new(Mirror), func(idx int, bean interface{}) error { - m := bean.(*Mirror) - if m.Repo == nil { - log.Error(4, "Disconnected mirror repository found: %d", m.ID) - return nil - } - - MirrorQueue.Add(m.RepoID) - return nil - }); err != nil { - log.Error(4, "MirrorUpdate: %v", err) - } -} - -// SyncMirrors checks and syncs mirrors. -// TODO: sync more mirrors at same time. -func SyncMirrors() { - sess := x.NewSession() - defer sess.Close() - // Start listening on new sync requests. - for repoID := range MirrorQueue.Queue() { - log.Trace("SyncMirrors [repo_id: %v]", repoID) - MirrorQueue.Remove(repoID) - - m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64()) - if err != nil { - log.Error(4, "GetMirrorByRepoID [%s]: %v", repoID, err) - continue - } - - results, ok := m.runSync() - if !ok { - continue - } - - m.ScheduleNextUpdate() - if err = updateMirror(sess, m); err != nil { - log.Error(4, "UpdateMirror [%s]: %v", repoID, err) - continue - } - - var gitRepo *git.Repository - if len(results) == 0 { - log.Trace("SyncMirrors [repo_id: %d]: no commits fetched", m.RepoID) - } else { - gitRepo, err = git.OpenRepository(m.Repo.RepoPath()) - if err != nil { - log.Error(2, "OpenRepository [%d]: %v", m.RepoID, err) - continue - } - } - - for _, result := range results { - // Discard GitHub pull requests, i.e. refs/pull/* - if strings.HasPrefix(result.refName, "refs/pull/") { - continue - } - - // Create reference - if result.oldCommitID == gitShortEmptySha { - if err = MirrorSyncCreateAction(m.Repo, result.refName); err != nil { - log.Error(2, "MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err) - } - continue - } - - // Delete reference - if result.newCommitID == gitShortEmptySha { - if err = MirrorSyncDeleteAction(m.Repo, result.refName); err != nil { - log.Error(2, "MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err) - } - continue - } - - // Push commits - oldCommitID, err := git.GetFullCommitID(gitRepo.Path, result.oldCommitID) - if err != nil { - log.Error(2, "GetFullCommitID [%d]: %v", m.RepoID, err) - continue - } - newCommitID, err := git.GetFullCommitID(gitRepo.Path, result.newCommitID) - if err != nil { - log.Error(2, "GetFullCommitID [%d]: %v", m.RepoID, err) - continue - } - commits, err := gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) - if err != nil { - log.Error(2, "CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err) - continue - } - if err = MirrorSyncPushAction(m.Repo, MirrorSyncPushActionOptions{ - RefName: result.refName, - OldCommitID: oldCommitID, - NewCommitID: newCommitID, - Commits: ListToPushCommits(commits), - }); err != nil { - log.Error(2, "MirrorSyncPushAction [repo_id: %d]: %v", m.RepoID, err) - continue - } - } - - // Get latest commit date and update to current repository updated time - commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath()) - if err != nil { - log.Error(2, "GetLatestCommitDate [%s]: %v", m.RepoID, err) - continue - } - - if _, err = sess.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", commitDate.Unix(), m.RepoID); err != nil { - log.Error(2, "Update repository 'updated_unix' [%s]: %v", m.RepoID, err) - continue - } - } -} - -// InitSyncMirrors initializes a go routine to sync the mirrors -func InitSyncMirrors() { - go SyncMirrors() + Iterate(new(Mirror), f) } diff --git a/models/update.go b/models/update.go index cde6b217c1c45..3026490a7e1fb 100644 --- a/models/update.go +++ b/models/update.go @@ -67,21 +67,21 @@ type PushUpdateOptions struct { // PushUpdate must be called for any push actions in order to // generates necessary push action history feeds. -func PushUpdate(branch string, opt PushUpdateOptions) error { - repo, err := pushUpdate(opt) +func PushUpdate(branch string, opt PushUpdateOptions) (*CommitRepoEvent, error) { + repo, event, err := pushUpdate(opt) if err != nil { - return err + return nil, err } pusher, err := GetUserByID(opt.PusherID) if err != nil { - return err + return nil, err } log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name) go AddTestPullRequestTask(pusher, repo.ID, branch, true) - return nil + return event, nil } func pushUpdateDeleteTag(repo *Repository, gitRepo *git.Repository, tagName string) error { @@ -184,11 +184,11 @@ func pushUpdateAddTag(repo *Repository, gitRepo *git.Repository, tagName string) return nil } -func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { +func pushUpdate(opts PushUpdateOptions) (repo *Repository, evt *CommitRepoEvent, err 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) + return nil, nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA) } repoPath := RepoPath(opts.RepoUserName, opts.RepoName) @@ -196,22 +196,22 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { gitUpdate := exec.Command("git", "update-server-info") gitUpdate.Dir = repoPath if err = gitUpdate.Run(); err != nil { - return nil, fmt.Errorf("Failed to call 'git update-server-info': %v", err) + return nil, nil, fmt.Errorf("Failed to call 'git update-server-info': %v", err) } owner, err := GetUserByName(opts.RepoUserName) if err != nil { - return nil, fmt.Errorf("GetUserByName: %v", err) + return nil, nil, fmt.Errorf("GetUserByName: %v", err) } repo, err = GetRepositoryByName(owner.ID, opts.RepoName) if err != nil { - return nil, fmt.Errorf("GetRepositoryByName: %v", err) + return nil, nil, fmt.Errorf("GetRepositoryByName: %v", err) } gitRepo, err := git.OpenRepository(repoPath) if err != nil { - return nil, fmt.Errorf("OpenRepository: %v", err) + return nil, nil, fmt.Errorf("OpenRepository: %v", err) } if err = repo.UpdateSize(); err != nil { @@ -225,14 +225,14 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { if isDelRef { err = pushUpdateDeleteTag(repo, gitRepo, tagName) if err != nil { - return nil, fmt.Errorf("pushUpdateDeleteTag: %v", err) + return nil, nil, fmt.Errorf("pushUpdateDeleteTag: %v", err) } } else { // Clear cache for tag commit count cache.Remove(repo.GetCommitsCountCacheKey(tagName, true)) err = pushUpdateAddTag(repo, gitRepo, tagName) if err != nil { - return nil, fmt.Errorf("pushUpdateAddTag: %v", err) + return nil, nil, fmt.Errorf("pushUpdateAddTag: %v", err) } } } else if !isDelRef { @@ -243,7 +243,7 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { newCommit, err := gitRepo.GetCommit(opts.NewCommitID) if err != nil { - return nil, fmt.Errorf("gitRepo.GetCommit: %v", err) + return nil, nil, fmt.Errorf("gitRepo.GetCommit: %v", err) } // Push new branch. @@ -251,12 +251,12 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { if isNewRef { l, err = newCommit.CommitsBeforeLimit(10) if err != nil { - return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err) + return nil, 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) + return nil, nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err) } } @@ -267,7 +267,7 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { UpdateRepoIndexer(repo) } - if err := CommitRepoAction(CommitRepoActionOptions{ + evt, err = CommitRepoAction(CommitRepoActionOptions{ PusherName: opts.PusherName, RepoOwnerID: owner.ID, RepoName: repo.Name, @@ -275,8 +275,9 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { OldCommitID: opts.OldCommitID, NewCommitID: opts.NewCommitID, Commits: commits, - }); err != nil { - return nil, fmt.Errorf("CommitRepoAction: %v", err) + }) + if err != nil { + return nil, nil, fmt.Errorf("CommitRepoAction: %v", err) } - return repo, nil + return repo, evt, nil } diff --git a/modules/cron/cron.go b/modules/cron/cron.go index ede73e06669e0..a8aeacbfd2f32 100644 --- a/modules/cron/cron.go +++ b/modules/cron/cron.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/mirror" "code.gitea.io/gitea/modules/setting" ) @@ -23,14 +24,14 @@ func NewContext() { err error ) if setting.Cron.UpdateMirror.Enabled { - entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, models.MirrorUpdate) + entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, mirror.MirrorUpdate) if err != nil { log.Fatal(4, "Cron[Update mirrors]: %v", err) } if setting.Cron.UpdateMirror.RunAtStart { entry.Prev = time.Now() entry.ExecTimes++ - go models.MirrorUpdate() + go mirror.MirrorUpdate() } } if setting.Cron.RepoHealthCheck.Enabled { diff --git a/modules/mirror/action.go b/modules/mirror/action.go new file mode 100644 index 0000000000000..405d13635c8fc --- /dev/null +++ b/modules/mirror/action.go @@ -0,0 +1,67 @@ +// Copyright 2018 The Gitea 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 mirror + +import ( + "encoding/json" + "fmt" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/sdk/gitea" +) + +// MirrorSyncPushActionOptions mirror synchronization action options. +type MirrorSyncPushActionOptions struct { + RefName string + OldCommitID string + NewCommitID string + Commits *models.PushCommits +} + +// MirrorSyncPushAction adds new action for mirror synchronization of pushed commits. +func MirrorSyncPushAction(repo *models.Repository, opts MirrorSyncPushActionOptions) error { + if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum { + opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum] + } + + apiCommits := opts.Commits.ToAPIPayloadCommits(repo.HTMLURL()) + + opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID) + apiPusher := repo.MustOwner().APIFormat() + if err := models.PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{ + Ref: opts.RefName, + Before: opts.OldCommitID, + After: opts.NewCommitID, + CompareURL: setting.AppURL + opts.Commits.CompareURL, + Commits: apiCommits, + Repo: repo.APIFormat(models.AccessModeOwner), + Pusher: apiPusher, + Sender: apiPusher, + }); err != nil { + return fmt.Errorf("PrepareWebhooks: %v", err) + } + + data, err := json.Marshal(opts.Commits) + if err != nil { + return err + } + + notification.NotifyRepoMirrorSync(models.ActionMirrorSyncPush, repo, opts.RefName, data) + return nil +} + +// MirrorSyncCreateAction adds new action for mirror synchronization of new reference. +func MirrorSyncCreateAction(repo *models.Repository, refName string) error { + notification.NotifyRepoMirrorSync(models.ActionMirrorSyncCreate, repo, refName, nil) + return nil +} + +// MirrorSyncDeleteAction adds new action for mirror synchronization of delete reference. +func MirrorSyncDeleteAction(repo *models.Repository, refName string) error { + notification.NotifyRepoMirrorSync(models.ActionMirrorSyncDelete, repo, refName, nil) + return nil +} diff --git a/modules/mirror/sync.go b/modules/mirror/sync.go new file mode 100644 index 0000000000000..84d2c3152398a --- /dev/null +++ b/modules/mirror/sync.go @@ -0,0 +1,317 @@ +// Copyright 2018 The Gitea 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 mirror + +import ( + "fmt" + "strings" + "time" + + "code.gitea.io/git" + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/cache" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/process" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/sync" + "code.gitea.io/gitea/modules/util" + "github.com/Unknwon/com" + ini "gopkg.in/ini.v1" +) + +const ( + mirrorUpdate = "mirror_update" +) + +// MirrorQueue holds an UniqueQueue object of the mirror +var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength) + +// gitShortEmptySha Git short empty SHA +const gitShortEmptySha = "0000000" + +// mirrorSyncResult contains information of a updated reference. +// If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. +// If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty. +type mirrorSyncResult struct { + refName string + oldCommitID string + newCommitID string +} + +// parseRemoteUpdateOutput detects create, update and delete operations of references from upstream. +func parseRemoteUpdateOutput(output string) []*mirrorSyncResult { + results := make([]*mirrorSyncResult, 0, 3) + lines := strings.Split(output, "\n") + for i := range lines { + // Make sure reference name is presented before continue + idx := strings.Index(lines[i], "-> ") + if idx == -1 { + continue + } + + refName := lines[i][idx+3:] + + switch { + case strings.HasPrefix(lines[i], " * "): // New reference + results = append(results, &mirrorSyncResult{ + refName: refName, + oldCommitID: gitShortEmptySha, + }) + case strings.HasPrefix(lines[i], " - "): // Delete reference + results = append(results, &mirrorSyncResult{ + refName: refName, + newCommitID: gitShortEmptySha, + }) + case strings.HasPrefix(lines[i], " "): // New commits of a reference + delimIdx := strings.Index(lines[i][3:], " ") + if delimIdx == -1 { + log.Error(2, "SHA delimiter not found: %q", lines[i]) + continue + } + shas := strings.Split(lines[i][3:delimIdx+3], "..") + if len(shas) != 2 { + log.Error(2, "Expect two SHAs but not what found: %q", lines[i]) + continue + } + results = append(results, &mirrorSyncResult{ + refName: refName, + oldCommitID: shas[0], + newCommitID: shas[1], + }) + + default: + log.Warn("parseRemoteUpdateOutput: unexpected update line %q", lines[i]) + } + } + return results +} + +func remoteAddress(repoPath string) (string, error) { + cfg, err := ini.Load(models.GitConfigPath(repoPath)) + if err != nil { + return "", err + } + return cfg.Section("remote \"origin\"").Key("url").Value(), nil +} + +// sanitizeOutput sanitizes output of a command, replacing occurrences of the +// repository's remote address with a sanitized version. +func sanitizeOutput(output, repoPath string) (string, error) { + remoteAddr, err := remoteAddress(repoPath) + if err != nil { + // if we're unable to load the remote address, then we're unable to + // sanitize. + return "", err + } + return util.SanitizeMessage(output, remoteAddr), nil +} + +// runSync returns true if sync finished without error. +func runSync(m *models.Mirror) ([]*mirrorSyncResult, bool) { + repoPath := m.Repo.RepoPath() + wikiPath := m.Repo.WikiPath() + timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second + + gitArgs := []string{"remote", "update"} + if m.EnablePrune { + gitArgs = append(gitArgs, "--prune") + } + + _, stderr, err := process.GetManager().ExecDir( + timeout, repoPath, fmt.Sprintf("Mirror.runSync: %s", repoPath), + "git", gitArgs...) + if err != nil { + // sanitize the output, since it may contain the remote address, which may + // contain a password + message, err := sanitizeOutput(stderr, repoPath) + if err != nil { + log.Error(4, "sanitizeOutput: %v", err) + return nil, false + } + desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, message) + log.Error(4, desc) + if err = models.CreateRepositoryNotice(desc); err != nil { + log.Error(4, "CreateRepositoryNotice: %v", err) + } + return nil, false + } + output := stderr + + gitRepo, err := git.OpenRepository(repoPath) + if err != nil { + log.Error(4, "OpenRepository: %v", err) + return nil, false + } + if err = models.SyncReleasesWithTags(m.Repo, gitRepo); err != nil { + log.Error(4, "Failed to synchronize tags to releases for repository: %v", err) + } + + if err := m.Repo.UpdateSize(); err != nil { + log.Error(4, "Failed to update size for mirror repository: %v", err) + } + + if m.Repo.HasWiki() { + if _, stderr, err := process.GetManager().ExecDir( + timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath), + "git", "remote", "update", "--prune"); err != nil { + // sanitize the output, since it may contain the remote address, which may + // contain a password + message, err := sanitizeOutput(stderr, wikiPath) + if err != nil { + log.Error(4, "sanitizeOutput: %v", err) + return nil, false + } + desc := fmt.Sprintf("Failed to update mirror wiki repository '%s': %s", wikiPath, message) + log.Error(4, desc) + if err = models.CreateRepositoryNotice(desc); err != nil { + log.Error(4, "CreateRepositoryNotice: %v", err) + } + return nil, false + } + } + + branches, err := m.Repo.GetBranches() + if err != nil { + log.Error(4, "GetBranches: %v", err) + return nil, false + } + + for i := range branches { + cache.Remove(m.Repo.GetCommitsCountCacheKey(branches[i].Name, true)) + } + + m.UpdatedUnix = util.TimeStampNow() + return parseRemoteUpdateOutput(output), true +} + +// MirrorUpdate checks and updates mirror repositories. +func MirrorUpdate() { + if !models.TaskStartIfNotRunning(mirrorUpdate) { + return + } + defer models.TaskStop(mirrorUpdate) + + log.Trace("Doing: MirrorUpdate") + + if err := models.IterateNextMirrors(func(idx int, bean interface{}) error { + m := bean.(*models.Mirror) + if m.Repo == nil { + log.Error(4, "Disconnected mirror repository found: %d", m.ID) + return nil + } + + MirrorQueue.Add(m.RepoID) + return nil + }); err != nil { + log.Error(4, "MirrorUpdate: %v", err) + } +} + +// SyncMirrors checks and syncs mirrors. +// TODO: sync more mirrors at same time. +func SyncMirrors() { + // Start listening on new sync requests. + for repoID := range MirrorQueue.Queue() { + log.Trace("SyncMirrors [repo_id: %v]", repoID) + MirrorQueue.Remove(repoID) + + m, err := models.GetMirrorByRepoID(com.StrTo(repoID).MustInt64()) + if err != nil { + log.Error(4, "GetMirrorByRepoID [%s]: %v", repoID, err) + continue + } + + results, ok := runSync(m) + if !ok { + continue + } + + m.ScheduleNextUpdate() + if err = models.UpdateMirror(m); err != nil { + log.Error(4, "UpdateMirror [%s]: %v", repoID, err) + continue + } + + var gitRepo *git.Repository + if len(results) == 0 { + log.Trace("SyncMirrors [repo_id: %d]: no commits fetched", m.RepoID) + } else { + gitRepo, err = git.OpenRepository(m.Repo.RepoPath()) + if err != nil { + log.Error(2, "OpenRepository [%d]: %v", m.RepoID, err) + continue + } + } + + for _, result := range results { + // Discard GitHub pull requests, i.e. refs/pull/* + if strings.HasPrefix(result.refName, "refs/pull/") { + continue + } + + // Create reference + if result.oldCommitID == gitShortEmptySha { + if err = MirrorSyncCreateAction(m.Repo, result.refName); err != nil { + log.Error(2, "MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err) + } + continue + } + + // Delete reference + if result.newCommitID == gitShortEmptySha { + if err = MirrorSyncDeleteAction(m.Repo, result.refName); err != nil { + log.Error(2, "MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err) + } + continue + } + + // Push commits + oldCommitID, err := git.GetFullCommitID(gitRepo.Path, result.oldCommitID) + if err != nil { + log.Error(2, "GetFullCommitID [%d]: %v", m.RepoID, err) + continue + } + newCommitID, err := git.GetFullCommitID(gitRepo.Path, result.newCommitID) + if err != nil { + log.Error(2, "GetFullCommitID [%d]: %v", m.RepoID, err) + continue + } + commits, err := gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) + if err != nil { + log.Error(2, "CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err) + continue + } + if err = MirrorSyncPushAction(m.Repo, MirrorSyncPushActionOptions{ + RefName: result.refName, + OldCommitID: oldCommitID, + NewCommitID: newCommitID, + Commits: models.ListToPushCommits(commits), + }); err != nil { + log.Error(2, "MirrorSyncPushAction [repo_id: %d]: %v", m.RepoID, err) + continue + } + } + + // Get latest commit date and update to current repository updated time + commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath()) + if err != nil { + log.Error(2, "GetLatestCommitDate [%s]: %v", m.RepoID, err) + continue + } + + if err = models.UpdateRepositoryCols(&models.Repository{ + ID: m.RepoID, + UpdatedUnix: util.TimeStamp(commitDate.Unix()), + }, "updated_unix"); err != nil { + log.Error(2, "Update repository 'updated_unix' [%s]: %v", m.RepoID, err) + continue + } + } +} + +// InitSyncMirrors initializes a go routine to sync the mirrors +func InitSyncMirrors() { + go SyncMirrors() +} diff --git a/modules/notification/action/action.go b/modules/notification/action/action.go new file mode 100644 index 0000000000000..a7c48db5d62d3 --- /dev/null +++ b/modules/notification/action/action.go @@ -0,0 +1,206 @@ +// Copyright 2018 The Gitea 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 action + +import ( + "fmt" + "path" + "strings" + + "code.gitea.io/git" + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/notification/base" +) + +type actionNotifier struct { + base.NullNotifier +} + +var ( + _ base.Notifier = &actionNotifier{} +) + +// NewNotifier returns a new actionNotifier +func NewNotifier() base.Notifier { + return &actionNotifier{} +} + +func (r *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, + issue *models.Issue, comment *models.Comment) { + act := &models.Action{ + ActUserID: doer.ID, + ActUser: doer, + Content: fmt.Sprintf("%d|%s", issue.Index, strings.Split(comment.Content, "\n")[0]), + RepoID: repo.ID, + Repo: repo, + Comment: comment, + CommentID: comment.ID, + IsPrivate: repo.IsPrivate, + } + // Check comment type. + switch comment.Type { + case models.CommentTypeComment: + act.OpType = models.ActionCommentIssue + case models.CommentTypeReopen: + act.OpType = models.ActionReopenIssue + if issue.IsPull { + act.OpType = models.ActionReopenPullRequest + } + case models.CommentTypeClose: + act.OpType = models.ActionCloseIssue + if issue.IsPull { + act.OpType = models.ActionClosePullRequest + } + } + + if act.OpType > 0 { + if err := models.NotifyWatchers(act); err != nil { + log.Error(4, "notifyWatchers: %v", err) + } + } +} + +func (r *actionNotifier) NotifyNewIssue(issue *models.Issue) { + if err := models.NotifyWatchers(&models.Action{ + ActUserID: issue.Poster.ID, + ActUser: issue.Poster, + OpType: models.ActionCreateIssue, + Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), + RepoID: issue.Repo.ID, + Repo: issue.Repo, + IsPrivate: issue.Repo.IsPrivate, + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyNewPullRequest(pr *models.PullRequest) { + issue := pr.Issue + if err := models.NotifyWatchers(&models.Action{ + ActUserID: issue.Poster.ID, + ActUser: issue.Poster, + OpType: models.ActionCreatePullRequest, + Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), + RepoID: issue.Repo.ID, + Repo: issue.Repo, + IsPrivate: issue.Repo.IsPrivate, + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User, baseRepo *git.Repository) { + repo := pr.Issue.Repo + issue := pr.Issue + if err := models.NotifyWatchers(&models.Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: models.ActionMergePullRequest, + Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) { + if err := models.NotifyWatchers(&models.Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: models.ActionCreateRepo, + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyRepositoryChangedName(doer *models.User, oldRepoName string, repo *models.Repository) { + if err := models.NotifyWatchers(&models.Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: models.ActionRenameRepo, + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + Content: oldRepoName, + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyRepositoryTransfered(doer *models.User, oldOwner *models.User, newRepo *models.Repository) { + if err := models.NotifyWatchers(&models.Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: models.ActionTransferRepo, + RepoID: newRepo.ID, + Repo: newRepo, + IsPrivate: newRepo.IsPrivate, + Content: path.Join(oldOwner.Name, newRepo.Name), + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) { + if err := models.NotifyWatchers(&models.Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: models.ActionCreateRepo, + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) { + if err := models.NotifyWatchers(&models.Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: models.ActionCreateRepo, + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyRepoMirrorSync(opType models.ActionType, repo *models.Repository, refName string, data []byte) { + if err := models.NotifyWatchers(&models.Action{ + ActUserID: repo.OwnerID, + ActUser: repo.MustOwner(), + OpType: opType, + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + RefName: refName, + Content: string(data), + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} + +func (r *actionNotifier) NotifyCommitsPushed(pusher *models.User, opType models.ActionType, repo *models.Repository, refName string, data []byte) { + if err := models.NotifyWatchers(&models.Action{ + ActUserID: pusher.ID, + ActUser: pusher, + OpType: opType, + Content: string(data), + RepoID: repo.ID, + Repo: repo, + RefName: refName, + IsPrivate: repo.IsPrivate, + }); err != nil { + log.Error(4, "NotifyWatchers: %v", err) + } +} diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go index e44f3cc63216f..ac704170a94c8 100644 --- a/modules/notification/base/notifier.go +++ b/modules/notification/base/notifier.go @@ -14,9 +14,12 @@ type Notifier interface { Run() NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) + NotifyRepositoryChangedName(doer *models.User, oldRepoName string, repo *models.Repository) + NotifyRepositoryTransfered(doer *models.User, oldOwner *models.User, newRepo *models.Repository) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) NotifyDeleteRepository(doer *models.User, repo *models.Repository) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) + NotifyRepoMirrorSync(opType models.ActionType, repo *models.Repository, refName string, data []byte) NotifyNewIssue(*models.Issue) NotifyIssueChangeStatus(*models.User, *models.Issue, bool) @@ -40,4 +43,6 @@ type Notifier interface { NotifyNewRelease(rel *models.Release) NotifyUpdateRelease(doer *models.User, rel *models.Release) NotifyDeleteRelease(doer *models.User, rel *models.Release) + + NotifyCommitsPushed(pusher *models.User, opType models.ActionType, repo *models.Repository, refName string, data []byte) } diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index 12be1999f9019..9148f52dea501 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -106,3 +106,19 @@ func (*NullNotifier) NotifyCreateRepository(doer *models.User, u *models.User, r // NotifyMigrateRepository places a place holder function func (*NullNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) { } + +// NotifyRepositoryTransfered places a place holder function +func (*NullNotifier) NotifyRepositoryTransfered(doer *models.User, oldOwner *models.User, newRepo *models.Repository) { +} + +// NotifyRepositoryChangedName places a place holder function +func (*NullNotifier) NotifyRepositoryChangedName(doer *models.User, oldRepoName string, repo *models.Repository) { +} + +// NotifyRepoMirrorSync places a place holder function +func (*NullNotifier) NotifyRepoMirrorSync(opType models.ActionType, repo *models.Repository, refName string, data []byte) { +} + +// NotifyCommitsPushed places a place holder function +func (*NullNotifier) NotifyCommitsPushed(pusher *models.User, opType models.ActionType, repo *models.Repository, refName string, data []byte) { +} diff --git a/modules/notification/notification.go b/modules/notification/notification.go index e0de88346d75f..6b71e431333ff 100644 --- a/modules/notification/notification.go +++ b/modules/notification/notification.go @@ -7,6 +7,7 @@ package notification import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/notification/action" "code.gitea.io/gitea/modules/notification/base" "code.gitea.io/gitea/modules/notification/indexer" "code.gitea.io/gitea/modules/notification/mail" @@ -27,6 +28,7 @@ func init() { RegisterNotifier(ui.NewNotifier()) RegisterNotifier(mail.NewNotifier()) RegisterNotifier(indexer.NewNotifier()) + RegisterNotifier(action.NewNotifier()) } // NotifyCreateIssueComment notifies issue comment related message to notifiers @@ -171,9 +173,37 @@ func NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repo } } -// NotifyMigrateRepository notifies create repository to notifiers +// NotifyMigrateRepository notifies migrate repository to notifiers func NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) { for _, notifier := range notifiers { notifier.NotifyMigrateRepository(doer, u, repo) } } + +// NotifyRepositoryChangedName notifies change repository name to notifiers +func NotifyRepositoryChangedName(doer *models.User, oldRepoName string, repo *models.Repository) { + for _, notifier := range notifiers { + notifier.NotifyRepositoryChangedName(doer, oldRepoName, repo) + } +} + +// NotifyRepositoryTransfered notifies transfer repository to notifiers +func NotifyRepositoryTransfered(doer *models.User, oldOwner *models.User, newRepo *models.Repository) { + for _, notifier := range notifiers { + notifier.NotifyRepositoryTransfered(doer, oldOwner, newRepo) + } +} + +// NotifyRepoMirrorSync notifies mirror repository sync to notifiers +func NotifyRepoMirrorSync(opType models.ActionType, repo *models.Repository, refName string, data []byte) { + for _, notifier := range notifiers { + notifier.NotifyRepoMirrorSync(opType, repo, refName, data) + } +} + +// NotifyCommitsPushed notifies when commits push to notifiers +func NotifyCommitsPushed(pusher *models.User, opType models.ActionType, repo *models.Repository, refName string, data []byte) { + for _, notifier := range notifiers { + notifier.NotifyCommitsPushed(pusher, opType, repo, refName, data) + } +} diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index d10f668712ab2..63c804c972b0c 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -7,6 +7,7 @@ package repo import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/notification" api "code.gitea.io/sdk/gitea" ) @@ -103,5 +104,8 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) { ctx.Error(500, "ForkRepository", err) return } + + notification.NotifyForkRepository(ctx.User, repo, fork) + ctx.JSON(202, fork.APIFormat(models.AccessModeOwner)) } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 7abe6241bdcf8..74f0bffb5f33b 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -590,6 +590,8 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) { return } + notification.NotifyMergePullRequest(pr, ctx.User, ctx.Repo.GitRepo) + log.Trace("Pull request merged: %d", pr.ID) ctx.Status(200) } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 36e8380f44b1a..aa7d0b1756bdb 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -14,6 +14,8 @@ import ( "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/mirror" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/convert" @@ -242,6 +244,8 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR return } + notification.NotifyCreateRepository(ctx.User, owner, repo) + ctx.JSON(201, repo.APIFormat(models.AccessModeOwner)) } @@ -424,6 +428,8 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { return } + notification.NotifyMigrateRepository(ctx.User, ctxUser, repo) + log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName) ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin)) } @@ -563,7 +569,7 @@ func MirrorSync(ctx *context.APIContext) { ctx.Error(403, "MirrorSync", "Must have write access") } - go models.MirrorQueue.Add(repo.ID) + go mirror.MirrorQueue.Add(repo.ID) ctx.Status(200) } diff --git a/routers/init.go b/routers/init.go index 3b8d8ac908c46..859a8c52d8ed2 100644 --- a/routers/init.go +++ b/routers/init.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/mailer" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/mirror" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/ssh" @@ -95,7 +96,7 @@ func GlobalInit() { log.Fatal(4, "Failed to initialize issue indexer: %v", err) } models.InitRepoIndexer() - models.InitSyncMirrors() + mirror.InitSyncMirrors() models.InitDeliverHooks() models.InitTestPullRequests() log.NewGitLogger(path.Join(setting.LogRootPath, "http.log")) diff --git a/routers/private/push_update.go b/routers/private/push_update.go index 5c42f066ee7d5..b275f219868fd 100644 --- a/routers/private/push_update.go +++ b/routers/private/push_update.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/notification" macaron "gopkg.in/macaron.v1" ) @@ -32,7 +33,7 @@ func PushUpdate(ctx *macaron.Context) { return } - err := models.PushUpdate(branch, opt) + event, err := models.PushUpdate(branch, opt) if err != nil { if models.IsErrUserNotExist(err) { ctx.Error(404) @@ -43,5 +44,8 @@ func PushUpdate(ctx *macaron.Context) { } return } + + notification.NotifyCommitsPushed(event.Pusher, event.OpType, event.Repo, event.RefName, event.Data) + ctx.Status(202) } diff --git a/routers/repo/branch.go b/routers/repo/branch.go index 19925c1edceed..38f2dcff44796 100644 --- a/routers/repo/branch.go +++ b/routers/repo/branch.go @@ -128,7 +128,7 @@ func deleteBranch(ctx *context.Context, branchName string) error { } // Don't return error below this - if err := models.PushUpdate(branchName, models.PushUpdateOptions{ + if _, err := models.PushUpdate(branchName, models.PushUpdateOptions{ RefFullName: git.BranchPrefix + branchName, OldCommitID: commit.ID.String(), NewCommitID: git.EmptySHA, diff --git a/routers/repo/editor.go b/routers/repo/editor.go index 54046258b8435..5cdd434156b83 100644 --- a/routers/repo/editor.go +++ b/routers/repo/editor.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/uploader" @@ -309,7 +310,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo message += "\n\n" + form.CommitMessage } - if err := uploader.UpdateRepoFile(ctx.Repo.Repository, ctx.User, &uploader.UpdateRepoFileOptions{ + evt, err := uploader.UpdateRepoFile(ctx.Repo.Repository, ctx.User, &uploader.UpdateRepoFileOptions{ LastCommitID: lastCommit, OldBranch: oldBranchName, NewBranch: branchName, @@ -318,12 +319,15 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo Message: message, Content: strings.Replace(form.Content, "\r", "", -1), IsNewFile: isNewFile, - }); err != nil { + }) + if err != nil { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.fail_to_update_file", form.TreePath, err), tplEditFile, &form) return } + notification.NotifyCommitsPushed(evt.Pusher, evt.OpType, evt.Repo, evt.RefName, evt.Data) + ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(form.TreePath)) } @@ -448,17 +452,20 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) { message += "\n\n" + form.CommitMessage } - if err := uploader.DeleteRepoFile(ctx.Repo.Repository, ctx.User, &uploader.DeleteRepoFileOptions{ + evt, err := uploader.DeleteRepoFile(ctx.Repo.Repository, ctx.User, &uploader.DeleteRepoFileOptions{ LastCommitID: ctx.Repo.CommitID, OldBranch: oldBranchName, NewBranch: branchName, TreePath: ctx.Repo.TreePath, Message: message, - }); err != nil { + }) + if err != nil { ctx.ServerError("DeleteRepoFile", err) return } + notification.NotifyCommitsPushed(evt.Pusher, evt.OpType, evt.Repo, evt.RefName, evt.Data) + ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath)) ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName) } @@ -583,19 +590,22 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) { message += "\n\n" + form.CommitMessage } - if err := uploader.UploadRepoFiles(ctx.Repo.Repository, ctx.User, &uploader.UploadRepoFileOptions{ + evt, err := uploader.UploadRepoFiles(ctx.Repo.Repository, ctx.User, &uploader.UploadRepoFileOptions{ LastCommitID: ctx.Repo.CommitID, OldBranch: oldBranchName, NewBranch: branchName, TreePath: form.TreePath, Message: message, Files: form.Files, - }); err != nil { + }) + if err != nil { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.unable_to_upload_files", form.TreePath, err), tplUploadFile, &form) return } + notification.NotifyCommitsPushed(evt.Pusher, evt.OpType, evt.Repo, evt.RefName, evt.Data) + ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + form.TreePath) } diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 08c833c1671da..85932c582d8ac 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -209,6 +209,8 @@ func ForkPost(ctx *context.Context, form auth.CreateRepoForm) { return } + notification.NotifyForkRepository(ctx.User, forkRepo, repo) + log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name) ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name) } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 5add788fc8759..dd096e1e81fe5 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -175,6 +176,8 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { AutoInit: form.AutoInit, }) if err == nil { + notification.NotifyCreateRepository(ctx.User, ctxUser, repo) + log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name) ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name) return @@ -250,6 +253,8 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { RemoteAddr: remoteAddr, }) if err == nil { + notification.NotifyMigrateRepository(ctx.User, ctxUser, repo) + log.Trace("Repository migrated [%d]: %s/%s", repo.ID, ctxUser.Name, form.RepoName) ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + form.RepoName) return diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 2520b57bc7c32..bf1410ed0712a 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -16,6 +16,8 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/mirror" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" @@ -109,9 +111,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) if isNameChanged { - if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil { - log.Error(4, "RenameRepoAction: %v", err) - } + notification.NotifyRepositoryChangedName(ctx.User, oldRepoName, repo) } ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) @@ -159,7 +159,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } - go models.MirrorQueue.Add(repo.ID) + go mirror.MirrorQueue.Add(repo.ID) ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress")) ctx.Redirect(repo.Link() + "/settings") @@ -345,6 +345,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { ctx.ServerError("NewRepoRedirect", err) return } + notification.NotifyRepositoryTransfered(ctx.User, ctx.Repo.Owner, repo) log.Trace("Repository transferred: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed")) From bc72f085be99d18074040b6c9aa4b6c30b605f74 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Oct 2018 13:59:07 +0800 Subject: [PATCH 02/13] fix tests --- models/action.go | 11 ----------- models/action_test.go | 30 ++---------------------------- models/repo.go | 9 +++++++-- 3 files changed, 9 insertions(+), 41 deletions(-) diff --git a/models/action.go b/models/action.go index 4c3619ab54e32..79b6936992efd 100644 --- a/models/action.go +++ b/models/action.go @@ -705,17 +705,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) (*CommitRepoEvent, error) { return &event, nil } -func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) { - // Remove watch for organization. - if oldOwner.IsOrganization() { - if err = watchRepo(e, oldOwner.ID, repo.ID, false); err != nil { - return fmt.Errorf("watchRepo [false]: %v", err) - } - } - - return nil -} - // GetFeedsOptions options for retrieving feeds type GetFeedsOptions struct { RequestedUser *User diff --git a/models/action_test.go b/models/action_test.go index ea4c78ce1183c..08bd50d175609 100644 --- a/models/action_test.go +++ b/models/action_test.go @@ -243,7 +243,8 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) { func testCorrectRepoAction(t *testing.T, opts CommitRepoActionOptions, actionBean *Action) { AssertNotExistsBean(t, actionBean) - assert.NoError(t, CommitRepoAction(opts)) + _, err := CommitRepoAction(opts) + assert.NoError(t, err) AssertExistsAndLoadBean(t, actionBean) CheckConsistencyFor(t, &Action{}) } @@ -354,33 +355,6 @@ func TestCommitRepoAction(t *testing.T) { } } -func TestTransferRepoAction(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) - - user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{ID: 1, OwnerID: user2.ID}).(*Repository) - - repo.OwnerID = user4.ID - repo.Owner = user4 - - actionBean := &Action{ - OpType: ActionTransferRepo, - ActUserID: user2.ID, - ActUser: user2, - RepoID: repo.ID, - Repo: repo, - IsPrivate: repo.IsPrivate, - } - AssertNotExistsBean(t, actionBean) - assert.NoError(t, TransferRepoAction(user2, user2, repo)) - AssertExistsAndLoadBean(t, actionBean) - - _, err := x.ID(repo.ID).Cols("owner_id").Update(repo) - assert.NoError(t, err) - CheckConsistencyFor(t, &Action{}) -} - func TestGetFeeds(t *testing.T) { // test with an individual user assert.NoError(t, PrepareTestDatabase()) diff --git a/models/repo.go b/models/repo.go index 920f8721849bf..247d6e9441251 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1542,8 +1542,13 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error if err = watchRepo(sess, doer.ID, repo.ID, true); err != nil { return fmt.Errorf("watchRepo: %v", err) - } else if err = transferRepoAction(sess, doer, owner, repo); err != nil { - return fmt.Errorf("transferRepoAction: %v", err) + } + + // Remove watch for organization. + if owner.IsOrganization() { + if err = watchRepo(sess, owner.ID, repo.ID, false); err != nil { + return fmt.Errorf("watchRepo [false]: %v", err) + } } // Rename remote repository to new path and delete local copy. From a8c60020841de28ae391f2103960594b8a0842e2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Oct 2018 14:02:23 +0800 Subject: [PATCH 03/13] fix lint --- models/repo.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/models/repo.go b/models/repo.go index 247d6e9441251..0e3e4fe26b1d8 100644 --- a/models/repo.go +++ b/models/repo.go @@ -2230,10 +2230,13 @@ const ( archiveCleanup = "archive_cleanup" ) +// TaskStartIfNotRunning sets value of given name to true if not already in pool. +// Returns whether set value was set to true func TaskStartIfNotRunning(name string) bool { return taskStatusTable.StartIfNotRunning(name) } +// TaskStop sets value of given name to false in the pool. func TaskStop(name string) { taskStatusTable.Stop(name) } From 541b4eae841a2be2682bbbeedaacb297ef53d700 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Oct 2018 14:17:50 +0800 Subject: [PATCH 04/13] fix lint --- modules/cron/cron.go | 4 ++-- modules/mirror/action.go | 16 +++++++------- modules/mirror/sync.go | 42 ++++++++++++++++++------------------- routers/api/v1/repo/repo.go | 2 +- routers/repo/setting.go | 2 +- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/modules/cron/cron.go b/modules/cron/cron.go index a8aeacbfd2f32..81376fc27bebb 100644 --- a/modules/cron/cron.go +++ b/modules/cron/cron.go @@ -24,14 +24,14 @@ func NewContext() { err error ) if setting.Cron.UpdateMirror.Enabled { - entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, mirror.MirrorUpdate) + entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, mirror.Update) if err != nil { log.Fatal(4, "Cron[Update mirrors]: %v", err) } if setting.Cron.UpdateMirror.RunAtStart { entry.Prev = time.Now() entry.ExecTimes++ - go mirror.MirrorUpdate() + go mirror.Update() } } if setting.Cron.RepoHealthCheck.Enabled { diff --git a/modules/mirror/action.go b/modules/mirror/action.go index 405d13635c8fc..09ddb08785ea3 100644 --- a/modules/mirror/action.go +++ b/modules/mirror/action.go @@ -14,16 +14,16 @@ import ( api "code.gitea.io/sdk/gitea" ) -// MirrorSyncPushActionOptions mirror synchronization action options. -type MirrorSyncPushActionOptions struct { +// SyncPushActionOptions mirror synchronization action options. +type SyncPushActionOptions struct { RefName string OldCommitID string NewCommitID string Commits *models.PushCommits } -// MirrorSyncPushAction adds new action for mirror synchronization of pushed commits. -func MirrorSyncPushAction(repo *models.Repository, opts MirrorSyncPushActionOptions) error { +// SyncPushAction adds new action for mirror synchronization of pushed commits. +func SyncPushAction(repo *models.Repository, opts SyncPushActionOptions) error { if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum { opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum] } @@ -54,14 +54,14 @@ func MirrorSyncPushAction(repo *models.Repository, opts MirrorSyncPushActionOpti return nil } -// MirrorSyncCreateAction adds new action for mirror synchronization of new reference. -func MirrorSyncCreateAction(repo *models.Repository, refName string) error { +// SyncCreateAction adds new action for mirror synchronization of new reference. +func SyncCreateAction(repo *models.Repository, refName string) error { notification.NotifyRepoMirrorSync(models.ActionMirrorSyncCreate, repo, refName, nil) return nil } -// MirrorSyncDeleteAction adds new action for mirror synchronization of delete reference. -func MirrorSyncDeleteAction(repo *models.Repository, refName string) error { +// SyncDeleteAction adds new action for mirror synchronization of delete reference. +func SyncDeleteAction(repo *models.Repository, refName string) error { notification.NotifyRepoMirrorSync(models.ActionMirrorSyncDelete, repo, refName, nil) return nil } diff --git a/modules/mirror/sync.go b/modules/mirror/sync.go index 84d2c3152398a..bada8f8084b57 100644 --- a/modules/mirror/sync.go +++ b/modules/mirror/sync.go @@ -22,27 +22,27 @@ import ( ) const ( - mirrorUpdate = "mirror_update" + updateCronName = "mirror_update" ) -// MirrorQueue holds an UniqueQueue object of the mirror -var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength) +// Queue holds an UniqueQueue object of the mirror +var Queue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength) // gitShortEmptySha Git short empty SHA const gitShortEmptySha = "0000000" -// mirrorSyncResult contains information of a updated reference. +// syncResult contains information of a updated reference. // If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. // If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty. -type mirrorSyncResult struct { +type syncResult struct { refName string oldCommitID string newCommitID string } // parseRemoteUpdateOutput detects create, update and delete operations of references from upstream. -func parseRemoteUpdateOutput(output string) []*mirrorSyncResult { - results := make([]*mirrorSyncResult, 0, 3) +func parseRemoteUpdateOutput(output string) []*syncResult { + results := make([]*syncResult, 0, 3) lines := strings.Split(output, "\n") for i := range lines { // Make sure reference name is presented before continue @@ -55,12 +55,12 @@ func parseRemoteUpdateOutput(output string) []*mirrorSyncResult { switch { case strings.HasPrefix(lines[i], " * "): // New reference - results = append(results, &mirrorSyncResult{ + results = append(results, &syncResult{ refName: refName, oldCommitID: gitShortEmptySha, }) case strings.HasPrefix(lines[i], " - "): // Delete reference - results = append(results, &mirrorSyncResult{ + results = append(results, &syncResult{ refName: refName, newCommitID: gitShortEmptySha, }) @@ -75,7 +75,7 @@ func parseRemoteUpdateOutput(output string) []*mirrorSyncResult { log.Error(2, "Expect two SHAs but not what found: %q", lines[i]) continue } - results = append(results, &mirrorSyncResult{ + results = append(results, &syncResult{ refName: refName, oldCommitID: shas[0], newCommitID: shas[1], @@ -109,7 +109,7 @@ func sanitizeOutput(output, repoPath string) (string, error) { } // runSync returns true if sync finished without error. -func runSync(m *models.Mirror) ([]*mirrorSyncResult, bool) { +func runSync(m *models.Mirror) ([]*syncResult, bool) { repoPath := m.Repo.RepoPath() wikiPath := m.Repo.WikiPath() timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second @@ -186,12 +186,12 @@ func runSync(m *models.Mirror) ([]*mirrorSyncResult, bool) { return parseRemoteUpdateOutput(output), true } -// MirrorUpdate checks and updates mirror repositories. -func MirrorUpdate() { - if !models.TaskStartIfNotRunning(mirrorUpdate) { +// Update checks and updates mirror repositories. +func Update() { + if !models.TaskStartIfNotRunning(updateCronName) { return } - defer models.TaskStop(mirrorUpdate) + defer models.TaskStop(updateCronName) log.Trace("Doing: MirrorUpdate") @@ -202,7 +202,7 @@ func MirrorUpdate() { return nil } - MirrorQueue.Add(m.RepoID) + Queue.Add(m.RepoID) return nil }); err != nil { log.Error(4, "MirrorUpdate: %v", err) @@ -213,9 +213,9 @@ func MirrorUpdate() { // TODO: sync more mirrors at same time. func SyncMirrors() { // Start listening on new sync requests. - for repoID := range MirrorQueue.Queue() { + for repoID := range Queue.Queue() { log.Trace("SyncMirrors [repo_id: %v]", repoID) - MirrorQueue.Remove(repoID) + Queue.Remove(repoID) m, err := models.GetMirrorByRepoID(com.StrTo(repoID).MustInt64()) if err != nil { @@ -253,7 +253,7 @@ func SyncMirrors() { // Create reference if result.oldCommitID == gitShortEmptySha { - if err = MirrorSyncCreateAction(m.Repo, result.refName); err != nil { + if err = SyncCreateAction(m.Repo, result.refName); err != nil { log.Error(2, "MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err) } continue @@ -261,7 +261,7 @@ func SyncMirrors() { // Delete reference if result.newCommitID == gitShortEmptySha { - if err = MirrorSyncDeleteAction(m.Repo, result.refName); err != nil { + if err = SyncDeleteAction(m.Repo, result.refName); err != nil { log.Error(2, "MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err) } continue @@ -283,7 +283,7 @@ func SyncMirrors() { log.Error(2, "CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err) continue } - if err = MirrorSyncPushAction(m.Repo, MirrorSyncPushActionOptions{ + if err = SyncPushAction(m.Repo, SyncPushActionOptions{ RefName: result.refName, OldCommitID: oldCommitID, NewCommitID: newCommitID, diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index aa7d0b1756bdb..23730c7faa588 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -569,7 +569,7 @@ func MirrorSync(ctx *context.APIContext) { ctx.Error(403, "MirrorSync", "Must have write access") } - go mirror.MirrorQueue.Add(repo.ID) + go mirror.Queue.Add(repo.ID) ctx.Status(200) } diff --git a/routers/repo/setting.go b/routers/repo/setting.go index bf1410ed0712a..f3ad1d5f46383 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -159,7 +159,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } - go mirror.MirrorQueue.Add(repo.ID) + go mirror.Queue.Add(repo.ID) ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress")) ctx.Redirect(repo.Link() + "/settings") From c4c0a6dfb0d14e48c1a102a408ec7a9b3c7212d1 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Oct 2018 14:30:44 +0800 Subject: [PATCH 05/13] fix tests --- models/action_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/action_test.go b/models/action_test.go index 08bd50d175609..af2f10ff201a8 100644 --- a/models/action_test.go +++ b/models/action_test.go @@ -6,6 +6,7 @@ import ( "testing" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -243,7 +244,8 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) { func testCorrectRepoAction(t *testing.T, opts CommitRepoActionOptions, actionBean *Action) { AssertNotExistsBean(t, actionBean) - _, err := CommitRepoAction(opts) + evt, err := CommitRepoAction(opts) + notification.NotifyCommitsPushed(evt.Pusher, evt.OpType, evt.Repo, evt.RefName, evt.Data) assert.NoError(t, err) AssertExistsAndLoadBean(t, actionBean) CheckConsistencyFor(t, &Action{}) From 27d8cdc871a44f40918e0ce1932ee860423ed35e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Oct 2018 14:32:51 +0800 Subject: [PATCH 06/13] fix tests --- models/repo_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/models/repo_test.go b/models/repo_test.go index 752ffc2dd9085..d6e8717fb5e2c 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -9,6 +9,7 @@ import ( "testing" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "github.com/Unknwon/com" @@ -162,6 +163,8 @@ func TestTransferOwnership(t *testing.T) { repo.Owner = AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) assert.NoError(t, TransferOwnership(doer, "user2", repo)) + notification.NotifyRepositoryTransfered(doer, doer, repo) + transferredRepo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) assert.EqualValues(t, 2, transferredRepo.OwnerID) From 95aa7dda2a72bd0e204fe825ab85181e139c5d21 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Oct 2018 14:35:30 +0800 Subject: [PATCH 07/13] fix tests --- models/action_test.go | 117 ------------------------------------------ models/repo_test.go | 9 ---- 2 files changed, 126 deletions(-) diff --git a/models/action_test.go b/models/action_test.go index af2f10ff201a8..74c6db2fa5a52 100644 --- a/models/action_test.go +++ b/models/action_test.go @@ -5,8 +5,6 @@ import ( "path" "testing" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -242,121 +240,6 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) { CheckConsistencyFor(t, &Action{}) } -func testCorrectRepoAction(t *testing.T, opts CommitRepoActionOptions, actionBean *Action) { - AssertNotExistsBean(t, actionBean) - evt, err := CommitRepoAction(opts) - notification.NotifyCommitsPushed(evt.Pusher, evt.OpType, evt.Repo, evt.RefName, evt.Data) - assert.NoError(t, err) - AssertExistsAndLoadBean(t, actionBean) - CheckConsistencyFor(t, &Action{}) -} - -func TestCommitRepoAction(t *testing.T) { - samples := []struct { - userID int64 - repositoryID int64 - commitRepoActionOptions CommitRepoActionOptions - action Action - }{ - { - userID: 2, - repositoryID: 2, - commitRepoActionOptions: CommitRepoActionOptions{ - RefFullName: "refName", - OldCommitID: "oldCommitID", - NewCommitID: "newCommitID", - Commits: &PushCommits{ - avatars: make(map[string]string), - Commits: []*PushCommit{ - { - Sha1: "abcdef1", - CommitterEmail: "user2@example.com", - CommitterName: "User Two", - AuthorEmail: "user4@example.com", - AuthorName: "User Four", - Message: "message1", - }, - { - Sha1: "abcdef2", - CommitterEmail: "user2@example.com", - CommitterName: "User Two", - AuthorEmail: "user2@example.com", - AuthorName: "User Two", - Message: "message2", - }, - }, - Len: 2, - }, - }, - action: Action{ - OpType: ActionCommitRepo, - RefName: "refName", - }, - }, - { - userID: 2, - repositoryID: 1, - commitRepoActionOptions: CommitRepoActionOptions{ - RefFullName: git.TagPrefix + "v1.1", - OldCommitID: git.EmptySHA, - NewCommitID: "newCommitID", - Commits: &PushCommits{}, - }, - action: Action{ - OpType: ActionPushTag, - RefName: "v1.1", - }, - }, - { - userID: 2, - repositoryID: 1, - commitRepoActionOptions: CommitRepoActionOptions{ - RefFullName: git.TagPrefix + "v1.1", - OldCommitID: "oldCommitID", - NewCommitID: git.EmptySHA, - Commits: &PushCommits{}, - }, - action: Action{ - OpType: ActionDeleteTag, - RefName: "v1.1", - }, - }, - { - userID: 2, - repositoryID: 1, - commitRepoActionOptions: CommitRepoActionOptions{ - RefFullName: git.BranchPrefix + "feature/1", - OldCommitID: "oldCommitID", - NewCommitID: git.EmptySHA, - Commits: &PushCommits{}, - }, - action: Action{ - OpType: ActionDeleteBranch, - RefName: "feature/1", - }, - }, - } - - for _, s := range samples { - PrepareTestEnv(t) - - user := AssertExistsAndLoadBean(t, &User{ID: s.userID}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{ID: s.repositoryID, OwnerID: user.ID}).(*Repository) - repo.Owner = user - - s.commitRepoActionOptions.PusherName = user.Name - s.commitRepoActionOptions.RepoOwnerID = user.ID - s.commitRepoActionOptions.RepoName = repo.Name - - s.action.ActUserID = user.ID - s.action.RepoID = repo.ID - s.action.Repo = repo - s.action.IsPrivate = repo.IsPrivate - - testCorrectRepoAction(t, s.commitRepoActionOptions, &s.action) - } -} - func TestGetFeeds(t *testing.T) { // test with an individual user assert.NoError(t, PrepareTestDatabase()) diff --git a/models/repo_test.go b/models/repo_test.go index d6e8717fb5e2c..17cf0df0c1c2e 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -9,7 +9,6 @@ import ( "testing" "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "github.com/Unknwon/com" @@ -163,19 +162,11 @@ func TestTransferOwnership(t *testing.T) { repo.Owner = AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) assert.NoError(t, TransferOwnership(doer, "user2", repo)) - notification.NotifyRepositoryTransfered(doer, doer, repo) - transferredRepo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) assert.EqualValues(t, 2, transferredRepo.OwnerID) assert.False(t, com.IsExist(RepoPath("user3", "repo3"))) assert.True(t, com.IsExist(RepoPath("user2", "repo3"))) - AssertExistsAndLoadBean(t, &Action{ - OpType: ActionTransferRepo, - ActUserID: 2, - RepoID: 3, - Content: "user3/repo3", - }) CheckConsistencyFor(t, &Repository{}, &User{}, &Team{}) } From 05f36260ab75b6c6bd468fc199fa790e1e8e362f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 9 Jan 2019 16:07:11 +0800 Subject: [PATCH 08/13] add tests --- models/release_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/models/release_test.go b/models/release_test.go index 39c6613054fc6..48acb4f1f2510 100644 --- a/models/release_test.go +++ b/models/release_test.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/modules/git" + "github.com/Unknwon/com" "github.com/stretchr/testify/assert" ) @@ -17,10 +18,13 @@ func TestRelease_Create(t *testing.T) { user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - repoPath := RepoPath(user.Name, repo.Name) + + repoPath := repo.LocalCopyPath() + assert.True(t, com.IsExist(repoPath)) gitRepo, err := git.OpenRepository(repoPath) assert.NoError(t, err) + assert.NotNil(t, gitRepo) assert.NoError(t, CreateRelease(gitRepo, &Release{ RepoID: repo.ID, From bd8b207a7c56487402bb65a94adbceb1996ed504 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 2 Mar 2019 10:13:00 +0800 Subject: [PATCH 09/13] fix merge --- models/repo_branch.go | 40 ----------- models/repo_editor.go | 140 ------------------------------------- modules/uploader/delete.go | 28 ++++---- modules/uploader/update.go | 42 +++++------ modules/uploader/upload.go | 46 ++++++------ 5 files changed, 58 insertions(+), 238 deletions(-) diff --git a/models/repo_branch.go b/models/repo_branch.go index 9ea4ce45fbb33..231c66cdd4ed1 100644 --- a/models/repo_branch.go +++ b/models/repo_branch.go @@ -14,46 +14,6 @@ import ( "github.com/Unknwon/com" ) -// discardLocalRepoBranchChanges discards local commits/changes of -// given branch to make sure it is even to remote branch. -func discardLocalRepoBranchChanges(localPath, branch string) error { - if !com.IsExist(localPath) { - return nil - } - // No need to check if nothing in the repository. - if !git.IsBranchExist(localPath, branch) { - return nil - } - - refName := "origin/" + branch - if err := git.ResetHEAD(localPath, true, refName); err != nil { - return fmt.Errorf("git reset --hard %s: %v", refName, err) - } - return nil -} - -// DiscardLocalRepoBranchChanges discards the local repository branch changes -func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error { - return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch) -} - -// checkoutNewBranch checks out to a new branch from the a branch name. -func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error { - if err := git.Checkout(localPath, git.CheckoutOptions{ - Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, - Branch: newBranch, - OldBranch: oldBranch, - }); err != nil { - return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err) - } - return nil -} - -// CheckoutNewBranch checks out a new branch -func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error { - return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch) -} - // Branch holds the branch information type Branch struct { Path string diff --git a/models/repo_editor.go b/models/repo_editor.go index 2d877e3ad38a2..1b1c41ad41624 100644 --- a/models/repo_editor.go +++ b/models/repo_editor.go @@ -6,9 +6,7 @@ package models import ( "fmt" - "io" "io/ioutil" - "mime/multipart" "os" "os/exec" "path" @@ -16,7 +14,6 @@ import ( "time" "github.com/Unknwon/com" - gouuid "github.com/satori/go.uuid" "code.gitea.io/git" @@ -325,143 +322,6 @@ func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) ( return event, nil } -// ____ ___ .__ .___ ___________.___.__ -// | | \______ | | _________ __| _/ \_ _____/| | | ____ ______ -// | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/ -// | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \ -// |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ > -// |__| \/ \/ \/ \/ \/ -// - -// Upload represent a uploaded file to a repo to be deleted when moved -type Upload struct { - ID int64 `xorm:"pk autoincr"` - UUID string `xorm:"uuid UNIQUE"` - Name string -} - -// UploadLocalPath returns where uploads is stored in local file system based on given UUID. -func UploadLocalPath(uuid string) string { - return path.Join(setting.Repository.Upload.TempPath, uuid[0:1], uuid[1:2], uuid) -} - -// LocalPath returns where uploads are temporarily stored in local file system. -func (upload *Upload) LocalPath() string { - return UploadLocalPath(upload.UUID) -} - -// NewUpload creates a new upload object. -func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err error) { - upload := &Upload{ - UUID: gouuid.NewV4().String(), - Name: name, - } - - localPath := upload.LocalPath() - if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil { - return nil, fmt.Errorf("MkdirAll: %v", err) - } - - fw, err := os.Create(localPath) - if err != nil { - return nil, fmt.Errorf("Create: %v", err) - } - defer fw.Close() - - if _, err = fw.Write(buf); err != nil { - return nil, fmt.Errorf("Write: %v", err) - } else if _, err = io.Copy(fw, file); err != nil { - return nil, fmt.Errorf("Copy: %v", err) - } - - if _, err := x.Insert(upload); err != nil { - return nil, err - } - - return upload, nil -} - -// GetUploadByUUID returns the Upload by UUID -func GetUploadByUUID(uuid string) (*Upload, error) { - upload := &Upload{UUID: uuid} - has, err := x.Get(upload) - if err != nil { - return nil, err - } else if !has { - return nil, ErrUploadNotExist{0, uuid} - } - return upload, nil -} - -// GetUploadsByUUIDs returns multiple uploads by UUIDS -func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) { - if len(uuids) == 0 { - return []*Upload{}, nil - } - - // Silently drop invalid uuids. - uploads := make([]*Upload, 0, len(uuids)) - return uploads, x.In("uuid", uuids).Find(&uploads) -} - -// DeleteUploads deletes multiple uploads -func DeleteUploads(uploads ...*Upload) (err error) { - if len(uploads) == 0 { - return nil - } - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - ids := make([]int64, len(uploads)) - for i := 0; i < len(uploads); i++ { - ids[i] = uploads[i].ID - } - if _, err = sess. - In("id", ids). - Delete(new(Upload)); err != nil { - return fmt.Errorf("delete uploads: %v", err) - } - - for _, upload := range uploads { - localPath := upload.LocalPath() - if !com.IsFile(localPath) { - continue - } - - if err := os.Remove(localPath); err != nil { - return fmt.Errorf("remove upload: %v", err) - } - } - - return sess.Commit() -} - -// DeleteUpload delete a upload -func DeleteUpload(u *Upload) error { - return DeleteUploads(u) -} - -// DeleteUploadByUUID deletes a upload by UUID -func DeleteUploadByUUID(uuid string) error { - upload, err := GetUploadByUUID(uuid) - if err != nil { - if IsErrUploadNotExist(err) { - return nil - } - return fmt.Errorf("GetUploadByUUID: %v", err) - } - - if err := DeleteUpload(upload); err != nil { - return fmt.Errorf("DeleteUpload: %v", err) - } - - return nil -} - // UploadRepoFileOptions contains the uploaded repository file options type UploadRepoFileOptions struct { LastCommitID string diff --git a/modules/uploader/delete.go b/modules/uploader/delete.go index 2353f18c46531..831b0e48510d2 100644 --- a/modules/uploader/delete.go +++ b/modules/uploader/delete.go @@ -21,22 +21,22 @@ type DeleteRepoFileOptions struct { } // DeleteRepoFile deletes a file in the given repository -func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepoFileOptions) error { +func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepoFileOptions) (*models.CommitRepoEvent, error) { t, err := NewTemporaryUploadRepository(repo) defer t.Close() if err != nil { - return err + return nil, err } if err := t.Clone(opts.OldBranch); err != nil { - return err + return nil, err } if err := t.SetDefaultIndex(); err != nil { - return err + return nil, err } filesInIndex, err := t.LsFiles(opts.TreePath) if err != nil { - return fmt.Errorf("UpdateRepoFile: %v", err) + return nil, fmt.Errorf("UpdateRepoFile: %v", err) } inFilelist := false @@ -46,28 +46,28 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo } } if !inFilelist { - return git.ErrNotExist{RelPath: opts.TreePath} + return nil, git.ErrNotExist{RelPath: opts.TreePath} } if err := t.RemoveFilesFromIndex(opts.TreePath); err != nil { - return err + return nil, err } // Now write the tree treeHash, err := t.WriteTree() if err != nil { - return err + return nil, err } // Now commit the tree commitHash, err := t.CommitTree(doer, treeHash, opts.Message) if err != nil { - return err + return nil, err } // Then push this tree to NewBranch if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { - return err + return nil, err } // Simulate push event. @@ -77,9 +77,9 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo } if err = repo.GetOwner(); err != nil { - return fmt.Errorf("GetOwner: %v", err) + return nil, fmt.Errorf("GetOwner: %v", err) } - err = models.PushUpdate( + evt, err := models.PushUpdate( opts.NewBranch, models.PushUpdateOptions{ PusherID: doer.ID, @@ -92,9 +92,9 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo }, ) if err != nil { - return fmt.Errorf("PushUpdate: %v", err) + return nil, fmt.Errorf("PushUpdate: %v", err) } // FIXME: Should we UpdateRepoIndexer(repo) here? - return nil + return evt, nil } diff --git a/modules/uploader/update.go b/modules/uploader/update.go index bc543c7ffa523..4b779273e82fa 100644 --- a/modules/uploader/update.go +++ b/modules/uploader/update.go @@ -27,28 +27,28 @@ type UpdateRepoFileOptions struct { } // UpdateRepoFile adds or updates a file in the given repository -func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepoFileOptions) error { +func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepoFileOptions) (*models.CommitRepoEvent, error) { t, err := NewTemporaryUploadRepository(repo) defer t.Close() if err != nil { - return err + return nil, err } if err := t.Clone(opts.OldBranch); err != nil { - return err + return nil, err } if err := t.SetDefaultIndex(); err != nil { - return err + return nil, err } filesInIndex, err := t.LsFiles(opts.NewTreeName, opts.OldTreeName) if err != nil { - return fmt.Errorf("UpdateRepoFile: %v", err) + return nil, fmt.Errorf("UpdateRepoFile: %v", err) } if opts.IsNewFile { for _, file := range filesInIndex { if file == opts.NewTreeName { - return models.ErrRepoFileAlreadyExist{FileName: opts.NewTreeName} + return nil, models.ErrRepoFileAlreadyExist{FileName: opts.NewTreeName} } } } @@ -58,7 +58,7 @@ func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepo for _, file := range filesInIndex { if file == opts.OldTreeName { if err := t.RemoveFilesFromIndex(opts.OldTreeName); err != nil { - return err + return nil, err } } } @@ -68,7 +68,7 @@ func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepo // Check there is no way this can return multiple infos filename2attribute2info, err := t.CheckAttribute("filter", opts.NewTreeName) if err != nil { - return err + return nil, err } content := opts.Content @@ -78,7 +78,7 @@ func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepo // OK so we are supposed to LFS this data! oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content)) if err != nil { - return err + return nil, err } lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(opts.Content)), RepositoryID: repo.ID} content = lfsMetaObject.Pointer() @@ -87,46 +87,46 @@ func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepo // Add the object to the database objectHash, err := t.HashObject(strings.NewReader(content)) if err != nil { - return err + return nil, err } // Add the object to the index if err := t.AddObjectToIndex("100644", objectHash, opts.NewTreeName); err != nil { - return err + return nil, err } // Now write the tree treeHash, err := t.WriteTree() if err != nil { - return err + return nil, err } // Now commit the tree commitHash, err := t.CommitTree(doer, treeHash, opts.Message) if err != nil { - return err + return nil, err } if lfsMetaObject != nil { // We have an LFS object - create it lfsMetaObject, err = models.NewLFSMetaObject(lfsMetaObject) if err != nil { - return err + return nil, err } contentStore := &lfs.ContentStore{BasePath: setting.LFS.ContentPath} if !contentStore.Exists(lfsMetaObject) { if err := contentStore.Put(lfsMetaObject, strings.NewReader(opts.Content)); err != nil { if err2 := repo.RemoveLFSMetaObjectByOid(lfsMetaObject.Oid); err2 != nil { - return fmt.Errorf("Error whilst removing failed inserted LFS object %s: %v (Prev Error: %v)", lfsMetaObject.Oid, err2, err) + return nil, fmt.Errorf("Error whilst removing failed inserted LFS object %s: %v (Prev Error: %v)", lfsMetaObject.Oid, err2, err) } - return err + return nil, err } } } // Then push this tree to NewBranch if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { - return err + return nil, err } // Simulate push event. @@ -136,9 +136,9 @@ func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepo } if err = repo.GetOwner(); err != nil { - return fmt.Errorf("GetOwner: %v", err) + return nil, fmt.Errorf("GetOwner: %v", err) } - err = models.PushUpdate( + evt, err := models.PushUpdate( opts.NewBranch, models.PushUpdateOptions{ PusherID: doer.ID, @@ -151,9 +151,9 @@ func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepo }, ) if err != nil { - return fmt.Errorf("PushUpdate: %v", err) + return nil, fmt.Errorf("PushUpdate: %v", err) } models.UpdateRepoIndexer(repo) - return nil + return evt, nil } diff --git a/modules/uploader/upload.go b/modules/uploader/upload.go index 81d7c3ba20d99..e5621ef20c7dd 100644 --- a/modules/uploader/upload.go +++ b/modules/uploader/upload.go @@ -46,26 +46,26 @@ func cleanUpAfterFailure(infos *[]uploadInfo, t *TemporaryUploadRepository, orig } // UploadRepoFiles uploads files to the given repository -func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions) error { +func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions) (*models.CommitRepoEvent, error) { if len(opts.Files) == 0 { - return nil + return nil, nil } uploads, err := models.GetUploadsByUUIDs(opts.Files) if err != nil { - return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err) + return nil, fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err) } t, err := NewTemporaryUploadRepository(repo) defer t.Close() if err != nil { - return err + return nil, err } if err := t.Clone(opts.OldBranch); err != nil { - return err + return nil, err } if err := t.SetDefaultIndex(); err != nil { - return err + return nil, err } names := make([]string, len(uploads)) @@ -77,14 +77,14 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep filename2attribute2info, err := t.CheckAttribute("filter", names...) if err != nil { - return err + return nil, err } // Copy uploaded files into repository. for i, uploadInfo := range infos { file, err := os.Open(uploadInfo.upload.LocalPath()) if err != nil { - return err + return nil, err } defer file.Close() @@ -94,29 +94,29 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep // FIXME: Inefficient! this should probably happen in models.Upload oid, err := models.GenerateLFSOid(file) if err != nil { - return err + return nil, err } fileInfo, err := file.Stat() if err != nil { - return err + return nil, err } uploadInfo.lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: fileInfo.Size(), RepositoryID: t.repo.ID} if objectHash, err = t.HashObject(strings.NewReader(uploadInfo.lfsMetaObject.Pointer())); err != nil { - return err + return nil, err } infos[i] = uploadInfo } else { if objectHash, err = t.HashObject(file); err != nil { - return err + return nil, err } } // Add the object to the index if err := t.AddObjectToIndex("100644", objectHash, path.Join(opts.TreePath, uploadInfo.upload.Name)); err != nil { - return err + return nil, err } } @@ -124,13 +124,13 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep // Now write the tree treeHash, err := t.WriteTree() if err != nil { - return err + return nil, err } // Now commit the tree commitHash, err := t.CommitTree(doer, treeHash, opts.Message) if err != nil { - return err + return nil, err } // Now deal with LFS objects @@ -141,7 +141,7 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep uploadInfo.lfsMetaObject, err = models.NewLFSMetaObject(uploadInfo.lfsMetaObject) if err != nil { // OK Now we need to cleanup - return cleanUpAfterFailure(&infos, t, err) + return nil, cleanUpAfterFailure(&infos, t, err) } // Don't move the files yet - we need to ensure that // everything can be inserted first @@ -157,7 +157,7 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep if !contentStore.Exists(uploadInfo.lfsMetaObject) { file, err := os.Open(uploadInfo.upload.LocalPath()) if err != nil { - return cleanUpAfterFailure(&infos, t, err) + return nil, cleanUpAfterFailure(&infos, t, err) } defer file.Close() // FIXME: Put regenerates the hash and copies the file over. @@ -165,14 +165,14 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep if err := contentStore.Put(uploadInfo.lfsMetaObject, file); err != nil { // OK Now we need to cleanup // Can't clean up the store, once uploaded there they're there. - return cleanUpAfterFailure(&infos, t, err) + return nil, cleanUpAfterFailure(&infos, t, err) } } } // Then push this tree to NewBranch if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { - return err + return nil, err } // Simulate push event. @@ -182,9 +182,9 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep } if err = repo.GetOwner(); err != nil { - return fmt.Errorf("GetOwner: %v", err) + return nil, fmt.Errorf("GetOwner: %v", err) } - err = models.PushUpdate( + evt, err := models.PushUpdate( opts.NewBranch, models.PushUpdateOptions{ PusherID: doer.ID, @@ -197,9 +197,9 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep }, ) if err != nil { - return fmt.Errorf("PushUpdate: %v", err) + return nil, fmt.Errorf("PushUpdate: %v", err) } // FIXME: Should we models.UpdateRepoIndexer(repo) here? - return models.DeleteUploads(uploads...) + return evt, models.DeleteUploads(uploads...) } From e6ed8c394b774ad8522b65d8dc0ad42803dcd2a2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 2 Mar 2019 12:20:22 +0800 Subject: [PATCH 10/13] fix make vet --- models/release_test.go | 55 ----------------------------- modules/mirror/main_test.go | 16 +++++++++ modules/mirror/sync_test.go | 69 +++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 55 deletions(-) create mode 100644 modules/mirror/main_test.go create mode 100644 modules/mirror/sync_test.go diff --git a/models/release_test.go b/models/release_test.go index 48acb4f1f2510..5593e9812adf1 100644 --- a/models/release_test.go +++ b/models/release_test.go @@ -98,58 +98,3 @@ func TestRelease_Create(t *testing.T) { IsTag: true, }, nil)) } - -func TestRelease_MirrorDelete(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) - - user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - repoPath := RepoPath(user.Name, repo.Name) - migrationOptions := MigrateRepoOptions{ - Name: "test_mirror", - Description: "Test mirror", - IsPrivate: false, - IsMirror: true, - RemoteAddr: repoPath, - } - mirror, err := MigrateRepository(user, user, migrationOptions) - assert.NoError(t, err) - - gitRepo, err := git.OpenRepository(repoPath) - assert.NoError(t, err) - - findOptions := FindReleasesOptions{IncludeDrafts: true, IncludeTags: true} - initCount, err := GetReleaseCountByRepoID(mirror.ID, findOptions) - assert.NoError(t, err) - - assert.NoError(t, CreateRelease(gitRepo, &Release{ - RepoID: repo.ID, - PublisherID: user.ID, - TagName: "v0.2", - Target: "master", - Title: "v0.2 is released", - Note: "v0.2 is released", - IsDraft: false, - IsPrerelease: false, - IsTag: true, - }, nil)) - - err = mirror.GetMirror() - assert.NoError(t, err) - - _, ok := mirror.Mirror.runSync() - assert.True(t, ok) - - count, err := GetReleaseCountByRepoID(mirror.ID, findOptions) - assert.EqualValues(t, initCount+1, count) - - release, err := GetRelease(repo.ID, "v0.2") - assert.NoError(t, err) - assert.NoError(t, DeleteReleaseByID(release.ID, user, true)) - - _, ok = mirror.Mirror.runSync() - assert.True(t, ok) - - count, err = GetReleaseCountByRepoID(mirror.ID, findOptions) - assert.EqualValues(t, initCount, count) -} diff --git a/modules/mirror/main_test.go b/modules/mirror/main_test.go new file mode 100644 index 0000000000000..ade522c369db1 --- /dev/null +++ b/modules/mirror/main_test.go @@ -0,0 +1,16 @@ +// Copyright 2019 The Gitea 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 mirror + +import ( + "path/filepath" + "testing" + + "code.gitea.io/gitea/models" +) + +func TestMain(m *testing.M) { + models.MainTest(m, filepath.Join("..", "..")) +} diff --git a/modules/mirror/sync_test.go b/modules/mirror/sync_test.go new file mode 100644 index 0000000000000..6eb0ed389efc2 --- /dev/null +++ b/modules/mirror/sync_test.go @@ -0,0 +1,69 @@ +// Copyright 2018 The Gitea 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 mirror + +import ( + "testing" + + "code.gitea.io/git" + "code.gitea.io/gitea/models" + + "github.com/stretchr/testify/assert" +) + +func TestRelease_MirrorDelete(t *testing.T) { + assert.NoError(t, models.PrepareTestDatabase()) + + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + repoPath := models.RepoPath(user.Name, repo.Name) + migrationOptions := models.MigrateRepoOptions{ + Name: "test_mirror", + Description: "Test mirror", + IsPrivate: false, + IsMirror: true, + RemoteAddr: repoPath, + } + mirrorRepo, err := models.MigrateRepository(user, user, migrationOptions) + assert.NoError(t, err) + + gitRepo, err := git.OpenRepository(repoPath) + assert.NoError(t, err) + + findOptions := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true} + initCount, err := models.GetReleaseCountByRepoID(mirrorRepo.ID, findOptions) + assert.NoError(t, err) + + assert.NoError(t, models.CreateRelease(gitRepo, &models.Release{ + RepoID: repo.ID, + PublisherID: user.ID, + TagName: "v0.2", + Target: "master", + Title: "v0.2 is released", + Note: "v0.2 is released", + IsDraft: false, + IsPrerelease: false, + IsTag: true, + }, nil)) + + err = mirrorRepo.GetMirror() + assert.NoError(t, err) + + _, ok := runSync(mirrorRepo.Mirror) + assert.True(t, ok) + + count, err := models.GetReleaseCountByRepoID(mirrorRepo.ID, findOptions) + assert.EqualValues(t, initCount+1, count) + + release, err := models.GetRelease(repo.ID, "v0.2") + assert.NoError(t, err) + assert.NoError(t, models.DeleteReleaseByID(release.ID, user, true)) + + _, ok = runSync(mirrorRepo.Mirror) + assert.True(t, ok) + + count, err = models.GetReleaseCountByRepoID(mirrorRepo.ID, findOptions) + assert.EqualValues(t, initCount, count) +} From 61288cf7a49a8b4e27a05e975cce8f8f29fd572d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 2 Mar 2019 15:13:16 +0800 Subject: [PATCH 11/13] fix lint --- models/issue_comment.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/models/issue_comment.go b/models/issue_comment.go index 444a46ea828b4..53fc48f99635c 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -631,12 +631,9 @@ func sendCreateCommentAction(e *xorm.Session, opts *CreateCommentOptions, commen return err } } - // update the issue's updated_unix column - if err = updateIssueCols(e, opts.Issue, "updated_unix"); err != nil { - return err - } - return nil + // update the issue's updated_unix column + return updateIssueCols(e, opts.Issue, "updated_unix") } func createStatusComment(e *xorm.Session, doer *User, issue *Issue) (*Comment, error) { From 8e1ccb3c9855d87a98cac8cbcbdd32ce2b4d7f5b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 2 Apr 2019 10:26:55 +0800 Subject: [PATCH 12/13] switch to go mod --- models/repo_editor.go | 7 +++---- modules/mirror/sync.go | 2 +- modules/mirror/sync_test.go | 2 +- modules/notification/action/action.go | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/models/repo_editor.go b/models/repo_editor.go index 1b1c41ad41624..3c96c18e2b2b0 100644 --- a/models/repo_editor.go +++ b/models/repo_editor.go @@ -13,13 +13,12 @@ import ( "path/filepath" "time" - "github.com/Unknwon/com" - - "code.gitea.io/git" - + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" + + "github.com/Unknwon/com" ) // ___________ .___.__ __ ___________.__.__ diff --git a/modules/mirror/sync.go b/modules/mirror/sync.go index bada8f8084b57..1791220217212 100644 --- a/modules/mirror/sync.go +++ b/modules/mirror/sync.go @@ -9,9 +9,9 @@ import ( "strings" "time" - "code.gitea.io/git" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/cache" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" diff --git a/modules/mirror/sync_test.go b/modules/mirror/sync_test.go index 6eb0ed389efc2..7c11e74441599 100644 --- a/modules/mirror/sync_test.go +++ b/modules/mirror/sync_test.go @@ -7,8 +7,8 @@ package mirror import ( "testing" - "code.gitea.io/git" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" "github.com/stretchr/testify/assert" ) diff --git a/modules/notification/action/action.go b/modules/notification/action/action.go index a7c48db5d62d3..a1da0ab24617b 100644 --- a/modules/notification/action/action.go +++ b/modules/notification/action/action.go @@ -9,8 +9,8 @@ import ( "path" "strings" - "code.gitea.io/git" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification/base" ) From 21723962c37c696770fd521a7c08b90adafe49e4 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 2 Apr 2019 10:32:31 +0800 Subject: [PATCH 13/13] fix imports --- modules/mirror/sync.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/mirror/sync.go b/modules/mirror/sync.go index 1791220217212..31fc6b9197e70 100644 --- a/modules/mirror/sync.go +++ b/modules/mirror/sync.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/util" + "github.com/Unknwon/com" ini "gopkg.in/ini.v1" )