mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge branch 'master'
This commit is contained in:
+101
-9
@@ -1,4 +1,5 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
@@ -62,7 +63,7 @@ var (
|
||||
issueReferenceKeywordsPat *regexp.Regexp
|
||||
)
|
||||
|
||||
const issueRefRegexpStr = `(?:\S+/\S=)?#\d+`
|
||||
const issueRefRegexpStr = `(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)+`
|
||||
|
||||
func assembleKeywordsPattern(words []string) string {
|
||||
return fmt.Sprintf(`(?i)(?:%s) %s`, strings.Join(words, "|"), issueRefRegexpStr)
|
||||
@@ -143,6 +144,22 @@ func (a *Action) ShortActUserName() string {
|
||||
return base.EllipsisString(a.GetActUserName(), 20)
|
||||
}
|
||||
|
||||
// GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME
|
||||
func (a *Action) GetDisplayName() string {
|
||||
if setting.UI.DefaultShowFullName {
|
||||
return a.GetActFullName()
|
||||
}
|
||||
return a.ShortActUserName()
|
||||
}
|
||||
|
||||
// GetDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME
|
||||
func (a *Action) GetDisplayNameTitle() string {
|
||||
if setting.UI.DefaultShowFullName {
|
||||
return a.ShortActUserName()
|
||||
}
|
||||
return a.GetActFullName()
|
||||
}
|
||||
|
||||
// GetActAvatar the action's user's avatar link
|
||||
func (a *Action) GetActAvatar() string {
|
||||
a.loadActUser()
|
||||
@@ -192,6 +209,21 @@ func (a *Action) GetRepoLink() string {
|
||||
return "/" + a.GetRepoPath()
|
||||
}
|
||||
|
||||
// GetRepositoryFromMatch returns a *Repository from a username and repo strings
|
||||
func GetRepositoryFromMatch(ownerName string, repoName string) (*Repository, error) {
|
||||
var err error
|
||||
refRepo, err := GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||
if err != nil {
|
||||
if IsErrRepoNotExist(err) {
|
||||
log.Warn("Repository referenced in commit but does not exist: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Error("GetRepositoryByOwnerAndName: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return refRepo, nil
|
||||
}
|
||||
|
||||
// GetCommentLink returns link to action comment.
|
||||
func (a *Action) GetCommentLink() string {
|
||||
return a.getCommentLink(x)
|
||||
@@ -521,8 +553,24 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra
|
||||
c := commits[i]
|
||||
|
||||
refMarked := make(map[int64]bool)
|
||||
for _, ref := range issueReferenceKeywordsPat.FindAllString(c.Message, -1) {
|
||||
issue, err := getIssueFromRef(repo, ref)
|
||||
var refRepo *Repository
|
||||
var err error
|
||||
for _, m := range issueReferenceKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
|
||||
if len(m[3]) == 0 {
|
||||
continue
|
||||
}
|
||||
ref := m[3]
|
||||
|
||||
// issue is from another repo
|
||||
if len(m[1]) > 0 && len(m[2]) > 0 {
|
||||
refRepo, err = GetRepositoryFromMatch(string(m[1]), string(m[2]))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
refRepo = repo
|
||||
}
|
||||
issue, err := getIssueFromRef(refRepo, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -533,7 +581,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra
|
||||
refMarked[issue.ID] = true
|
||||
|
||||
message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, c.Message)
|
||||
if err = CreateRefComment(doer, repo, issue, message, c.Sha1); err != nil {
|
||||
if err = CreateRefComment(doer, refRepo, issue, message, c.Sha1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -543,19 +591,63 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra
|
||||
if repo.DefaultBranch != branchName && !repo.CloseIssuesViaCommitInAnyBranch {
|
||||
continue
|
||||
}
|
||||
|
||||
refMarked = make(map[int64]bool)
|
||||
for _, ref := range issueCloseKeywordsPat.FindAllString(c.Message, -1) {
|
||||
if err := changeIssueStatus(repo, doer, ref, refMarked, true); err != nil {
|
||||
for _, m := range issueCloseKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
|
||||
if len(m[3]) == 0 {
|
||||
continue
|
||||
}
|
||||
ref := m[3]
|
||||
|
||||
// issue is from another repo
|
||||
if len(m[1]) > 0 && len(m[2]) > 0 {
|
||||
refRepo, err = GetRepositoryFromMatch(string(m[1]), string(m[2]))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
refRepo = repo
|
||||
}
|
||||
|
||||
perm, err := GetUserRepoPermission(refRepo, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// only close issues in another repo if user has push access
|
||||
if perm.CanWrite(UnitTypeCode) {
|
||||
if err := changeIssueStatus(refRepo, doer, ref, refMarked, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It is conflict to have close and reopen at same time, so refsMarked doesn't need to reinit here.
|
||||
for _, ref := range issueReopenKeywordsPat.FindAllString(c.Message, -1) {
|
||||
if err := changeIssueStatus(repo, doer, ref, refMarked, false); err != nil {
|
||||
for _, m := range issueReopenKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
|
||||
if len(m[3]) == 0 {
|
||||
continue
|
||||
}
|
||||
ref := m[3]
|
||||
|
||||
// issue is from another repo
|
||||
if len(m[1]) > 0 && len(m[2]) > 0 {
|
||||
refRepo, err = GetRepositoryFromMatch(string(m[1]), string(m[2]))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
refRepo = repo
|
||||
}
|
||||
|
||||
perm, err := GetUserRepoPermission(refRepo, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// only reopen issues in another repo if user has push access
|
||||
if perm.CanWrite(UnitTypeCode) {
|
||||
if err := changeIssueStatus(refRepo, doer, ref, refMarked, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -294,6 +294,76 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
|
||||
// Test that a push to default branch closes issue in another repo
|
||||
// If the user also has push permissions to that repo
|
||||
pushCommits := []*PushCommit{
|
||||
{
|
||||
Sha1: "abcdef1",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User Two",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User Two",
|
||||
Message: "close user2/repo1#1",
|
||||
},
|
||||
}
|
||||
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
|
||||
commentBean := &Comment{
|
||||
Type: CommentTypeCommitRef,
|
||||
CommitSHA: "abcdef1",
|
||||
PosterID: user.ID,
|
||||
IssueID: 1,
|
||||
}
|
||||
|
||||
issueBean := &Issue{RepoID: 1, Index: 1, ID: 1}
|
||||
|
||||
AssertNotExistsBean(t, commentBean)
|
||||
AssertNotExistsBean(t, issueBean, "is_closed=1")
|
||||
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
|
||||
AssertExistsAndLoadBean(t, commentBean)
|
||||
AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
user := AssertExistsAndLoadBean(t, &User{ID: 10}).(*User)
|
||||
|
||||
// Test that a push with close reference *can not* close issue
|
||||
// If the commiter doesn't have push rights in that repo
|
||||
pushCommits := []*PushCommit{
|
||||
{
|
||||
Sha1: "abcdef3",
|
||||
CommitterEmail: "user10@example.com",
|
||||
CommitterName: "User Ten",
|
||||
AuthorEmail: "user10@example.com",
|
||||
AuthorName: "User Ten",
|
||||
Message: "close user3/repo3#1",
|
||||
},
|
||||
}
|
||||
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 6}).(*Repository)
|
||||
commentBean := &Comment{
|
||||
Type: CommentTypeCommitRef,
|
||||
CommitSHA: "abcdef3",
|
||||
PosterID: user.ID,
|
||||
IssueID: 6,
|
||||
}
|
||||
|
||||
issueBean := &Issue{RepoID: 3, Index: 1, ID: 6}
|
||||
|
||||
AssertNotExistsBean(t, commentBean)
|
||||
AssertNotExistsBean(t, issueBean, "is_closed=1")
|
||||
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
|
||||
AssertExistsAndLoadBean(t, commentBean)
|
||||
AssertNotExistsBean(t, issueBean, "is_closed=1")
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func testCorrectRepoAction(t *testing.T, opts CommitRepoActionOptions, actionBean *Action) {
|
||||
AssertNotExistsBean(t, actionBean)
|
||||
assert.NoError(t, CommitRepoAction(opts))
|
||||
|
||||
+2
-2
@@ -507,7 +507,7 @@ func (err ErrDeployKeyNameAlreadyUsed) Error() string {
|
||||
|
||||
// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
|
||||
type ErrAccessTokenNotExist struct {
|
||||
SHA string
|
||||
Token string
|
||||
}
|
||||
|
||||
// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
|
||||
@@ -517,7 +517,7 @@ func IsErrAccessTokenNotExist(err error) bool {
|
||||
}
|
||||
|
||||
func (err ErrAccessTokenNotExist) Error() string {
|
||||
return fmt.Sprintf("access token does not exist [sha: %s]", err.SHA)
|
||||
return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
|
||||
}
|
||||
|
||||
// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
id: 1
|
||||
uid: 1
|
||||
name: Token A
|
||||
sha1: hash1
|
||||
#token: d2c6c1ba3890b309189a8e618c72a162e4efbf36
|
||||
token_hash: 2b3668e11cb82d3af8c6e4524fc7841297668f5008d1626f0ad3417e9fa39af84c268248b78c481daa7e5dc437784003494f
|
||||
token_salt: QuSiZr1byZ
|
||||
token_last_eight: e4efbf36
|
||||
created_unix: 946687980
|
||||
updated_unix: 946687980
|
||||
|
||||
@@ -10,7 +13,10 @@
|
||||
id: 2
|
||||
uid: 1
|
||||
name: Token B
|
||||
sha1: hash2
|
||||
#token: 4c6f36e6cf498e2a448662f915d932c09c5a146c
|
||||
token_hash: 1a0e32a231ebbd582dc626c1543a42d3c63d4fa76c07c72862721467c55e8f81c923d60700f0528b5f5f443f055559d3a279
|
||||
token_salt: Lfwopukrq5
|
||||
token_last_eight: 9c5a146c
|
||||
created_unix: 946687980
|
||||
updated_unix: 946687980
|
||||
|
||||
@@ -18,6 +24,10 @@
|
||||
id: 3
|
||||
uid: 2
|
||||
name: Token A
|
||||
sha1: hash3
|
||||
#token: 90a18faa671dc43924b795806ffe4fd169d28c91
|
||||
token_hash: d6d404048048812d9e911d93aefbe94fc768d4876fdf75e3bef0bdc67828e0af422846d3056f2f25ec35c51dc92075685ec5
|
||||
token_salt: 99ArgXKlQQ
|
||||
token_last_eight: 69d28c91
|
||||
created_unix: 946687980
|
||||
updated_unix: 946687980
|
||||
#commented out tokens so you can see what they are in plaintext
|
||||
@@ -52,3 +52,15 @@
|
||||
tree_path: "README.md"
|
||||
created_unix: 946684812
|
||||
invalidated: true
|
||||
|
||||
-
|
||||
id: 7
|
||||
type: 21 # code comment
|
||||
poster_id: 100
|
||||
issue_id: 3
|
||||
content: "a review from a deleted user"
|
||||
line: -4
|
||||
review_id: 10
|
||||
tree_path: "README.md"
|
||||
created_unix: 946684812
|
||||
invalidated: true
|
||||
@@ -374,4 +374,32 @@
|
||||
repo_id: 41
|
||||
type: 3
|
||||
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 55
|
||||
repo_id: 10
|
||||
type: 1
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 56
|
||||
repo_id: 10
|
||||
type: 2
|
||||
config: "{\"EnableTimetracker\":true,\"AllowOnlyContributorsToTrackTime\":true}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 57
|
||||
repo_id: 10
|
||||
type: 3
|
||||
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 58
|
||||
repo_id: 11
|
||||
type: 1
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
@@ -70,3 +70,12 @@
|
||||
content: "New review 3 rejected"
|
||||
updated_unix: 946684810
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 10
|
||||
type: 3
|
||||
reviewer_id: 100
|
||||
issue_id: 3
|
||||
content: "a deleted user's review"
|
||||
updated_unix: 946684810
|
||||
created_unix: 946684810
|
||||
@@ -447,6 +447,7 @@ func (c *Comment) loadReview(e Engine) (err error) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.Review.Issue = c.Issue
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
// 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 models
|
||||
|
||||
import "github.com/go-xorm/xorm"
|
||||
|
||||
// InsertIssue insert one issue to database
|
||||
func InsertIssue(issue *Issue, labelIDs []int64) error {
|
||||
sess := x.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := insertIssue(sess, issue, labelIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func insertIssue(sess *xorm.Session, issue *Issue, labelIDs []int64) error {
|
||||
if issue.MilestoneID > 0 {
|
||||
sess.Incr("num_issues")
|
||||
if issue.IsClosed {
|
||||
sess.Incr("num_closed_issues")
|
||||
}
|
||||
if _, err := sess.ID(issue.MilestoneID).NoAutoTime().Update(new(Milestone)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := sess.NoAutoTime().Insert(issue); err != nil {
|
||||
return err
|
||||
}
|
||||
var issueLabels = make([]IssueLabel, 0, len(labelIDs))
|
||||
for _, labelID := range labelIDs {
|
||||
issueLabels = append(issueLabels, IssueLabel{
|
||||
IssueID: issue.ID,
|
||||
LabelID: labelID,
|
||||
})
|
||||
}
|
||||
if _, err := sess.Insert(issueLabels); err != nil {
|
||||
return err
|
||||
}
|
||||
if !issue.IsPull {
|
||||
sess.ID(issue.RepoID).Incr("num_issues")
|
||||
if issue.IsClosed {
|
||||
sess.Incr("num_closed_issues")
|
||||
}
|
||||
} else {
|
||||
sess.ID(issue.RepoID).Incr("num_pulls")
|
||||
if issue.IsClosed {
|
||||
sess.Incr("num_closed_pulls")
|
||||
}
|
||||
}
|
||||
if _, err := sess.NoAutoTime().Update(issue.Repo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess.Incr("num_issues")
|
||||
if issue.IsClosed {
|
||||
sess.Incr("num_closed_issues")
|
||||
}
|
||||
if _, err := sess.In("id", labelIDs).Update(new(Label)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if issue.MilestoneID > 0 {
|
||||
if _, err := sess.ID(issue.MilestoneID).SetExpr("completeness", "num_closed_issues * 100 / num_issues").Update(new(Milestone)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertComment inserted a comment
|
||||
func InsertComment(comment *Comment) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := sess.NoAutoTime().Insert(comment); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := sess.ID(comment.IssueID).Incr("num_comments").Update(new(Issue)); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// InsertPullRequest inserted a pull request
|
||||
func InsertPullRequest(pr *PullRequest, labelIDs []int64) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertIssue(sess, pr.Issue, labelIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
pr.IssueID = pr.Issue.ID
|
||||
if _, err := sess.NoAutoTime().Insert(pr); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// MigrateRelease migrates release
|
||||
func MigrateRelease(rel *Release) error {
|
||||
sess := x.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var oriRel = Release{
|
||||
RepoID: rel.RepoID,
|
||||
TagName: rel.TagName,
|
||||
}
|
||||
exist, err := sess.Get(&oriRel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
if _, err := sess.NoAutoTime().Insert(rel); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
rel.ID = oriRel.ID
|
||||
if _, err := sess.ID(rel.ID).Cols("target, title, note, is_tag, num_commits").Update(rel); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(rel.Attachments); i++ {
|
||||
rel.Attachments[i].ReleaseID = rel.ID
|
||||
}
|
||||
|
||||
if _, err := sess.NoAutoTime().Insert(rel.Attachments); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
@@ -224,6 +224,10 @@ var migrations = []Migration{
|
||||
// v84 -> v85
|
||||
NewMigration("add table to store original imported gpg keys", addGPGKeyImport),
|
||||
// v85 -> v86
|
||||
NewMigration("hash application token", hashAppToken),
|
||||
// v86 -> v87
|
||||
NewMigration("add http method to webhook", addHTTPMethodToWebhook),
|
||||
// v87 -> v88
|
||||
NewMigration("add includes_all_repositories to teams", addTeamIncludesAllRepositories),
|
||||
}
|
||||
|
||||
@@ -262,7 +266,7 @@ Please try to upgrade to a lower version (>= v0.6.0) first, then upgrade to curr
|
||||
return err
|
||||
}
|
||||
for i, m := range migrations[v-minDBVersion:] {
|
||||
log.Info("Migration: %s", m.Description())
|
||||
log.Info("Migration[%d]: %s", v+int64(i), m.Description())
|
||||
if err = m.Migrate(x); err != nil {
|
||||
return fmt.Errorf("do migrate: %v", err)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,14 @@ func renameRepoIsBareToIsEmpty(x *xorm.Engine) error {
|
||||
if models.DbCfg.Type == core.POSTGRES || models.DbCfg.Type == core.SQLITE {
|
||||
_, err = sess.Exec("DROP INDEX IF EXISTS IDX_repository_is_bare")
|
||||
} else if models.DbCfg.Type == core.MSSQL {
|
||||
_, err = sess.Exec("DROP INDEX IF EXISTS IDX_repository_is_bare ON repository")
|
||||
_, err = sess.Exec(`DECLARE @ConstraintName VARCHAR(256)
|
||||
DECLARE @SQL NVARCHAR(256)
|
||||
SELECT @ConstraintName = obj.name FROM sys.columns col LEFT OUTER JOIN sys.objects obj ON obj.object_id = col.default_object_id AND obj.type = 'D' WHERE col.object_id = OBJECT_ID('repository') AND obj.name IS NOT NULL AND col.name = 'is_bare'
|
||||
SET @SQL = N'ALTER TABLE [repository] DROP CONSTRAINT [' + @ConstraintName + N']'
|
||||
EXEC sp_executesql @SQL`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if models.DbCfg.Type == core.MYSQL {
|
||||
indexes, err := sess.QueryString(`SHOW INDEX FROM repository WHERE KEY_NAME = 'IDX_repository_is_bare'`)
|
||||
if err != nil {
|
||||
|
||||
+136
-8
@@ -5,21 +5,149 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
func addTeamIncludesAllRepositories(x *xorm.Engine) error {
|
||||
func hashAppToken(x *xorm.Engine) error {
|
||||
// AccessToken see models/token.go
|
||||
type AccessToken struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Sha1 string
|
||||
Token string `xorm:"-"`
|
||||
TokenHash string // sha256 of token - we will ensure UNIQUE later
|
||||
TokenSalt string
|
||||
TokenLastEight string `xorm:"token_last_eight"`
|
||||
|
||||
type Team struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IncludesAllRepositories bool `xorm:"NOT NULL DEFAULT false"`
|
||||
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
|
||||
HasRecentActivity bool `xorm:"-"`
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Team)); err != nil {
|
||||
// First remove the index
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := x.Exec("UPDATE `team` SET `includes_all_repositories` = ? WHERE `name`=?",
|
||||
true, "Owners")
|
||||
return err
|
||||
var err error
|
||||
if models.DbCfg.Type == core.POSTGRES || models.DbCfg.Type == core.SQLITE {
|
||||
_, err = sess.Exec("DROP INDEX IF EXISTS UQE_access_token_sha1")
|
||||
} else if models.DbCfg.Type == core.MSSQL {
|
||||
_, err = sess.Exec(`DECLARE @ConstraintName VARCHAR(256)
|
||||
DECLARE @SQL NVARCHAR(256)
|
||||
SELECT @ConstraintName = obj.name FROM sys.columns col LEFT OUTER JOIN sys.objects obj ON obj.object_id = col.default_object_id AND obj.type = 'D' WHERE col.object_id = OBJECT_ID('access_token') AND obj.name IS NOT NULL AND col.name = 'sha1'
|
||||
SET @SQL = N'ALTER TABLE [access_token] DROP CONSTRAINT [' + @ConstraintName + N']'
|
||||
EXEC sp_executesql @SQL`)
|
||||
} else if models.DbCfg.Type == core.MYSQL {
|
||||
indexes, err := sess.QueryString(`SHOW INDEX FROM access_token WHERE KEY_NAME = 'UQE_access_token_sha1'`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(indexes) >= 1 {
|
||||
_, err = sess.Exec("DROP INDEX UQE_access_token_sha1 ON access_token")
|
||||
}
|
||||
} else {
|
||||
_, err = sess.Exec("DROP INDEX UQE_access_token_sha1 ON access_token")
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Drop index failed: %v", err)
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Sync2(new(AccessToken)); err != nil {
|
||||
return fmt.Errorf("Sync2: %v", err)
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// transform all tokens to hashes
|
||||
const batchSize = 100
|
||||
for start := 0; ; start += batchSize {
|
||||
tokens := make([]*AccessToken, 0, batchSize)
|
||||
if err := sess.Limit(batchSize, start).Find(&tokens); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tokens) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for _, token := range tokens {
|
||||
// generate salt
|
||||
salt, err := generate.GetRandomString(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token.TokenSalt = salt
|
||||
token.TokenHash = hashToken(token.Sha1, salt)
|
||||
if len(token.Sha1) < 8 {
|
||||
log.Warn("Unable to transform token %s with name %s belonging to user ID %d, skipping transformation", token.Sha1, token.Name, token.UID)
|
||||
continue
|
||||
}
|
||||
token.TokenLastEight = token.Sha1[len(token.Sha1)-8:]
|
||||
token.Sha1 = "" // ensure to blank out column in case drop column doesn't work
|
||||
|
||||
if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil {
|
||||
return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Commit and begin new transaction for dropping columns
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dropTableColumns(sess, "access_token", "sha1"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
return resyncHashAppTokenWithUniqueHash(x)
|
||||
}
|
||||
|
||||
func resyncHashAppTokenWithUniqueHash(x *xorm.Engine) error {
|
||||
// AccessToken see models/token.go
|
||||
type AccessToken struct {
|
||||
TokenHash string `xorm:"UNIQUE"` // sha256 of token - we will ensure UNIQUE later
|
||||
}
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sess.Sync2(new(AccessToken)); err != nil {
|
||||
return fmt.Errorf("Sync2: %v", err)
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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 migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
func addHTTPMethodToWebhook(x *xorm.Engine) error {
|
||||
type Webhook struct {
|
||||
HTTPMethod string `xorm:"http_method DEFAULT 'POST'"`
|
||||
}
|
||||
|
||||
return x.Sync2(new(Webhook))
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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 migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
func addTeamIncludesAllRepositories(x *xorm.Engine) error {
|
||||
|
||||
type Team struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IncludesAllRepositories bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Team)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := x.Exec("UPDATE `team` SET `includes_all_repositories` = ? WHERE `name`=?",
|
||||
true, "Owners")
|
||||
return err
|
||||
}
|
||||
@@ -107,6 +107,7 @@ func TestRelease_MirrorDelete(t *testing.T) {
|
||||
IsPrivate: false,
|
||||
IsMirror: true,
|
||||
RemoteAddr: repoPath,
|
||||
Wiki: true,
|
||||
}
|
||||
mirror, err := MigrateRepository(user, user, migrationOptions)
|
||||
assert.NoError(t, err)
|
||||
|
||||
+18
-15
@@ -896,6 +896,7 @@ type MigrateRepoOptions struct {
|
||||
IsPrivate bool
|
||||
IsMirror bool
|
||||
RemoteAddr string
|
||||
Wiki bool // include wiki repository
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -917,7 +918,7 @@ func wikiRemoteURL(remote string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// MigrateRepository migrates a existing repository from other project hosting.
|
||||
// MigrateRepository migrates an existing repository from other project hosting.
|
||||
func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
repo, err := CreateRepository(doer, u, CreateRepoOptions{
|
||||
Name: opts.Name,
|
||||
@@ -930,7 +931,6 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
|
||||
}
|
||||
|
||||
repoPath := RepoPath(u.Name, opts.Name)
|
||||
wikiPath := WikiPath(u.Name, opts.Name)
|
||||
|
||||
if u.IsOrganization() {
|
||||
t, err := u.GetOwnerTeam()
|
||||
@@ -956,22 +956,25 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
|
||||
return repo, fmt.Errorf("Clone: %v", err)
|
||||
}
|
||||
|
||||
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
|
||||
if len(wikiRemotePath) > 0 {
|
||||
if err := os.RemoveAll(wikiPath); err != nil {
|
||||
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
||||
}
|
||||
|
||||
if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
}); err != nil {
|
||||
log.Warn("Clone wiki: %v", err)
|
||||
if opts.Wiki {
|
||||
wikiPath := WikiPath(u.Name, opts.Name)
|
||||
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
|
||||
if len(wikiRemotePath) > 0 {
|
||||
if err := os.RemoveAll(wikiPath); err != nil {
|
||||
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
||||
}
|
||||
|
||||
if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
}); err != nil {
|
||||
log.Warn("Clone wiki: %v", err)
|
||||
if err := os.RemoveAll(wikiPath); err != nil {
|
||||
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+82
-6
@@ -6,11 +6,22 @@ package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
// ActivityAuthorData represents statistical git commit count data
|
||||
type ActivityAuthorData struct {
|
||||
Name string `json:"name"`
|
||||
Login string `json:"login"`
|
||||
AvatarLink string `json:"avatar_link"`
|
||||
Commits int64 `json:"commits"`
|
||||
}
|
||||
|
||||
// ActivityStats represets issue and pull request information.
|
||||
type ActivityStats struct {
|
||||
OpenedPRs PullRequestList
|
||||
@@ -24,32 +35,97 @@ type ActivityStats struct {
|
||||
UnresolvedIssues IssueList
|
||||
PublishedReleases []*Release
|
||||
PublishedReleaseAuthorCount int64
|
||||
Code *git.CodeActivityStats
|
||||
}
|
||||
|
||||
// GetActivityStats return stats for repository at given time range
|
||||
func GetActivityStats(repoID int64, timeFrom time.Time, releases, issues, prs bool) (*ActivityStats, error) {
|
||||
stats := &ActivityStats{}
|
||||
func GetActivityStats(repo *Repository, timeFrom time.Time, releases, issues, prs, code bool) (*ActivityStats, error) {
|
||||
stats := &ActivityStats{Code: &git.CodeActivityStats{}}
|
||||
if releases {
|
||||
if err := stats.FillReleases(repoID, timeFrom); err != nil {
|
||||
if err := stats.FillReleases(repo.ID, timeFrom); err != nil {
|
||||
return nil, fmt.Errorf("FillReleases: %v", err)
|
||||
}
|
||||
}
|
||||
if prs {
|
||||
if err := stats.FillPullRequests(repoID, timeFrom); err != nil {
|
||||
if err := stats.FillPullRequests(repo.ID, timeFrom); err != nil {
|
||||
return nil, fmt.Errorf("FillPullRequests: %v", err)
|
||||
}
|
||||
}
|
||||
if issues {
|
||||
if err := stats.FillIssues(repoID, timeFrom); err != nil {
|
||||
if err := stats.FillIssues(repo.ID, timeFrom); err != nil {
|
||||
return nil, fmt.Errorf("FillIssues: %v", err)
|
||||
}
|
||||
}
|
||||
if err := stats.FillUnresolvedIssues(repoID, timeFrom, issues, prs); err != nil {
|
||||
if err := stats.FillUnresolvedIssues(repo.ID, timeFrom, issues, prs); err != nil {
|
||||
return nil, fmt.Errorf("FillUnresolvedIssues: %v", err)
|
||||
}
|
||||
if code {
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
code, err := gitRepo.GetCodeActivityStats(timeFrom, repo.DefaultBranch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FillFromGit: %v", err)
|
||||
}
|
||||
stats.Code = code
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetActivityStatsTopAuthors returns top author stats for git commits for all branches
|
||||
func GetActivityStatsTopAuthors(repo *Repository, timeFrom time.Time, count int) ([]*ActivityAuthorData, error) {
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
code, err := gitRepo.GetCodeActivityStats(timeFrom, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FillFromGit: %v", err)
|
||||
}
|
||||
if code.Authors == nil {
|
||||
return nil, nil
|
||||
}
|
||||
users := make(map[int64]*ActivityAuthorData)
|
||||
for k, v := range code.Authors {
|
||||
if len(k) == 0 {
|
||||
continue
|
||||
}
|
||||
u, err := GetUserByEmail(k)
|
||||
if u == nil || IsErrUserNotExist(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user, ok := users[u.ID]; !ok {
|
||||
users[u.ID] = &ActivityAuthorData{
|
||||
Name: u.DisplayName(),
|
||||
Login: u.LowerName,
|
||||
AvatarLink: u.AvatarLink(),
|
||||
Commits: v,
|
||||
}
|
||||
} else {
|
||||
user.Commits += v
|
||||
}
|
||||
}
|
||||
v := make([]*ActivityAuthorData, 0)
|
||||
for _, u := range users {
|
||||
v = append(v, u)
|
||||
}
|
||||
|
||||
sort.Slice(v[:], func(i, j int) bool {
|
||||
return v[i].Commits < v[j].Commits
|
||||
})
|
||||
|
||||
cnt := count
|
||||
if cnt > len(v) {
|
||||
cnt = len(v)
|
||||
}
|
||||
|
||||
return v[:cnt], nil
|
||||
}
|
||||
|
||||
// ActivePRCount returns total active pull request count
|
||||
func (stats *ActivityStats) ActivePRCount() int {
|
||||
return stats.OpenedPRCount() + stats.MergedPRCount()
|
||||
|
||||
@@ -128,7 +128,7 @@ func (m *Mirror) SaveAddress(addr string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = git.NewCommand("remote", "add", "origin", addr).RunInDir(repoPath)
|
||||
_, err = git.NewCommand("remote", "add", "origin", "--mirror=fetch", addr).RunInDir(repoPath)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
setting.SetCustomPathAndConf("", "")
|
||||
setting.NewContext()
|
||||
}
|
||||
|
||||
|
||||
+37
-14
@@ -1,24 +1,30 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"time"
|
||||
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// AccessToken represents a personal access token.
|
||||
type AccessToken struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Sha1 string `xorm:"UNIQUE VARCHAR(40)"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Token string `xorm:"-"`
|
||||
TokenHash string `xorm:"UNIQUE"` // sha256 of token
|
||||
TokenSalt string
|
||||
TokenLastEight string `xorm:"token_last_eight"`
|
||||
|
||||
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
|
||||
@@ -34,24 +40,41 @@ func (t *AccessToken) AfterLoad() {
|
||||
|
||||
// NewAccessToken creates new access token.
|
||||
func NewAccessToken(t *AccessToken) error {
|
||||
t.Sha1 = base.EncodeSha1(gouuid.NewV4().String())
|
||||
_, err := x.Insert(t)
|
||||
salt, err := generate.GetRandomString(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.TokenSalt = salt
|
||||
t.Token = base.EncodeSha1(gouuid.NewV4().String())
|
||||
t.TokenHash = hashToken(t.Token, t.TokenSalt)
|
||||
t.TokenLastEight = t.Token[len(t.Token)-8:]
|
||||
_, err = x.Insert(t)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAccessTokenBySHA returns access token by given sha1.
|
||||
func GetAccessTokenBySHA(sha string) (*AccessToken, error) {
|
||||
if sha == "" {
|
||||
// GetAccessTokenBySHA returns access token by given token value
|
||||
func GetAccessTokenBySHA(token string) (*AccessToken, error) {
|
||||
if token == "" {
|
||||
return nil, ErrAccessTokenEmpty{}
|
||||
}
|
||||
t := &AccessToken{Sha1: sha}
|
||||
has, err := x.Get(t)
|
||||
if len(token) < 8 {
|
||||
return nil, ErrAccessTokenNotExist{token}
|
||||
}
|
||||
var tokens []AccessToken
|
||||
lastEight := token[len(token)-8:]
|
||||
err := x.Table(&AccessToken{}).Where("token_last_eight = ?", lastEight).Find(&tokens)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrAccessTokenNotExist{sha}
|
||||
} else if len(tokens) == 0 {
|
||||
return nil, ErrAccessTokenNotExist{token}
|
||||
}
|
||||
return t, nil
|
||||
for _, t := range tokens {
|
||||
tempHash := hashToken(token, t.TokenSalt)
|
||||
if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
|
||||
return &t, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrAccessTokenNotExist{token}
|
||||
}
|
||||
|
||||
// ListAccessTokens returns a list of access tokens belongs to given user.
|
||||
|
||||
@@ -29,11 +29,12 @@ func TestNewAccessToken(t *testing.T) {
|
||||
|
||||
func TestGetAccessTokenBySHA(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
token, err := GetAccessTokenBySHA("hash1")
|
||||
token, err := GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), token.UID)
|
||||
assert.Equal(t, "Token A", token.Name)
|
||||
assert.Equal(t, "hash1", token.Sha1)
|
||||
assert.Equal(t, "2b3668e11cb82d3af8c6e4524fc7841297668f5008d1626f0ad3417e9fa39af84c268248b78c481daa7e5dc437784003494f", token.TokenHash)
|
||||
assert.Equal(t, "e4efbf36", token.TokenLastEight)
|
||||
|
||||
token, err = GetAccessTokenBySHA("notahash")
|
||||
assert.Error(t, err)
|
||||
@@ -69,7 +70,7 @@ func TestListAccessTokens(t *testing.T) {
|
||||
|
||||
func TestUpdateAccessToken(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
token, err := GetAccessTokenBySHA("hash2")
|
||||
token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
|
||||
assert.NoError(t, err)
|
||||
token.Name = "Token Z"
|
||||
|
||||
@@ -80,7 +81,7 @@ func TestUpdateAccessToken(t *testing.T) {
|
||||
func TestDeleteAccessTokenByID(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
token, err := GetAccessTokenBySHA("hash2")
|
||||
token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), token.UID)
|
||||
|
||||
|
||||
+1
-5
@@ -129,11 +129,7 @@ func aesDecrypt(key, text []byte) ([]byte, error) {
|
||||
|
||||
// NewTwoFactor creates a new two-factor authentication token.
|
||||
func NewTwoFactor(t *TwoFactor) error {
|
||||
_, err := t.GenerateScratchToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = x.Insert(t)
|
||||
_, err := x.Insert(t)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -661,6 +661,16 @@ func (u *User) DisplayName() string {
|
||||
return u.Name
|
||||
}
|
||||
|
||||
// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,
|
||||
// returns username otherwise.
|
||||
func (u *User) GetDisplayName() string {
|
||||
trimmed := strings.TrimSpace(u.FullName)
|
||||
if len(trimmed) > 0 && setting.UI.DefaultShowFullName {
|
||||
return trimmed
|
||||
}
|
||||
return u.Name
|
||||
}
|
||||
|
||||
func gitSafeName(name string) string {
|
||||
return strings.TrimSpace(strings.NewReplacer("\n", "", "<", "", ">", "").Replace(name))
|
||||
}
|
||||
|
||||
+28
-15
@@ -13,6 +13,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -105,6 +106,7 @@ type Webhook struct {
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
URL string `xorm:"url TEXT"`
|
||||
Signature string `xorm:"TEXT"`
|
||||
HTTPMethod string `xorm:"http_method"`
|
||||
ContentType HookContentType
|
||||
Secret string `xorm:"TEXT"`
|
||||
Events string `xorm:"TEXT"`
|
||||
@@ -553,6 +555,7 @@ type HookTask struct {
|
||||
Signature string `xorm:"TEXT"`
|
||||
api.Payloader `xorm:"-"`
|
||||
PayloadContent string `xorm:"TEXT"`
|
||||
HTTPMethod string `xorm:"http_method"`
|
||||
ContentType HookContentType
|
||||
EventType HookEventType
|
||||
IsSSL bool
|
||||
@@ -707,6 +710,7 @@ func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType,
|
||||
URL: w.URL,
|
||||
Signature: signature,
|
||||
Payloader: payloader,
|
||||
HTTPMethod: w.HTTPMethod,
|
||||
ContentType: w.ContentType,
|
||||
EventType: event,
|
||||
IsSSL: w.IsSSL,
|
||||
@@ -751,9 +755,32 @@ func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payl
|
||||
|
||||
func (t *HookTask) deliver() {
|
||||
t.IsDelivered = true
|
||||
t.RequestInfo = &HookRequest{
|
||||
Headers: map[string]string{},
|
||||
}
|
||||
t.ResponseInfo = &HookResponse{
|
||||
Headers: map[string]string{},
|
||||
}
|
||||
|
||||
timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second
|
||||
req := httplib.Post(t.URL).SetTimeout(timeout, timeout).
|
||||
|
||||
var req *httplib.Request
|
||||
if t.HTTPMethod == http.MethodPost {
|
||||
req = httplib.Post(t.URL)
|
||||
switch t.ContentType {
|
||||
case ContentTypeJSON:
|
||||
req = req.Header("Content-Type", "application/json").Body(t.PayloadContent)
|
||||
case ContentTypeForm:
|
||||
req.Param("payload", t.PayloadContent)
|
||||
}
|
||||
} else if t.HTTPMethod == http.MethodGet {
|
||||
req = httplib.Get(t.URL).Param("payload", t.PayloadContent)
|
||||
} else {
|
||||
t.ResponseInfo.Body = fmt.Sprintf("Invalid http method: %v", t.HTTPMethod)
|
||||
return
|
||||
}
|
||||
|
||||
req = req.SetTimeout(timeout, timeout).
|
||||
Header("X-Gitea-Delivery", t.UUID).
|
||||
Header("X-Gitea-Event", string(t.EventType)).
|
||||
Header("X-Gitea-Signature", t.Signature).
|
||||
@@ -764,25 +791,11 @@ func (t *HookTask) deliver() {
|
||||
HeaderWithSensitiveCase("X-GitHub-Event", string(t.EventType)).
|
||||
SetTLSClientConfig(&tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify})
|
||||
|
||||
switch t.ContentType {
|
||||
case ContentTypeJSON:
|
||||
req = req.Header("Content-Type", "application/json").Body(t.PayloadContent)
|
||||
case ContentTypeForm:
|
||||
req.Param("payload", t.PayloadContent)
|
||||
}
|
||||
|
||||
// Record delivery information.
|
||||
t.RequestInfo = &HookRequest{
|
||||
Headers: map[string]string{},
|
||||
}
|
||||
for k, vals := range req.Headers() {
|
||||
t.RequestInfo.Headers[k] = strings.Join(vals, ",")
|
||||
}
|
||||
|
||||
t.ResponseInfo = &HookResponse{
|
||||
Headers: map[string]string{},
|
||||
}
|
||||
|
||||
defer func() {
|
||||
t.Delivered = time.Now().UnixNano()
|
||||
if t.IsSucceed {
|
||||
|
||||
Reference in New Issue
Block a user