aboutsummaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/issues/pull.go4
-rw-r--r--models/issues/review_list.go75
-rw-r--r--models/issues/review_test.go3
-rw-r--r--models/migrations/v1_21/v276.go2
-rw-r--r--models/repo/repo.go126
-rw-r--r--models/repo/repo_test.go156
-rw-r--r--models/repo/update.go6
-rw-r--r--models/repo/wiki.go5
-rw-r--r--models/repo/wiki_test.go3
-rw-r--r--models/unittest/testdb.go1
10 files changed, 260 insertions, 121 deletions
diff --git a/models/issues/pull.go b/models/issues/pull.go
index 8c43eb19dd..786b2aa130 100644
--- a/models/issues/pull.go
+++ b/models/issues/pull.go
@@ -301,7 +301,7 @@ func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error {
return nil
}
- reviews, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
+ reviews, _, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
if err != nil {
return err
}
@@ -320,7 +320,7 @@ func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error {
// LoadRequestedReviewersTeams loads the requested reviewers teams.
func (pr *PullRequest) LoadRequestedReviewersTeams(ctx context.Context) error {
- reviews, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
+ reviews, _, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
if err != nil {
return err
}
diff --git a/models/issues/review_list.go b/models/issues/review_list.go
index bc7d7ec0f0..928f24fb2d 100644
--- a/models/issues/review_list.go
+++ b/models/issues/review_list.go
@@ -5,6 +5,8 @@ package issues
import (
"context"
+ "slices"
+ "sort"
"code.gitea.io/gitea/models/db"
organization_model "code.gitea.io/gitea/models/organization"
@@ -153,43 +155,60 @@ func CountReviews(ctx context.Context, opts FindReviewOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toCond()).Count(&Review{})
}
-// GetReviewersFromOriginalAuthorsByIssueID gets the latest review of each original authors for a pull request
-func GetReviewersFromOriginalAuthorsByIssueID(ctx context.Context, issueID int64) (ReviewList, error) {
+// GetReviewsByIssueID gets the latest review of each reviewer for a pull request
+// The first returned parameter is the latest review of each individual reviewer or team
+// The second returned parameter is the latest review of each original author which is migrated from other systems
+// The reviews are sorted by updated time
+func GetReviewsByIssueID(ctx context.Context, issueID int64) (latestReviews, migratedOriginalReviews ReviewList, err error) {
reviews := make([]*Review, 0, 10)
- // Get latest review of each reviewer, sorted in order they were made
- if err := db.GetEngine(ctx).SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id = 0 AND type in (?, ?, ?) AND original_author_id <> 0 GROUP BY issue_id, original_author_id) ORDER BY review.updated_unix ASC",
- issueID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest).
- Find(&reviews); err != nil {
- return nil, err
+ // Get all reviews for the issue id
+ if err := db.GetEngine(ctx).Where("issue_id=?", issueID).OrderBy("updated_unix ASC").Find(&reviews); err != nil {
+ return nil, nil, err
}
- return reviews, nil
-}
-
-// GetReviewsByIssueID gets the latest review of each reviewer for a pull request
-func GetReviewsByIssueID(ctx context.Context, issueID int64) (ReviewList, error) {
- reviews := make([]*Review, 0, 10)
-
- sess := db.GetEngine(ctx)
+ // filter them in memory to get the latest review of each reviewer
+ // Since the reviews should not be too many for one issue, less than 100 commonly, it's acceptable to do this in memory
+ // And since there are too less indexes in review table, it will be very slow to filter in the database
+ reviewersMap := make(map[int64][]*Review) // key is reviewer id
+ originalReviewersMap := make(map[int64][]*Review) // key is original author id
+ reviewTeamsMap := make(map[int64][]*Review) // key is reviewer team id
+ countedReivewTypes := []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest}
+ for _, review := range reviews {
+ if review.ReviewerTeamID == 0 && slices.Contains(countedReivewTypes, review.Type) && !review.Dismissed {
+ if review.OriginalAuthorID != 0 {
+ originalReviewersMap[review.OriginalAuthorID] = append(originalReviewersMap[review.OriginalAuthorID], review)
+ } else {
+ reviewersMap[review.ReviewerID] = append(reviewersMap[review.ReviewerID], review)
+ }
+ } else if review.ReviewerTeamID != 0 && review.OriginalAuthorID == 0 {
+ reviewTeamsMap[review.ReviewerTeamID] = append(reviewTeamsMap[review.ReviewerTeamID], review)
+ }
+ }
- // Get latest review of each reviewer, sorted in order they were made
- if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id = 0 AND type in (?, ?, ?) AND dismissed = ? AND original_author_id = 0 GROUP BY issue_id, reviewer_id) ORDER BY review.updated_unix ASC",
- issueID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, false).
- Find(&reviews); err != nil {
- return nil, err
+ individualReviews := make([]*Review, 0, 10)
+ for _, reviews := range reviewersMap {
+ individualReviews = append(individualReviews, reviews[len(reviews)-1])
}
+ sort.Slice(individualReviews, func(i, j int) bool {
+ return individualReviews[i].UpdatedUnix < individualReviews[j].UpdatedUnix
+ })
- teamReviewRequests := make([]*Review, 0, 5)
- if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id <> 0 AND original_author_id = 0 GROUP BY issue_id, reviewer_team_id) ORDER BY review.updated_unix ASC",
- issueID).
- Find(&teamReviewRequests); err != nil {
- return nil, err
+ originalReviews := make([]*Review, 0, 10)
+ for _, reviews := range originalReviewersMap {
+ originalReviews = append(originalReviews, reviews[len(reviews)-1])
}
+ sort.Slice(originalReviews, func(i, j int) bool {
+ return originalReviews[i].UpdatedUnix < originalReviews[j].UpdatedUnix
+ })
- if len(teamReviewRequests) > 0 {
- reviews = append(reviews, teamReviewRequests...)
+ teamReviewRequests := make([]*Review, 0, 5)
+ for _, reviews := range reviewTeamsMap {
+ teamReviewRequests = append(teamReviewRequests, reviews[len(reviews)-1])
}
+ sort.Slice(teamReviewRequests, func(i, j int) bool {
+ return teamReviewRequests[i].UpdatedUnix < teamReviewRequests[j].UpdatedUnix
+ })
- return reviews, nil
+ return append(individualReviews, teamReviewRequests...), originalReviews, nil
}
diff --git a/models/issues/review_test.go b/models/issues/review_test.go
index 50330e3ff2..2588b8ba41 100644
--- a/models/issues/review_test.go
+++ b/models/issues/review_test.go
@@ -162,8 +162,9 @@ func TestGetReviewersByIssueID(t *testing.T) {
},
)
- allReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
+ allReviews, migratedReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
assert.NoError(t, err)
+ assert.Empty(t, migratedReviews)
for _, review := range allReviews {
assert.NoError(t, review.LoadReviewer(db.DefaultContext))
}
diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go
index 15177bf040..9d22c9052e 100644
--- a/models/migrations/v1_21/v276.go
+++ b/models/migrations/v1_21/v276.go
@@ -172,7 +172,7 @@ func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)
}
- u, err := giturl.Parse(remoteURL)
+ u, err := giturl.ParseGitURL(remoteURL)
if err != nil {
return "", err
}
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 5ef4d470c3..4432fef810 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -20,6 +20,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
+ giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
@@ -279,6 +280,8 @@ func (repo *Repository) IsBroken() bool {
}
// MarkAsBrokenEmpty marks the repo as broken and empty
+// FIXME: the status "broken" and "is_empty" were abused,
+// The code always set them together, no way to distinguish whether a repo is really "empty" or "broken"
func (repo *Repository) MarkAsBrokenEmpty() {
repo.Status = RepositoryBroken
repo.IsEmpty = true
@@ -635,14 +638,26 @@ type CloneLink struct {
}
// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
-func ComposeHTTPSCloneURL(owner, repo string) string {
- return fmt.Sprintf("%s%s/%s.git", setting.AppURL, url.PathEscape(owner), url.PathEscape(repo))
+func ComposeHTTPSCloneURL(ctx context.Context, owner, repo string) string {
+ return fmt.Sprintf("%s%s/%s.git", httplib.GuessCurrentAppURL(ctx), url.PathEscape(owner), url.PathEscape(repo))
}
-func ComposeSSHCloneURL(ownerName, repoName string) string {
+func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) string {
sshUser := setting.SSH.User
sshDomain := setting.SSH.Domain
+ if sshUser == "(DOER_USERNAME)" {
+ // Some users use SSH reverse-proxy and need to use the current signed-in username as the SSH user
+ // to make the SSH reverse-proxy could prepare the user's public keys ahead.
+ // For most cases we have the correct "doer", then use it as the SSH user.
+ // If we can't get the doer, then use the built-in SSH user.
+ if doer != nil {
+ sshUser = doer.Name
+ } else {
+ sshUser = setting.SSH.BuiltinServerUser
+ }
+ }
+
// non-standard port, it must use full URI
if setting.SSH.Port != 22 {
sshHost := net.JoinHostPort(sshDomain, strconv.Itoa(setting.SSH.Port))
@@ -660,21 +675,20 @@ func ComposeSSHCloneURL(ownerName, repoName string) string {
return fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), url.PathEscape(repoName))
}
-func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
- repoName := repo.Name
- if isWiki {
- repoName += ".wiki"
- }
-
+func (repo *Repository) cloneLink(ctx context.Context, doer *user_model.User, repoPathName string) *CloneLink {
cl := new(CloneLink)
- cl.SSH = ComposeSSHCloneURL(repo.OwnerName, repoName)
- cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
+ cl.SSH = ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName)
+ cl.HTTPS = ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName)
return cl
}
// CloneLink returns clone URLs of repository.
-func (repo *Repository) CloneLink() (cl *CloneLink) {
- return repo.cloneLink(false)
+func (repo *Repository) CloneLink(ctx context.Context, doer *user_model.User) (cl *CloneLink) {
+ return repo.cloneLink(ctx, doer, repo.Name)
+}
+
+func (repo *Repository) CloneLinkGeneral(ctx context.Context) (cl *CloneLink) {
+ return repo.cloneLink(ctx, nil /* no doer, use a general git user */, repo.Name)
}
// GetOriginalURLHostname returns the hostname of a URL or the URL
@@ -770,47 +784,75 @@ func GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*Repo
return &repo, err
}
-// getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url
-func getRepositoryURLPathSegments(repoURL string) []string {
- if strings.HasPrefix(repoURL, setting.AppURL) {
- return strings.Split(strings.TrimPrefix(repoURL, setting.AppURL), "/")
+func parseRepositoryURL(ctx context.Context, repoURL string) (ret struct {
+ OwnerName, RepoName, RemainingPath string
+},
+) {
+ // possible urls for git:
+ // https://my.domain/sub-path/<owner>/<repo>[.git]
+ // git+ssh://user@my.domain/<owner>/<repo>[.git]
+ // ssh://user@my.domain/<owner>/<repo>[.git]
+ // user@my.domain:<owner>/<repo>[.git]
+
+ fillPathParts := func(s string) {
+ s = strings.TrimPrefix(s, "/")
+ fields := strings.SplitN(s, "/", 3)
+ if len(fields) >= 2 {
+ ret.OwnerName = fields[0]
+ ret.RepoName = strings.TrimSuffix(fields[1], ".git")
+ if len(fields) == 3 {
+ ret.RemainingPath = "/" + fields[2]
+ }
+ }
}
- sshURLVariants := [4]string{
- setting.SSH.Domain + ":",
- setting.SSH.User + "@" + setting.SSH.Domain + ":",
- "git+ssh://" + setting.SSH.Domain + "/",
- "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/",
+ parsed, err := giturl.ParseGitURL(repoURL)
+ if err != nil {
+ return ret
}
-
- for _, sshURL := range sshURLVariants {
- if strings.HasPrefix(repoURL, sshURL) {
- return strings.Split(strings.TrimPrefix(repoURL, sshURL), "/")
+ if parsed.URL.Scheme == "http" || parsed.URL.Scheme == "https" {
+ if !httplib.IsCurrentGiteaSiteURL(ctx, repoURL) {
+ return ret
+ }
+ fillPathParts(strings.TrimPrefix(parsed.URL.Path, setting.AppSubURL))
+ } else if parsed.URL.Scheme == "ssh" || parsed.URL.Scheme == "git+ssh" {
+ domainSSH := setting.SSH.Domain
+ domainCur := httplib.GuessCurrentHostDomain(ctx)
+ urlDomain, _, _ := net.SplitHostPort(parsed.URL.Host)
+ urlDomain = util.IfZero(urlDomain, parsed.URL.Host)
+ if urlDomain == "" {
+ return ret
+ }
+ // check whether URL domain is the App domain
+ domainMatches := domainSSH == urlDomain
+ // check whether URL domain is current domain from context
+ domainMatches = domainMatches || (domainCur != "" && domainCur == urlDomain)
+ if domainMatches {
+ fillPathParts(parsed.URL.Path)
}
}
-
- return nil
+ return ret
}
// GetRepositoryByURL returns the repository by given url
func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error) {
- // possible urls for git:
- // https://my.domain/sub-path/<owner>/<repo>.git
- // https://my.domain/sub-path/<owner>/<repo>
- // git+ssh://user@my.domain/<owner>/<repo>.git
- // git+ssh://user@my.domain/<owner>/<repo>
- // user@my.domain:<owner>/<repo>.git
- // user@my.domain:<owner>/<repo>
-
- pathSegments := getRepositoryURLPathSegments(repoURL)
-
- if len(pathSegments) != 2 {
+ ret := parseRepositoryURL(ctx, repoURL)
+ if ret.OwnerName == "" {
return nil, fmt.Errorf("unknown or malformed repository URL")
}
+ return GetRepositoryByOwnerAndName(ctx, ret.OwnerName, ret.RepoName)
+}
- ownerName := pathSegments[0]
- repoName := strings.TrimSuffix(pathSegments[1], ".git")
- return GetRepositoryByOwnerAndName(ctx, ownerName, repoName)
+// GetRepositoryByURLRelax also accepts an SSH clone URL without user part
+func GetRepositoryByURLRelax(ctx context.Context, repoURL string) (*Repository, error) {
+ if !strings.Contains(repoURL, "://") && !strings.Contains(repoURL, "@") {
+ // convert "example.com:owner/repo" to "@example.com:owner/repo"
+ p1, p2, p3 := strings.Index(repoURL, "."), strings.Index(repoURL, ":"), strings.Index(repoURL, "/")
+ if 0 < p1 && p1 < p2 && p2 < p3 {
+ repoURL = "@" + repoURL
+ }
+ }
+ return GetRepositoryByURL(ctx, repoURL)
}
// GetRepositoryByID returns the repository by given id if exists.
diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go
index 001f8ecd84..ffae642285 100644
--- a/models/repo/repo_test.go
+++ b/models/repo/repo_test.go
@@ -4,18 +4,23 @@
package repo
import (
+ "context"
+ "net/http"
+ "net/url"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
var (
@@ -127,65 +132,121 @@ func TestMetas(t *testing.T) {
assert.Equal(t, ",owners,team1,", metas["teams"])
}
+func TestParseRepositoryURLPathSegments(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000")()
+
+ ctxURL, _ := url.Parse("https://gitea")
+ ctxReq := &http.Request{URL: ctxURL, Header: http.Header{}}
+ ctxReq.Host = ctxURL.Host
+ ctxReq.Header.Add("X-Forwarded-Proto", ctxURL.Scheme)
+ ctx := context.WithValue(context.Background(), httplib.RequestContextKey, ctxReq)
+ cases := []struct {
+ input string
+ ownerName, repoName, remaining string
+ }{
+ {input: "/user/repo"},
+
+ {input: "https://localhost:3000/user/repo", ownerName: "user", repoName: "repo"},
+ {input: "https://external:3000/user/repo"},
+
+ {input: "https://localhost:3000/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"},
+
+ {input: "https://gitea/user/repo", ownerName: "user", repoName: "repo"},
+ {input: "https://gitea:3333/user/repo"},
+
+ {input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"},
+ {input: "ssh://external:2222/user/repo"},
+
+ {input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "git+ssh://user@external/user/repo.git"},
+
+ {input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "root@gitea:user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "root@external:user/repo.git"},
+ }
+
+ for _, c := range cases {
+ t.Run(c.input, func(t *testing.T) {
+ ret := parseRepositoryURL(ctx, c.input)
+ assert.Equal(t, c.ownerName, ret.OwnerName)
+ assert.Equal(t, c.repoName, ret.RepoName)
+ assert.Equal(t, c.remaining, ret.RemainingPath)
+ })
+ }
+
+ t.Run("WithSubpath", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000/subpath")()
+ defer test.MockVariableValue(&setting.AppSubURL, "/subpath")()
+ cases = []struct {
+ input string
+ ownerName, repoName, remaining string
+ }{
+ {input: "https://localhost:3000/user/repo"},
+ {input: "https://localhost:3000/subpath/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"},
+
+ {input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"},
+ {input: "ssh://external:2222/user/repo"},
+
+ {input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "git+ssh://user@external/user/repo.git"},
+
+ {input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "root@external:user/repo.git"},
+ }
+
+ for _, c := range cases {
+ t.Run(c.input, func(t *testing.T) {
+ ret := parseRepositoryURL(ctx, c.input)
+ assert.Equal(t, c.ownerName, ret.OwnerName)
+ assert.Equal(t, c.repoName, ret.RepoName)
+ assert.Equal(t, c.remaining, ret.RemainingPath)
+ })
+ }
+ })
+}
+
func TestGetRepositoryByURL(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
t.Run("InvalidPath", func(t *testing.T) {
repo, err := GetRepositoryByURL(db.DefaultContext, "something")
-
assert.Nil(t, repo)
assert.Error(t, err)
})
- t.Run("ValidHttpURL", func(t *testing.T) {
- test := func(t *testing.T, url string) {
- repo, err := GetRepositoryByURL(db.DefaultContext, url)
-
- assert.NotNil(t, repo)
- assert.NoError(t, err)
-
- assert.Equal(t, int64(2), repo.ID)
- assert.Equal(t, int64(2), repo.OwnerID)
- }
+ testRepo2 := func(t *testing.T, url string) {
+ repo, err := GetRepositoryByURL(db.DefaultContext, url)
+ require.NoError(t, err)
+ assert.EqualValues(t, 2, repo.ID)
+ assert.EqualValues(t, 2, repo.OwnerID)
+ }
- test(t, "https://try.gitea.io/user2/repo2")
- test(t, "https://try.gitea.io/user2/repo2.git")
+ t.Run("ValidHttpURL", func(t *testing.T) {
+ testRepo2(t, "https://try.gitea.io/user2/repo2")
+ testRepo2(t, "https://try.gitea.io/user2/repo2.git")
})
t.Run("ValidGitSshURL", func(t *testing.T) {
- test := func(t *testing.T, url string) {
- repo, err := GetRepositoryByURL(db.DefaultContext, url)
-
- assert.NotNil(t, repo)
- assert.NoError(t, err)
+ testRepo2(t, "git+ssh://sshuser@try.gitea.io/user2/repo2")
+ testRepo2(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git")
- assert.Equal(t, int64(2), repo.ID)
- assert.Equal(t, int64(2), repo.OwnerID)
- }
-
- test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2")
- test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git")
-
- test(t, "git+ssh://try.gitea.io/user2/repo2")
- test(t, "git+ssh://try.gitea.io/user2/repo2.git")
+ testRepo2(t, "git+ssh://try.gitea.io/user2/repo2")
+ testRepo2(t, "git+ssh://try.gitea.io/user2/repo2.git")
})
t.Run("ValidImplicitSshURL", func(t *testing.T) {
- test := func(t *testing.T, url string) {
- repo, err := GetRepositoryByURL(db.DefaultContext, url)
-
- assert.NotNil(t, repo)
- assert.NoError(t, err)
+ testRepo2(t, "sshuser@try.gitea.io:user2/repo2")
+ testRepo2(t, "sshuser@try.gitea.io:user2/repo2.git")
+ testRelax := func(t *testing.T, url string) {
+ repo, err := GetRepositoryByURLRelax(db.DefaultContext, url)
+ require.NoError(t, err)
assert.Equal(t, int64(2), repo.ID)
assert.Equal(t, int64(2), repo.OwnerID)
}
-
- test(t, "sshuser@try.gitea.io:user2/repo2")
- test(t, "sshuser@try.gitea.io:user2/repo2.git")
-
- test(t, "try.gitea.io:user2/repo2")
- test(t, "try.gitea.io:user2/repo2.git")
+ // TODO: it doesn't seem to be common git ssh URL, should we really support this?
+ testRelax(t, "try.gitea.io:user2/repo2")
+ testRelax(t, "try.gitea.io:user2/repo2.git")
})
}
@@ -199,23 +260,30 @@ func TestComposeSSHCloneURL(t *testing.T) {
setting.SSH.Domain = "domain"
setting.SSH.Port = 22
setting.Repository.UseCompatSSHURI = false
- assert.Equal(t, "git@domain:user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "git@domain:user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
setting.Repository.UseCompatSSHURI = true
- assert.Equal(t, "ssh://git@domain/user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "ssh://git@domain/user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
// test SSH_DOMAIN while use non-standard SSH port
setting.SSH.Port = 123
setting.Repository.UseCompatSSHURI = false
- assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
setting.Repository.UseCompatSSHURI = true
- assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
// test IPv6 SSH_DOMAIN
setting.Repository.UseCompatSSHURI = false
setting.SSH.Domain = "::1"
setting.SSH.Port = 22
- assert.Equal(t, "git@[::1]:user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "git@[::1]:user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
+ setting.SSH.Port = 123
+ assert.Equal(t, "ssh://git@[::1]:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
+
+ setting.SSH.User = "(DOER_USERNAME)"
+ setting.SSH.Domain = "domain"
+ setting.SSH.Port = 22
+ assert.Equal(t, "doer@domain:user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
setting.SSH.Port = 123
- assert.Equal(t, "ssh://git@[::1]:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "ssh://doer@domain:123/user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
}
func TestIsUsableRepoName(t *testing.T) {
diff --git a/models/repo/update.go b/models/repo/update.go
index e7ca224028..fce357a1ac 100644
--- a/models/repo/update.go
+++ b/models/repo/update.go
@@ -46,6 +46,12 @@ func UpdateRepositoryCols(ctx context.Context, repo *Repository, cols ...string)
return err
}
+// UpdateRepositoryColsNoAutoTime updates repository's columns and but applies time change automatically
+func UpdateRepositoryColsNoAutoTime(ctx context.Context, repo *Repository, cols ...string) error {
+ _, err := db.GetEngine(ctx).ID(repo.ID).Cols(cols...).NoAutoTime().Update(repo)
+ return err
+}
+
// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error.
type ErrReachLimitOfRepo struct {
Limit int
diff --git a/models/repo/wiki.go b/models/repo/wiki.go
index b378666a20..4239a815b2 100644
--- a/models/repo/wiki.go
+++ b/models/repo/wiki.go
@@ -5,6 +5,7 @@
package repo
import (
+ "context"
"fmt"
"path/filepath"
"strings"
@@ -72,8 +73,8 @@ func (err ErrWikiInvalidFileName) Unwrap() error {
}
// WikiCloneLink returns clone URLs of repository wiki.
-func (repo *Repository) WikiCloneLink() *CloneLink {
- return repo.cloneLink(true)
+func (repo *Repository) WikiCloneLink(ctx context.Context, doer *user_model.User) *CloneLink {
+ return repo.cloneLink(ctx, doer, repo.Name+".wiki")
}
// WikiPath returns wiki data path by given user and repository name.
diff --git a/models/repo/wiki_test.go b/models/repo/wiki_test.go
index 629986f741..0157b7735d 100644
--- a/models/repo/wiki_test.go
+++ b/models/repo/wiki_test.go
@@ -4,6 +4,7 @@
package repo_test
import (
+ "context"
"path/filepath"
"testing"
@@ -18,7 +19,7 @@ func TestRepository_WikiCloneLink(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- cloneLink := repo.WikiCloneLink()
+ cloneLink := repo.WikiCloneLink(context.Background(), nil)
assert.Equal(t, "ssh://sshuser@try.gitea.io:3000/user2/repo1.wiki.git", cloneLink.SSH)
assert.Equal(t, "https://try.gitea.io/user2/repo1.wiki.git", cloneLink.HTTPS)
}
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 0dcff166cc..7a9ca9698d 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -84,6 +84,7 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
setting.IsInTesting = true
setting.AppURL = "https://try.gitea.io/"
+ setting.Domain = "try.gitea.io"
setting.RunUser = "runuser"
setting.SSH.User = "sshuser"
setting.SSH.BuiltinServerUser = "builtinuser"