mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-05 18:32:41 +09:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ccd92407a | ||
|
|
17c691f8aa | ||
|
|
cb3fe4cbf1 | ||
|
|
c63a80138a | ||
|
|
b4b8c9679f | ||
|
|
c0bb5ebc15 | ||
|
|
9409ac9030 | ||
|
|
a3928fd820 | ||
|
|
f0bda12c49 | ||
|
|
58c38ab4b6 | ||
|
|
a276aaf61e | ||
|
|
e03934f035 | ||
|
|
3a14a69e8a | ||
|
|
f0f48e0fff | ||
|
|
1aeeaa8e89 | ||
|
|
eb8d5f6aff | ||
|
|
9ef148abeb | ||
|
|
f11df80058 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -4,6 +4,26 @@ This changelog goes through all the changes that have been made in each release
|
|||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||||
|
|
||||||
|
## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
|
||||||
|
* BUGFIXES
|
||||||
|
* Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
|
||||||
|
* Add ErrReactionAlreadyExist error (#9550) (#9564)
|
||||||
|
* Fix bug when migrate from API (#8631) (#9563)
|
||||||
|
* Use default avatar for ghost user (#9536) (#9537)
|
||||||
|
* Fix repository issues pagination bug when there are more than one label filter (#9512) (#9528)
|
||||||
|
* Fix deleted branch not removed when push the branch again (#9516) (#9524)
|
||||||
|
* Fix missing repository status when migrating repository via API (#9511)
|
||||||
|
* Trigger webhook when deleting a branch after merging a PR (#9510)
|
||||||
|
* Fix paging on /repos/{owner}/{repo}/git/trees/{sha} API endpoint (#9482)
|
||||||
|
* Fix NewCommitStatus (#9434) (#9435)
|
||||||
|
* Use OriginalURL instead of CloneAddr in migration logging (#9418) (#9420)
|
||||||
|
* Fix Slack webhook payload title generation to work with Mattermost (#9404)
|
||||||
|
* DefaultBranch needs to be prefixed by BranchPrefix (#9356) (#9359)
|
||||||
|
* Fix issue indexer not triggered when migrating a repository (#9333)
|
||||||
|
* Fix bug that release attachment files not deleted when deleting repository (#9322) (#9329)
|
||||||
|
* Fix migration releases (#9319) (#9326) (#9328)
|
||||||
|
* Fix File Edit: Author/Committer interchanged (#9297) (#9300)
|
||||||
|
|
||||||
## [1.10.1](https://github.com/go-gitea/gitea/releases/tag/v1.10.1) - 2019-12-05
|
## [1.10.1](https://github.com/go-gitea/gitea/releases/tag/v1.10.1) - 2019-12-05
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
* Fix max length check and limit in multiple repo forms (#9148) (#9204)
|
* Fix max length check and limit in multiple repo forms (#9148) (#9204)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
@@ -62,3 +63,61 @@ func TestAPICreateIssue(t *testing.T) {
|
|||||||
Title: title,
|
Title: title,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIEditIssue(t *testing.T) {
|
||||||
|
prepareTestEnv(t)
|
||||||
|
|
||||||
|
issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 9}).(*models.Issue)
|
||||||
|
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
|
||||||
|
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||||
|
assert.NoError(t, issueBefore.LoadAttributes())
|
||||||
|
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
|
||||||
|
assert.Equal(t, api.StateOpen, issueBefore.State())
|
||||||
|
|
||||||
|
session := loginUser(t, owner.Name)
|
||||||
|
token := getTokenForLoggedInUser(t, session)
|
||||||
|
|
||||||
|
// update values of issue
|
||||||
|
issueState := "closed"
|
||||||
|
removeDeadline := time.Unix(0, 0)
|
||||||
|
milestone := int64(4)
|
||||||
|
body := "new content!"
|
||||||
|
title := "new title from api set"
|
||||||
|
|
||||||
|
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repo.Name, issueBefore.Index, token)
|
||||||
|
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
|
||||||
|
State: &issueState,
|
||||||
|
Deadline: &removeDeadline,
|
||||||
|
Milestone: &milestone,
|
||||||
|
Body: &body,
|
||||||
|
Title: title,
|
||||||
|
|
||||||
|
// ToDo change more
|
||||||
|
})
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusCreated)
|
||||||
|
var apiIssue api.Issue
|
||||||
|
DecodeJSON(t, resp, &apiIssue)
|
||||||
|
|
||||||
|
issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 9}).(*models.Issue)
|
||||||
|
|
||||||
|
// check deleted user
|
||||||
|
assert.Equal(t, int64(500), issueAfter.PosterID)
|
||||||
|
assert.NoError(t, issueAfter.LoadAttributes())
|
||||||
|
assert.Equal(t, int64(-1), issueAfter.PosterID)
|
||||||
|
assert.Equal(t, int64(-1), issueBefore.PosterID)
|
||||||
|
assert.Equal(t, int64(-1), apiIssue.Poster.ID)
|
||||||
|
|
||||||
|
// API response
|
||||||
|
assert.Equal(t, api.StateClosed, apiIssue.State)
|
||||||
|
assert.Equal(t, milestone, apiIssue.Milestone.ID)
|
||||||
|
assert.Equal(t, body, apiIssue.Body)
|
||||||
|
assert.True(t, apiIssue.Deadline == nil)
|
||||||
|
assert.Equal(t, title, apiIssue.Title)
|
||||||
|
|
||||||
|
// in database
|
||||||
|
assert.Equal(t, api.StateClosed, issueAfter.State())
|
||||||
|
assert.Equal(t, milestone, issueAfter.MilestoneID)
|
||||||
|
assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix))
|
||||||
|
assert.Equal(t, body, issueAfter.Content)
|
||||||
|
assert.Equal(t, title, issueAfter.Title)
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ func getCreateFileOptions() api.CreateFileOptions {
|
|||||||
NewBranchName: "master",
|
NewBranchName: "master",
|
||||||
Message: "Making this new file new/file.txt",
|
Message: "Making this new file new/file.txt",
|
||||||
Author: api.Identity{
|
Author: api.Identity{
|
||||||
Name: "John Doe",
|
Name: "Anne Doe",
|
||||||
Email: "johndoe@example.com",
|
Email: "annedoe@example.com",
|
||||||
},
|
},
|
||||||
Committer: api.Identity{
|
Committer: api.Identity{
|
||||||
Name: "Jane Doe",
|
Name: "John Doe",
|
||||||
Email: "janedoe@example.com",
|
Email: "johndoe@example.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Content: contentEncoded,
|
Content: contentEncoded,
|
||||||
@@ -77,8 +77,8 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon
|
|||||||
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
||||||
Author: &api.CommitUser{
|
Author: &api.CommitUser{
|
||||||
Identity: api.Identity{
|
Identity: api.Identity{
|
||||||
Name: "Jane Doe",
|
Name: "Anne Doe",
|
||||||
Email: "janedoe@example.com",
|
Email: "annedoe@example.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Committer: &api.CommitUser{
|
Committer: &api.CommitUser{
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ func getUpdateFileOptions() *api.UpdateFileOptions {
|
|||||||
Email: "johndoe@example.com",
|
Email: "johndoe@example.com",
|
||||||
},
|
},
|
||||||
Committer: api.Identity{
|
Committer: api.Identity{
|
||||||
Name: "Jane Doe",
|
Name: "Anne Doe",
|
||||||
Email: "janedoe@example.com",
|
Email: "annedoe@example.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||||
@@ -80,14 +80,14 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon
|
|||||||
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
||||||
Author: &api.CommitUser{
|
Author: &api.CommitUser{
|
||||||
Identity: api.Identity{
|
Identity: api.Identity{
|
||||||
Name: "Jane Doe",
|
Name: "John Doe",
|
||||||
Email: "janedoe@example.com",
|
Email: "johndoe@example.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Committer: &api.CommitUser{
|
Committer: &api.CommitUser{
|
||||||
Identity: api.Identity{
|
Identity: api.Identity{
|
||||||
Name: "John Doe",
|
Name: "Anne Doe",
|
||||||
Email: "johndoe@example.com",
|
Email: "annedoe@example.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Message: "My update of README.md\n",
|
Message: "My update of README.md\n",
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) {
|
|||||||
resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
|
resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
|
||||||
respJSON := map[string]string{}
|
respJSON := map[string]string{}
|
||||||
DecodeJSON(t, resp, &respJSON)
|
DecodeJSON(t, resp, &respJSON)
|
||||||
assert.Equal(t, respJSON["message"], "The repository with the same name already exists.")
|
assert.Equal(t, "The repository with the same name already exists.", respJSON["message"])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -480,6 +480,12 @@ func (deletedBranch *DeletedBranch) LoadUser() {
|
|||||||
deletedBranch.DeletedBy = user
|
deletedBranch.DeletedBy = user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveDeletedBranch removes all deleted branches
|
||||||
|
func RemoveDeletedBranch(repoID int64, branch string) error {
|
||||||
|
_, err := x.Where("repo_id=? AND name=?", repoID, branch).Delete(new(DeletedBranch))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveOldDeletedBranches removes old deleted branches
|
// RemoveOldDeletedBranches removes old deleted branches
|
||||||
func RemoveOldDeletedBranches() {
|
func RemoveOldDeletedBranches() {
|
||||||
log.Trace("Doing: DeletedBranchesCleanup")
|
log.Trace("Doing: DeletedBranchesCleanup")
|
||||||
|
|||||||
@@ -1104,6 +1104,21 @@ func (err ErrNewIssueInsert) Error() string {
|
|||||||
return err.OriginalError.Error()
|
return err.OriginalError.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrReactionAlreadyExist is used when a existing reaction was try to created
|
||||||
|
type ErrReactionAlreadyExist struct {
|
||||||
|
Reaction string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrReactionAlreadyExist checks if an error is a ErrReactionAlreadyExist.
|
||||||
|
func IsErrReactionAlreadyExist(err error) bool {
|
||||||
|
_, ok := err.(ErrReactionAlreadyExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrReactionAlreadyExist) Error() string {
|
||||||
|
return fmt.Sprintf("reaction '%s' already exists", err.Reaction)
|
||||||
|
}
|
||||||
|
|
||||||
// __________ .__ .__ __________ __
|
// __________ .__ .__ __________ __
|
||||||
// \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_
|
// \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_
|
||||||
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
|
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
|
||||||
|
|||||||
@@ -97,3 +97,16 @@
|
|||||||
is_pull: true
|
is_pull: true
|
||||||
created_unix: 946684820
|
created_unix: 946684820
|
||||||
updated_unix: 978307180
|
updated_unix: 978307180
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 9
|
||||||
|
repo_id: 42
|
||||||
|
index: 1
|
||||||
|
poster_id: 500
|
||||||
|
name: issue from deleted account
|
||||||
|
content: content from deleted account
|
||||||
|
is_closed: false
|
||||||
|
is_pull: false
|
||||||
|
created_unix: 946684830
|
||||||
|
updated_unix: 999307200
|
||||||
|
deadline_unix: 1019307200
|
||||||
|
|||||||
@@ -21,3 +21,11 @@
|
|||||||
content: content3
|
content: content3
|
||||||
is_closed: true
|
is_closed: true
|
||||||
num_issues: 0
|
num_issues: 0
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 4
|
||||||
|
repo_id: 42
|
||||||
|
name: milestone of repo42
|
||||||
|
content: content random
|
||||||
|
is_closed: false
|
||||||
|
num_issues: 0
|
||||||
|
|||||||
@@ -547,7 +547,8 @@
|
|||||||
is_private: false
|
is_private: false
|
||||||
num_stars: 0
|
num_stars: 0
|
||||||
num_forks: 0
|
num_forks: 0
|
||||||
num_issues: 0
|
num_issues: 1
|
||||||
|
num_milestones: 1
|
||||||
is_mirror: false
|
is_mirror: false
|
||||||
|
|
||||||
-
|
-
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@@ -238,6 +239,16 @@ func (issue *Issue) loadReactions(e Engine) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (issue *Issue) loadMilestone(e Engine) (err error) {
|
||||||
|
if issue.Milestone == nil && issue.MilestoneID > 0 {
|
||||||
|
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
||||||
|
if err != nil && !IsErrMilestoneNotExist(err) {
|
||||||
|
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
||||||
if err = issue.loadRepo(e); err != nil {
|
if err = issue.loadRepo(e); err != nil {
|
||||||
return
|
return
|
||||||
@@ -251,11 +262,8 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if issue.Milestone == nil && issue.MilestoneID > 0 {
|
if err = issue.loadMilestone(e); err != nil {
|
||||||
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
return
|
||||||
if err != nil && !IsErrMilestoneNotExist(err) {
|
|
||||||
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = issue.loadAssignees(e); err != nil {
|
if err = issue.loadAssignees(e); err != nil {
|
||||||
@@ -295,6 +303,11 @@ func (issue *Issue) LoadAttributes() error {
|
|||||||
return issue.loadAttributes(x)
|
return issue.loadAttributes(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadMilestone load milestone of this issue.
|
||||||
|
func (issue *Issue) LoadMilestone() error {
|
||||||
|
return issue.loadMilestone(x)
|
||||||
|
}
|
||||||
|
|
||||||
// GetIsRead load the `IsRead` field of the issue
|
// GetIsRead load the `IsRead` field of the issue
|
||||||
func (issue *Issue) GetIsRead(userID int64) error {
|
func (issue *Issue) GetIsRead(userID int64) error {
|
||||||
issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
|
issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
|
||||||
@@ -1730,22 +1743,17 @@ func SearchIssueIDsByKeyword(kw string, repoID int64, limit, start int) (int64,
|
|||||||
return total, ids, nil
|
return total, ids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateIssue(e Engine, issue *Issue) error {
|
// UpdateIssueByAPI updates all allowed fields of given issue.
|
||||||
_, err := e.ID(issue.ID).AllCols().Update(issue)
|
func UpdateIssueByAPI(issue *Issue) error {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIssue updates all fields of given issue.
|
|
||||||
func UpdateIssue(issue *Issue) error {
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err := sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := updateIssue(sess, issue); err != nil {
|
if _, err := sess.ID(issue.ID).Cols(
|
||||||
|
"name", "is_closed", "content", "milestone_id", "priority",
|
||||||
|
"deadline_unix", "updated_unix", "closed_unix", "is_locked").
|
||||||
|
Update(issue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := issue.neuterCrossReferences(sess); err != nil {
|
if err := issue.neuterCrossReferences(sess); err != nil {
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ type Reaction struct {
|
|||||||
type FindReactionsOptions struct {
|
type FindReactionsOptions struct {
|
||||||
IssueID int64
|
IssueID int64
|
||||||
CommentID int64
|
CommentID int64
|
||||||
|
UserID int64
|
||||||
|
Reaction string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *FindReactionsOptions) toConds() builder.Cond {
|
func (opts *FindReactionsOptions) toConds() builder.Cond {
|
||||||
@@ -40,6 +42,13 @@ func (opts *FindReactionsOptions) toConds() builder.Cond {
|
|||||||
if opts.CommentID > 0 {
|
if opts.CommentID > 0 {
|
||||||
cond = cond.And(builder.Eq{"reaction.comment_id": opts.CommentID})
|
cond = cond.And(builder.Eq{"reaction.comment_id": opts.CommentID})
|
||||||
}
|
}
|
||||||
|
if opts.UserID > 0 {
|
||||||
|
cond = cond.And(builder.Eq{"reaction.user_id": opts.UserID})
|
||||||
|
}
|
||||||
|
if opts.Reaction != "" {
|
||||||
|
cond = cond.And(builder.Eq{"reaction.type": opts.Reaction})
|
||||||
|
}
|
||||||
|
|
||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +66,25 @@ func createReaction(e *xorm.Session, opts *ReactionOptions) (*Reaction, error) {
|
|||||||
UserID: opts.Doer.ID,
|
UserID: opts.Doer.ID,
|
||||||
IssueID: opts.Issue.ID,
|
IssueID: opts.Issue.ID,
|
||||||
}
|
}
|
||||||
|
findOpts := FindReactionsOptions{
|
||||||
|
IssueID: opts.Issue.ID,
|
||||||
|
CommentID: -1, // reaction to issue only
|
||||||
|
Reaction: opts.Type,
|
||||||
|
UserID: opts.Doer.ID,
|
||||||
|
}
|
||||||
if opts.Comment != nil {
|
if opts.Comment != nil {
|
||||||
reaction.CommentID = opts.Comment.ID
|
reaction.CommentID = opts.Comment.ID
|
||||||
|
findOpts.CommentID = opts.Comment.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
existingR, err := findReactions(e, findOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(existingR) > 0 {
|
||||||
|
return existingR[0], ErrReactionAlreadyExist{Reaction: opts.Type}
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := e.Insert(reaction); err != nil {
|
if _, err := e.Insert(reaction); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -76,19 +101,19 @@ type ReactionOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateReaction creates reaction for issue or comment.
|
// CreateReaction creates reaction for issue or comment.
|
||||||
func CreateReaction(opts *ReactionOptions) (reaction *Reaction, err error) {
|
func CreateReaction(opts *ReactionOptions) (*Reaction, error) {
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err = sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
reaction, err = createReaction(sess, opts)
|
reaction, err := createReaction(sess, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return reaction, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
if err := sess.Commit(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return reaction, nil
|
return reaction, nil
|
||||||
|
|||||||
@@ -50,9 +50,10 @@ func TestIssueAddDuplicateReaction(t *testing.T) {
|
|||||||
Type: "heart",
|
Type: "heart",
|
||||||
})
|
})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Nil(t, reaction)
|
assert.Equal(t, ErrReactionAlreadyExist{Reaction: "heart"}, err)
|
||||||
|
|
||||||
AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID})
|
existingR := AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID}).(*Reaction)
|
||||||
|
assert.Equal(t, existingR.ID, reaction.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIssueDeleteReaction(t *testing.T) {
|
func TestIssueDeleteReaction(t *testing.T) {
|
||||||
@@ -129,7 +130,6 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
|
|||||||
user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||||
user3 := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
|
user3 := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
|
||||||
user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
|
user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
|
||||||
ghost := NewGhostUser()
|
|
||||||
|
|
||||||
issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
|
issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
|
||||||
|
|
||||||
@@ -139,14 +139,13 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
|
|||||||
addReaction(t, user2, issue1, comment1, "heart")
|
addReaction(t, user2, issue1, comment1, "heart")
|
||||||
addReaction(t, user3, issue1, comment1, "heart")
|
addReaction(t, user3, issue1, comment1, "heart")
|
||||||
addReaction(t, user4, issue1, comment1, "+1")
|
addReaction(t, user4, issue1, comment1, "+1")
|
||||||
addReaction(t, ghost, issue1, comment1, "heart")
|
|
||||||
|
|
||||||
err := comment1.LoadReactions()
|
err := comment1.LoadReactions()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, comment1.Reactions, 5)
|
assert.Len(t, comment1.Reactions, 4)
|
||||||
|
|
||||||
reactions := comment1.Reactions.GroupByType()
|
reactions := comment1.Reactions.GroupByType()
|
||||||
assert.Len(t, reactions["heart"], 4)
|
assert.Len(t, reactions["heart"], 3)
|
||||||
assert.Len(t, reactions["+1"], 1)
|
assert.Len(t, reactions["+1"], 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +159,7 @@ func TestIssueCommentReactionCount(t *testing.T) {
|
|||||||
comment1 := AssertExistsAndLoadBean(t, &Comment{ID: 1}).(*Comment)
|
comment1 := AssertExistsAndLoadBean(t, &Comment{ID: 1}).(*Comment)
|
||||||
|
|
||||||
addReaction(t, user1, issue1, comment1, "heart")
|
addReaction(t, user1, issue1, comment1, "heart")
|
||||||
DeleteCommentReaction(user1, issue1, comment1, "heart")
|
assert.NoError(t, DeleteCommentReaction(user1, issue1, comment1, "heart"))
|
||||||
|
|
||||||
AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID, CommentID: comment1.ID})
|
AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID, CommentID: comment1.ID})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1781,6 +1781,12 @@ func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRepositoryStatus updates a repository's status
|
||||||
|
func UpdateRepositoryStatus(repoID int64, status RepositoryStatus) error {
|
||||||
|
_, err := x.Exec("UPDATE repository SET status = ? WHERE id = ?", status, repoID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateRepositoryUpdatedTime updates a repository's updated time
|
// UpdateRepositoryUpdatedTime updates a repository's updated time
|
||||||
func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error {
|
func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error {
|
||||||
_, err := x.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", updateTime.Unix(), repoID)
|
_, err := x.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", updateTime.Unix(), repoID)
|
||||||
@@ -1860,6 +1866,17 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attachments := make([]*Attachment, 0, 20)
|
||||||
|
if err = sess.Join("INNER", "`release`", "`release`.id = `attachment`.release_id").
|
||||||
|
Where("`release`.repo_id = ?", repoID).
|
||||||
|
Find(&attachments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
releaseAttachments := make([]string, 0, len(attachments))
|
||||||
|
for i := 0; i < len(attachments); i++ {
|
||||||
|
releaseAttachments = append(releaseAttachments, attachments[i].LocalPath())
|
||||||
|
}
|
||||||
|
|
||||||
if err = deleteBeans(sess,
|
if err = deleteBeans(sess,
|
||||||
&Access{RepoID: repo.ID},
|
&Access{RepoID: repo.ID},
|
||||||
&Action{RepoID: repo.ID},
|
&Action{RepoID: repo.ID},
|
||||||
@@ -1910,13 +1927,13 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentPaths := make([]string, 0, 20)
|
attachments = attachments[:0]
|
||||||
attachments := make([]*Attachment, 0, len(attachmentPaths))
|
|
||||||
if err = sess.Join("INNER", "issue", "issue.id = attachment.issue_id").
|
if err = sess.Join("INNER", "issue", "issue.id = attachment.issue_id").
|
||||||
Where("issue.repo_id = ?", repoID).
|
Where("issue.repo_id = ?", repoID).
|
||||||
Find(&attachments); err != nil {
|
Find(&attachments); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
attachmentPaths := make([]string, 0, len(attachments))
|
||||||
for j := range attachments {
|
for j := range attachments {
|
||||||
attachmentPaths = append(attachmentPaths, attachments[j].LocalPath())
|
attachmentPaths = append(attachmentPaths, attachments[j].LocalPath())
|
||||||
}
|
}
|
||||||
@@ -1953,11 +1970,6 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove attachment files.
|
|
||||||
for i := range attachmentPaths {
|
|
||||||
removeAllWithNotice(sess, "Delete attachment", attachmentPaths[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove LFS objects
|
// Remove LFS objects
|
||||||
var lfsObjects []*LFSMetaObject
|
var lfsObjects []*LFSMetaObject
|
||||||
if err = sess.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil {
|
if err = sess.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil {
|
||||||
@@ -1997,6 +2009,8 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
return fmt.Errorf("Commit: %v", err)
|
return fmt.Errorf("Commit: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sess.Close()
|
||||||
|
|
||||||
if org.IsOrganization() {
|
if org.IsOrganization() {
|
||||||
if err = PrepareWebhooks(repo, HookEventRepository, &api.RepositoryPayload{
|
if err = PrepareWebhooks(repo, HookEventRepository, &api.RepositoryPayload{
|
||||||
Action: api.HookRepoDeleted,
|
Action: api.HookRepoDeleted,
|
||||||
@@ -2009,6 +2023,19 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
go HookQueue.Add(repo.ID)
|
go HookQueue.Add(repo.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should always delete the files after the database transaction succeed. If
|
||||||
|
// we delete the file but the database rollback, the repository will be borken.
|
||||||
|
|
||||||
|
// Remove issue attachment files.
|
||||||
|
for i := range attachmentPaths {
|
||||||
|
removeAllWithNotice(x, "Delete issue attachment", attachmentPaths[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove release attachment files.
|
||||||
|
for i := range releaseAttachments {
|
||||||
|
removeAllWithNotice(x, "Delete release attachment", releaseAttachments[i])
|
||||||
|
}
|
||||||
|
|
||||||
if len(repo.Avatar) > 0 {
|
if len(repo.Avatar) > 0 {
|
||||||
avatarPath := repo.CustomAvatarPath()
|
avatarPath := repo.CustomAvatarPath()
|
||||||
if com.IsExist(avatarPath) {
|
if com.IsExist(avatarPath) {
|
||||||
@@ -2784,3 +2811,9 @@ func (repo *Repository) GetOriginalURLHostname() string {
|
|||||||
|
|
||||||
return u.Host
|
return u.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRepositoryCols updates repository's columns
|
||||||
|
func UpdateRepositoryCols(repo *Repository, cols ...string) error {
|
||||||
|
_, err := x.ID(repo.ID).Cols(cols...).Update(repo)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ type fileUpdate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultBranchSha(repo *Repository) (string, error) {
|
func getDefaultBranchSha(repo *Repository) (string, error) {
|
||||||
stdout, err := git.NewCommand("show-ref", "-s", repo.DefaultBranch).RunInDir(repo.RepoPath())
|
stdout, err := git.NewCommand("show-ref", "-s", git.BranchPrefix+repo.DefaultBranch).RunInDir(repo.RepoPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,10 +36,11 @@ type SlackPayload struct {
|
|||||||
|
|
||||||
// SlackAttachment contains the slack message
|
// SlackAttachment contains the slack message
|
||||||
type SlackAttachment struct {
|
type SlackAttachment struct {
|
||||||
Fallback string `json:"fallback"`
|
Fallback string `json:"fallback"`
|
||||||
Color string `json:"color"`
|
Color string `json:"color"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Text string `json:"text"`
|
TitleLink string `json:"title_link"`
|
||||||
|
Text string `json:"text"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSecret sets the slack secret
|
// SetSecret sets the slack secret
|
||||||
@@ -133,70 +134,78 @@ func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, e
|
|||||||
|
|
||||||
func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
|
func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
|
||||||
fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
|
titleLink := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index)
|
||||||
var text, title, attachmentText string
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
|
var text, attachmentText string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookIssueOpened:
|
case api.HookIssueOpened:
|
||||||
text = fmt.Sprintf("[%s] Issue submitted by %s", p.Repository.FullName, senderLink)
|
text = fmt.Sprintf("[%s] Issue opened by %s", repoLink, senderLink)
|
||||||
title = titleLink
|
|
||||||
attachmentText = SlackTextFormatter(p.Issue.Body)
|
attachmentText = SlackTextFormatter(p.Issue.Body)
|
||||||
case api.HookIssueClosed:
|
case api.HookIssueClosed:
|
||||||
text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueReOpened:
|
case api.HookIssueReOpened:
|
||||||
text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueEdited:
|
case api.HookIssueEdited:
|
||||||
text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
attachmentText = SlackTextFormatter(p.Issue.Body)
|
attachmentText = SlackTextFormatter(p.Issue.Body)
|
||||||
case api.HookIssueAssigned:
|
case api.HookIssueAssigned:
|
||||||
text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName,
|
text = fmt.Sprintf("[%s] Issue assigned to %s: [%s](%s) by %s", repoLink,
|
||||||
SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
|
SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
|
||||||
titleLink, senderLink)
|
title, titleLink, senderLink)
|
||||||
case api.HookIssueUnassigned:
|
case api.HookIssueUnassigned:
|
||||||
text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueLabelUpdated:
|
case api.HookIssueLabelUpdated:
|
||||||
text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueLabelCleared:
|
case api.HookIssueLabelCleared:
|
||||||
text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueSynchronized:
|
case api.HookIssueSynchronized:
|
||||||
text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueMilestoned:
|
case api.HookIssueMilestoned:
|
||||||
text = fmt.Sprintf("[%s] Issue milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
|
mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
|
||||||
|
text = fmt.Sprintf("[%s] Issue milestoned to [%s](%s): [%s](%s) by %s", repoLink,
|
||||||
|
p.Issue.Milestone.Title, mileStoneLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueDemilestoned:
|
case api.HookIssueDemilestoned:
|
||||||
text = fmt.Sprintf("[%s] Issue milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue milestone cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SlackPayload{
|
pl := &SlackPayload{
|
||||||
Channel: slack.Channel,
|
Channel: slack.Channel,
|
||||||
Text: text,
|
Text: text,
|
||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
}
|
||||||
Color: slack.Color,
|
if attachmentText != "" {
|
||||||
Title: title,
|
pl.Attachments = []SlackAttachment{{
|
||||||
Text: attachmentText,
|
Color: slack.Color,
|
||||||
}},
|
Title: title,
|
||||||
}, nil
|
TitleLink: titleLink,
|
||||||
|
Text: attachmentText,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
|
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)),
|
title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
|
||||||
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
var text, title, attachmentText string
|
var text, titleLink, attachmentText string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookIssueCommentCreated:
|
case api.HookIssueCommentCreated:
|
||||||
text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
|
text = fmt.Sprintf("[%s] New comment created by %s", repoLink, senderLink)
|
||||||
title = titleLink
|
titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
|
||||||
attachmentText = SlackTextFormatter(p.Comment.Body)
|
attachmentText = SlackTextFormatter(p.Comment.Body)
|
||||||
case api.HookIssueCommentEdited:
|
case api.HookIssueCommentEdited:
|
||||||
text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
|
text = fmt.Sprintf("[%s] Comment edited by %s", repoLink, senderLink)
|
||||||
title = titleLink
|
titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
|
||||||
attachmentText = SlackTextFormatter(p.Comment.Body)
|
attachmentText = SlackTextFormatter(p.Comment.Body)
|
||||||
case api.HookIssueCommentDeleted:
|
case api.HookIssueCommentDeleted:
|
||||||
text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink)
|
text = fmt.Sprintf("[%s] Comment deleted by %s", repoLink, senderLink)
|
||||||
title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
|
titleLink = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
|
||||||
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
|
|
||||||
attachmentText = SlackTextFormatter(p.Comment.Body)
|
attachmentText = SlackTextFormatter(p.Comment.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,9 +215,10 @@ func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (
|
|||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
Attachments: []SlackAttachment{{
|
||||||
Color: slack.Color,
|
Color: slack.Color,
|
||||||
Title: title,
|
Title: title,
|
||||||
Text: attachmentText,
|
TitleLink: titleLink,
|
||||||
|
Text: attachmentText,
|
||||||
}},
|
}},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -281,65 +291,75 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
|
|||||||
|
|
||||||
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
|
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
|
||||||
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
|
titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
|
||||||
var text, title, attachmentText string
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
|
var text, attachmentText string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookIssueOpened:
|
case api.HookIssueOpened:
|
||||||
text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink)
|
text = fmt.Sprintf("[%s] Pull request opened by %s", repoLink, senderLink)
|
||||||
title = titleLink
|
|
||||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||||
case api.HookIssueClosed:
|
case api.HookIssueClosed:
|
||||||
if p.PullRequest.HasMerged {
|
if p.PullRequest.HasMerged {
|
||||||
text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request merged: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
} else {
|
} else {
|
||||||
text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
case api.HookIssueReOpened:
|
case api.HookIssueReOpened:
|
||||||
text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueEdited:
|
case api.HookIssueEdited:
|
||||||
text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||||
case api.HookIssueAssigned:
|
case api.HookIssueAssigned:
|
||||||
list := make([]string, len(p.PullRequest.Assignees))
|
list := make([]string, len(p.PullRequest.Assignees))
|
||||||
for i, user := range p.PullRequest.Assignees {
|
for i, user := range p.PullRequest.Assignees {
|
||||||
list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName)
|
list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName)
|
||||||
}
|
}
|
||||||
text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
|
text = fmt.Sprintf("[%s] Pull request assigned to %s: [%s](%s) by %s", repoLink,
|
||||||
strings.Join(list, ", "),
|
strings.Join(list, ", "),
|
||||||
titleLink, senderLink)
|
title, titleLink, senderLink)
|
||||||
case api.HookIssueUnassigned:
|
case api.HookIssueUnassigned:
|
||||||
text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueLabelUpdated:
|
case api.HookIssueLabelUpdated:
|
||||||
text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueLabelCleared:
|
case api.HookIssueLabelCleared:
|
||||||
text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueSynchronized:
|
case api.HookIssueSynchronized:
|
||||||
text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueMilestoned:
|
case api.HookIssueMilestoned:
|
||||||
text = fmt.Sprintf("[%s] Pull request milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
|
mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
|
||||||
|
text = fmt.Sprintf("[%s] Pull request milestoned to [%s](%s): [%s](%s) %s", repoLink,
|
||||||
|
p.PullRequest.Milestone.Title, mileStoneLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueDemilestoned:
|
case api.HookIssueDemilestoned:
|
||||||
text = fmt.Sprintf("[%s] Pull request milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request milestone cleared: [%s](%s) %s", repoLink, title, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SlackPayload{
|
pl := &SlackPayload{
|
||||||
Channel: slack.Channel,
|
Channel: slack.Channel,
|
||||||
Text: text,
|
Text: text,
|
||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
}
|
||||||
Color: slack.Color,
|
if attachmentText != "" {
|
||||||
Title: title,
|
pl.Attachments = []SlackAttachment{{
|
||||||
Text: attachmentText,
|
Color: slack.Color,
|
||||||
}},
|
Title: title,
|
||||||
}, nil
|
TitleLink: titleLink,
|
||||||
|
Text: attachmentText,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event HookEventType) (*SlackPayload, error) {
|
func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event HookEventType) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
|
||||||
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
|
titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
|
||||||
var text, title, attachmentText string
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
|
var text string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookIssueSynchronized:
|
case api.HookIssueSynchronized:
|
||||||
action, err := parseHookPullRequestEventType(event)
|
action, err := parseHookPullRequestEventType(event)
|
||||||
@@ -347,7 +367,7 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
text = fmt.Sprintf("[%s] Pull request review %s : %s by %s", p.Repository.FullName, action, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SlackPayload{
|
return &SlackPayload{
|
||||||
@@ -355,17 +375,13 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
|
|||||||
Text: text,
|
Text: text,
|
||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
|
||||||
Color: slack.Color,
|
|
||||||
Title: title,
|
|
||||||
Text: attachmentText,
|
|
||||||
}},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
|
func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
var text, title, attachmentText string
|
var text, title, attachmentText string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookRepoCreated:
|
case api.HookRepoCreated:
|
||||||
text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
|
text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
|
||||||
@@ -380,9 +396,10 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla
|
|||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
Attachments: []SlackAttachment{{
|
||||||
Color: slack.Color,
|
Color: slack.Color,
|
||||||
Title: title,
|
Title: title,
|
||||||
Text: attachmentText,
|
TitleLink: title,
|
||||||
|
Text: attachmentText,
|
||||||
}},
|
}},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,26 +138,31 @@ func populateIssueIndexer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
is, err := models.Issues(&models.IssuesOptions{
|
UpdateRepoIndexer(repo)
|
||||||
RepoIDs: []int64{repo.ID},
|
|
||||||
IsClosed: util.OptionalBoolNone,
|
|
||||||
IsPull: util.OptionalBoolNone,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Issues: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err = models.IssueList(is).LoadDiscussComments(); err != nil {
|
|
||||||
log.Error("LoadComments: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, issue := range is {
|
|
||||||
UpdateIssueIndexer(issue)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRepoIndexer add/update all issues of the repositories
|
||||||
|
func UpdateRepoIndexer(repo *models.Repository) {
|
||||||
|
is, err := models.Issues(&models.IssuesOptions{
|
||||||
|
RepoIDs: []int64{repo.ID},
|
||||||
|
IsClosed: util.OptionalBoolNone,
|
||||||
|
IsPull: util.OptionalBoolNone,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Issues: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = models.IssueList(is).LoadDiscussComments(); err != nil {
|
||||||
|
log.Error("LoadComments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, issue := range is {
|
||||||
|
UpdateIssueIndexer(issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateIssueIndexer add/update an issue to the issue indexer
|
// UpdateIssueIndexer add/update an issue to the issue indexer
|
||||||
func UpdateIssueIndexer(issue *models.Issue) {
|
func UpdateIssueIndexer(issue *models.Issue) {
|
||||||
var comments []string
|
var comments []string
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type Uploader interface {
|
|||||||
CreateTopics(topic ...string) error
|
CreateTopics(topic ...string) error
|
||||||
CreateMilestones(milestones ...*Milestone) error
|
CreateMilestones(milestones ...*Milestone) error
|
||||||
CreateReleases(releases ...*Release) error
|
CreateReleases(releases ...*Release) error
|
||||||
|
SyncTags() error
|
||||||
CreateLabels(labels ...*Label) error
|
CreateLabels(labels ...*Label) error
|
||||||
CreateIssues(issues ...*Issue) error
|
CreateIssues(issues ...*Issue) error
|
||||||
CreateComments(comments ...*Comment) error
|
CreateComments(comments ...*Comment) error
|
||||||
|
|||||||
@@ -288,11 +288,12 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
|
|||||||
|
|
||||||
rels = append(rels, &rel)
|
rels = append(rels, &rel)
|
||||||
}
|
}
|
||||||
if err := models.InsertReleases(rels...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync tags to releases in database
|
return models.InsertReleases(rels...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncTags syncs releases with tags in the database
|
||||||
|
func (g *GiteaLocalUploader) SyncTags() error {
|
||||||
return models.SyncReleasesWithTags(g.repo, g.gitRepo)
|
return models.SyncReleasesWithTags(g.repo, g.gitRepo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOpt
|
|||||||
opts.PullRequests = false
|
opts.PullRequests = false
|
||||||
opts.GitServiceType = structs.PlainGitService
|
opts.GitServiceType = structs.PlainGitService
|
||||||
downloader = NewPlainGitDownloader(ownerName, opts.RepoName, opts.CloneAddr)
|
downloader = NewPlainGitDownloader(ownerName, opts.RepoName, opts.CloneAddr)
|
||||||
log.Trace("Will migrate from git: %s", opts.CloneAddr)
|
log.Trace("Will migrate from git: %s", opts.OriginalURL)
|
||||||
} else if opts.GitServiceType == structs.NotMigrated {
|
} else if opts.GitServiceType == structs.NotMigrated {
|
||||||
opts.GitServiceType = theFactory.GitServiceType()
|
opts.GitServiceType = theFactory.GitServiceType()
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOpt
|
|||||||
log.Error("rollback failed: %v", err1)
|
log.Error("rollback failed: %v", err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err2 := models.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.CloneAddr, err)); err2 != nil {
|
if err2 := models.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
|
||||||
log.Error("create respotiry notice failed: ", err2)
|
log.Error("create respotiry notice failed: ", err2)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -165,6 +165,11 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts
|
|||||||
}
|
}
|
||||||
releases = releases[relBatchSize:]
|
releases = releases[relBatchSize:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Once all releases (if any) are inserted, sync any remaining non-release tags
|
||||||
|
if err := uploader.SyncTags(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var commentBatchSize = uploader.MaxBatchInsertSize("comment")
|
var commentBatchSize = uploader.MaxBatchInsertSize("comment")
|
||||||
|
|||||||
@@ -107,3 +107,8 @@ func (r *indexerNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
|
|||||||
func (r *indexerNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
|
func (r *indexerNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
|
||||||
issue_indexer.UpdateIssueIndexer(issue)
|
issue_indexer.UpdateIssueIndexer(issue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *indexerNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
|
||||||
|
issue_indexer.UpdateRepoIndexer(repo)
|
||||||
|
models.UpdateRepoIndexer(repo)
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo
|
|||||||
|
|
||||||
message := strings.TrimSpace(opts.Message)
|
message := strings.TrimSpace(opts.Message)
|
||||||
|
|
||||||
author, committer := GetAuthorAndCommitterUsers(opts.Committer, opts.Author, doer)
|
author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
|
||||||
|
|
||||||
t, err := NewTemporaryUploadRepository(repo)
|
t, err := NewTemporaryUploadRepository(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func GetFileCommitResponse(repo *models.Repository, commit *git.Commit) (*api.Fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthorAndCommitterUsers Gets the author and committer user objects from the IdentityOptions
|
// GetAuthorAndCommitterUsers Gets the author and committer user objects from the IdentityOptions
|
||||||
func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models.User) (committerUser, authorUser *models.User) {
|
func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models.User) (authorUser, committerUser *models.User) {
|
||||||
// Committer and author are optional. If they are not the doer (not same email address)
|
// Committer and author are optional. If they are not the doer (not same email address)
|
||||||
// then we use bogus User objects for them to store their FullName and Email.
|
// then we use bogus User objects for them to store their FullName and Email.
|
||||||
// If only one of the two are provided, we set both of them to it.
|
// If only one of the two are provided, we set both of them to it.
|
||||||
|
|||||||
@@ -79,11 +79,11 @@ func GetTreeBySHA(repo *models.Repository, sha string, page, perPage int, recurs
|
|||||||
for e := rangeStart; e < rangeEnd; e++ {
|
for e := rangeStart; e < rangeEnd; e++ {
|
||||||
i := e - rangeStart
|
i := e - rangeStart
|
||||||
|
|
||||||
tree.Entries[e].Path = entries[e].Name()
|
tree.Entries[i].Path = entries[e].Name()
|
||||||
tree.Entries[e].Mode = fmt.Sprintf("%06o", entries[e].Mode())
|
tree.Entries[i].Mode = fmt.Sprintf("%06o", entries[e].Mode())
|
||||||
tree.Entries[e].Type = entries[e].Type()
|
tree.Entries[i].Type = entries[e].Type()
|
||||||
tree.Entries[e].Size = entries[e].Size()
|
tree.Entries[i].Size = entries[e].Size()
|
||||||
tree.Entries[e].SHA = entries[e].ID.String()
|
tree.Entries[i].SHA = entries[e].ID.String()
|
||||||
|
|
||||||
if entries[e].IsDir() {
|
if entries[e].IsDir() {
|
||||||
copy(treeURL[copyPos:], entries[e].ID.String())
|
copy(treeURL[copyPos:], entries[e].ID.String())
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
|
|||||||
|
|
||||||
message := strings.TrimSpace(opts.Message)
|
message := strings.TrimSpace(opts.Message)
|
||||||
|
|
||||||
author, committer := GetAuthorAndCommitterUsers(opts.Committer, opts.Author, doer)
|
author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
|
||||||
|
|
||||||
t, err := NewTemporaryUploadRepository(repo)
|
t, err := NewTemporaryUploadRepository(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -453,10 +453,15 @@ func PushUpdate(repo *models.Repository, branch string, opts models.PushUpdateOp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if !isDelRef {
|
} else if !isDelRef {
|
||||||
|
branchName := opts.RefFullName[len(git.BranchPrefix):]
|
||||||
|
if err = models.RemoveDeletedBranch(repo.ID, branchName); err != nil {
|
||||||
|
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, branchName, err)
|
||||||
|
}
|
||||||
|
|
||||||
// If is branch reference
|
// If is branch reference
|
||||||
|
|
||||||
// Clear cache for branch commit count
|
// Clear cache for branch commit count
|
||||||
cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
|
cache.Remove(repo.GetCommitsCountCacheKey(branchName, true))
|
||||||
|
|
||||||
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
|
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -97,8 +97,6 @@ func runMigrateTask(t *models.Task) (err error) {
|
|||||||
opts.MigrateToRepoID = t.RepoID
|
opts.MigrateToRepoID = t.RepoID
|
||||||
repo, err := migrations.MigrateRepository(t.Doer, t.Owner.Name, *opts)
|
repo, err := migrations.MigrateRepository(t.Doer, t.Owner.Name, *opts)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
notification.NotifyMigrateRepository(t.Doer, t.Owner, repo)
|
|
||||||
|
|
||||||
log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
|
log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,6 +239,14 @@ func NewFuncMap() []template.FuncMap {
|
|||||||
"MirrorFullAddress": mirror_service.AddressNoCredentials,
|
"MirrorFullAddress": mirror_service.AddressNoCredentials,
|
||||||
"MirrorUserName": mirror_service.Username,
|
"MirrorUserName": mirror_service.Username,
|
||||||
"MirrorPassword": mirror_service.Password,
|
"MirrorPassword": mirror_service.Password,
|
||||||
|
"contain": func(s []int64, id int64) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -352,8 +352,8 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = models.UpdateIssue(issue); err != nil {
|
if err = models.UpdateIssueByAPI(issue); err != nil {
|
||||||
ctx.Error(500, "UpdateIssue", err)
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if form.State != nil {
|
if form.State != nil {
|
||||||
@@ -372,7 +372,11 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
|||||||
// Refetch from database to assign some automatic values
|
// Refetch from database to assign some automatic values
|
||||||
issue, err = models.GetIssueByID(issue.ID)
|
issue, err = models.GetIssueByID(issue.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(500, "GetIssueByID", err)
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = issue.LoadMilestone(); err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.JSON(201, issue.APIFormat())
|
ctx.JSON(201, issue.APIFormat())
|
||||||
|
|||||||
@@ -420,8 +420,8 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = models.UpdateIssue(issue); err != nil {
|
if err = models.UpdateIssueByAPI(issue); err != nil {
|
||||||
ctx.Error(500, "UpdateIssue", err)
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if form.State != nil {
|
if form.State != nil {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -431,15 +433,53 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
|
|||||||
opts.Releases = false
|
opts.Releases = false
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts)
|
repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
|
||||||
if err == nil {
|
Name: opts.RepoName,
|
||||||
notification.NotifyCreateRepository(ctx.User, ctxUser, repo)
|
Description: opts.Description,
|
||||||
|
OriginalURL: opts.CloneAddr,
|
||||||
log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
|
IsPrivate: opts.Private,
|
||||||
ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
|
IsMirror: opts.Mirror,
|
||||||
|
Status: models.RepositoryBeingMigrated,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
handleMigrateError(ctx, ctxUser, remoteAddr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts.MigrateToRepoID = repo.ID
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprintf(&buf, "Handler crashed with error: %v", log.Stack(2))
|
||||||
|
err = errors.New(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
repo.Status = models.RepositoryReady
|
||||||
|
if err := models.UpdateRepositoryCols(repo, "status"); err == nil {
|
||||||
|
notification.NotifyMigrateRepository(ctx.User, ctxUser, repo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo != nil {
|
||||||
|
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
|
||||||
|
log.Error("DeleteRepository: %v", errDelete)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err = migrations.MigrateRepository(ctx.User, ctxUser.Name, opts); err != nil {
|
||||||
|
handleMigrateError(ctx, ctxUser, remoteAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
|
||||||
|
ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteAddr string, err error) {
|
||||||
switch {
|
switch {
|
||||||
case models.IsErrRepoAlreadyExist(err):
|
case models.IsErrRepoAlreadyExist(err):
|
||||||
ctx.Error(409, "", "The repository with the same name already exists.")
|
ctx.Error(409, "", "The repository with the same name already exists.")
|
||||||
@@ -448,7 +488,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
|
|||||||
case migrations.IsTwoFactorAuthError(err):
|
case migrations.IsTwoFactorAuthError(err):
|
||||||
ctx.Error(422, "", "Remote visit required two factors authentication.")
|
ctx.Error(422, "", "Remote visit required two factors authentication.")
|
||||||
case models.IsErrReachLimitOfRepo(err):
|
case models.IsErrReachLimitOfRepo(err):
|
||||||
ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit()))
|
ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
|
||||||
case models.IsErrNameReserved(err):
|
case models.IsErrNameReserved(err):
|
||||||
ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
|
ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
|
||||||
case models.IsErrNamePatternNotAllowed(err):
|
case models.IsErrNamePatternNotAllowed(err):
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) {
|
|||||||
// schema:
|
// schema:
|
||||||
// "$ref": "#/definitions/CreateStatusOption"
|
// "$ref": "#/definitions/CreateStatusOption"
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "201":
|
||||||
// "$ref": "#/responses/StatusList"
|
// "$ref": "#/responses/Status"
|
||||||
sha := ctx.Params("sha")
|
sha := ctx.Params("sha")
|
||||||
if len(sha) == 0 {
|
if len(sha) == 0 {
|
||||||
ctx.Error(400, "sha not given", nil)
|
ctx.Error(400, "sha not given", nil)
|
||||||
|
|||||||
@@ -261,7 +261,8 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["IssueStats"] = issueStats
|
ctx.Data["IssueStats"] = issueStats
|
||||||
ctx.Data["SelectLabels"] = com.StrTo(selectLabels).MustInt64()
|
ctx.Data["SelLabelIDs"] = labelIDs
|
||||||
|
ctx.Data["SelectLabels"] = selectLabels
|
||||||
ctx.Data["ViewType"] = viewType
|
ctx.Data["ViewType"] = viewType
|
||||||
ctx.Data["SortType"] = sortType
|
ctx.Data["SortType"] = sortType
|
||||||
ctx.Data["MilestoneID"] = milestoneID
|
ctx.Data["MilestoneID"] = milestoneID
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/notification"
|
"code.gitea.io/gitea/modules/notification"
|
||||||
|
"code.gitea.io/gitea/modules/repofiles"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/gitdiff"
|
"code.gitea.io/gitea/services/gitdiff"
|
||||||
@@ -928,6 +929,21 @@ func CleanUpPullRequest(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := repofiles.PushUpdate(
|
||||||
|
pr.HeadRepo,
|
||||||
|
pr.HeadBranch,
|
||||||
|
models.PushUpdateOptions{
|
||||||
|
RefFullName: git.BranchPrefix + pr.HeadBranch,
|
||||||
|
OldCommitID: branchCommitID,
|
||||||
|
NewCommitID: git.EmptySHA,
|
||||||
|
PusherID: ctx.User.ID,
|
||||||
|
PusherName: ctx.User.Name,
|
||||||
|
RepoUserName: pr.HeadRepo.Owner.Name,
|
||||||
|
RepoName: pr.HeadRepo.Name,
|
||||||
|
}); err != nil {
|
||||||
|
log.Error("Update: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := models.AddDeletePRBranchComment(ctx.User, pr.BaseRepo, issue.ID, pr.HeadBranch); err != nil {
|
if err := models.AddDeletePRBranchComment(ctx.User, pr.BaseRepo, issue.ID, pr.HeadBranch); err != nil {
|
||||||
// Do not fail here as branch has already been deleted
|
// Do not fail here as branch has already been deleted
|
||||||
log.Error("DeleteBranch: %v", err)
|
log.Error("DeleteBranch: %v", err)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
@@ -23,14 +24,19 @@ func Avatar(ctx *context.Context) {
|
|||||||
|
|
||||||
log.Debug("Asked avatar for user %v and size %v", userName, size)
|
log.Debug("Asked avatar for user %v and size %v", userName, size)
|
||||||
|
|
||||||
user, err := models.GetUserByName(userName)
|
var user *models.User
|
||||||
if err != nil {
|
if strings.ToLower(userName) != "ghost" {
|
||||||
if models.IsErrUserNotExist(err) {
|
user, err = models.GetUserByName(userName)
|
||||||
ctx.ServerError("Requested avatar for invalid user", err)
|
if err != nil {
|
||||||
} else {
|
if models.IsErrUserNotExist(err) {
|
||||||
ctx.ServerError("Retrieving user by name", err)
|
ctx.ServerError("Requested avatar for invalid user", err)
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("Retrieving user by name", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
} else {
|
||||||
|
user = models.NewGhostUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(user.RealSizedAvatarLink(size))
|
ctx.Redirect(user.RealSizedAvatarLink(size))
|
||||||
|
|||||||
@@ -154,7 +154,7 @@
|
|||||||
<div class="menu">
|
<div class="menu">
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
<div class="item issue-action has-emoji" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
|
<div class="item issue-action has-emoji" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
|
||||||
<span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
|
<span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
<div class="menu">
|
<div class="menu">
|
||||||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a>
|
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a>
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
<a class="item has-emoji" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.ID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
|
<a class="item has-emoji" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.ID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
<div class="menu">
|
<div class="menu">
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
<div class="item issue-action has-emoji" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
|
<div class="item issue-action has-emoji" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
|
||||||
<span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
|
<span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5284,8 +5284,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"201": {
|
||||||
"$ref": "#/responses/StatusList"
|
"$ref": "#/responses/Status"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user