mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-10 15:32:55 +09:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83d04df365 | ||
|
|
9614bb1b9f | ||
|
|
b2c3a7d79f | ||
|
|
76bbcf1387 | ||
|
|
1bcbc02045 | ||
|
|
66ceee08dc | ||
|
|
ffff835b73 | ||
|
|
63c54f7e1f | ||
|
|
f9845454cf | ||
|
|
1e1211c194 | ||
|
|
10e549df7d | ||
|
|
519f69eb41 | ||
|
|
d4501ece55 | ||
|
|
c1152b15fe | ||
|
|
cb31f88383 | ||
|
|
6cb9ce1367 | ||
|
|
d93d5d7906 | ||
|
|
5c3863c319 | ||
|
|
80b50afe1f | ||
|
|
20a28b785a | ||
|
|
d330b2f52b |
24
CHANGELOG.md
24
CHANGELOG.md
@@ -4,6 +4,30 @@ 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.9.5](https://github.com/go-gitea/gitea/releases/tag/v1.9.5) - 2019-10-30
|
||||||
|
* BREAKING
|
||||||
|
* Hide some user information via API if user doesn't have enough permission (#8655) (#8658)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix milestone close timestamp (#8728) (#8731)
|
||||||
|
* Fix deadline on update issue or PR via API (#8699)
|
||||||
|
* Fix 'New Issue Missing Milestone Comment' (#8678) (#8682)
|
||||||
|
* Fix 500 when getting user as unauthenticated user (#8653) (#8662)
|
||||||
|
* Use AppSubUrl for more redirections (#8647) (#8652)
|
||||||
|
* Add SubURL to redirect path (#8632) (#8634) (#8640)
|
||||||
|
* Fix #8582 by handling empty repos (#8587) (#8593)
|
||||||
|
* Fix bug on pull requests when transfer head repository (#8571)
|
||||||
|
* Add missed close in ServeBlobLFS (#8527) (#8543)
|
||||||
|
* Return false if provided branch name is empty for IsBranchExist (#8485) (#8492)
|
||||||
|
* Create .ssh dir as necessary (#8369) (#8486) (#8489)
|
||||||
|
* Restore functionality for early gits (#7775) (#8476)
|
||||||
|
* Add check for empty set when dropping indexes during migration (#8475)
|
||||||
|
* Ensure Request Body Readers are closed in LFS server (#8454) (#8459)
|
||||||
|
* Ensure that LFS files are relative to the LFS content path (#8455) (#8458)
|
||||||
|
* SECURITY
|
||||||
|
* Ignore mentions for users with no access (#8395) (#8484)
|
||||||
|
* TESTING
|
||||||
|
* Update heatmap fixtures to restore tests (#8615) (#8617)
|
||||||
|
|
||||||
## [1.9.4](https://github.com/go-gitea/gitea/releases/tag/v1.9.4) - 2019-10-08
|
## [1.9.4](https://github.com/go-gitea/gitea/releases/tag/v1.9.4) - 2019-10-08
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
* Highlight issue references (#8101) (#8404)
|
* Highlight issue references (#8101) (#8404)
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ func TestAPITeamUser(t *testing.T) {
|
|||||||
var user2 *api.User
|
var user2 *api.User
|
||||||
DecodeJSON(t, resp, &user2)
|
DecodeJSON(t, resp, &user2)
|
||||||
user2.Created = user2.Created.In(time.Local)
|
user2.Created = user2.Created.In(time.Local)
|
||||||
user2.LastLogin = user2.LastLogin.In(time.Local)
|
|
||||||
user := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
|
user := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
|
||||||
|
|
||||||
assert.Equal(t, convert.ToUser(user, true, false), user2)
|
assert.Equal(t, convert.ToUser(user, true, false), user2)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestUserHeatmap(t *testing.T) {
|
|||||||
var heatmap []*models.UserHeatmapData
|
var heatmap []*models.UserHeatmapData
|
||||||
DecodeJSON(t, resp, &heatmap)
|
DecodeJSON(t, resp, &heatmap)
|
||||||
var dummyheatmap []*models.UserHeatmapData
|
var dummyheatmap []*models.UserHeatmapData
|
||||||
dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1540080000, Contributions: 1})
|
dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1571616000, Contributions: 1})
|
||||||
|
|
||||||
assert.Equal(t, dummyheatmap, heatmap)
|
assert.Equal(t, dummyheatmap, heatmap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -36,7 +38,12 @@ func withKeyFile(t *testing.T, keyname string, callback func(string)) {
|
|||||||
err = ssh.GenKeyPair(keyFile)
|
err = ssh.GenKeyPair(keyFile)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(path.Join(tmpDir, "ssh"), []byte("#!/bin/bash\n"+
|
||||||
|
"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\" \"$@\""), 0700)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
//Setup ssh wrapper
|
//Setup ssh wrapper
|
||||||
|
os.Setenv("GIT_SSH", path.Join(tmpDir, "ssh"))
|
||||||
os.Setenv("GIT_SSH_COMMAND",
|
os.Setenv("GIT_SSH_COMMAND",
|
||||||
"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\"")
|
"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\"")
|
||||||
os.Setenv("GIT_SSH_VARIANT", "ssh")
|
os.Setenv("GIT_SSH_VARIANT", "ssh")
|
||||||
@@ -53,6 +60,24 @@ func createSSHUrl(gitPath string, u *url.URL) *url.URL {
|
|||||||
return &u2
|
return &u2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func allowLFSFilters() []string {
|
||||||
|
// Now here we should explicitly allow lfs filters to run
|
||||||
|
globalArgs := git.GlobalCommandArgs
|
||||||
|
filteredLFSGlobalArgs := make([]string, len(git.GlobalCommandArgs))
|
||||||
|
j := 0
|
||||||
|
for _, arg := range git.GlobalCommandArgs {
|
||||||
|
if strings.Contains(arg, "lfs") {
|
||||||
|
j--
|
||||||
|
} else {
|
||||||
|
filteredLFSGlobalArgs[j] = arg
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filteredLFSGlobalArgs = filteredLFSGlobalArgs[:j]
|
||||||
|
git.GlobalCommandArgs = filteredLFSGlobalArgs
|
||||||
|
return globalArgs
|
||||||
|
}
|
||||||
|
|
||||||
func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
|
func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
|
||||||
prepareTestEnv(t, 1)
|
prepareTestEnv(t, 1)
|
||||||
s := http.Server{
|
s := http.Server{
|
||||||
@@ -78,7 +103,9 @@ func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
|
|||||||
|
|
||||||
func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
|
func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
|
oldGlobals := allowLFSFilters()
|
||||||
assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
|
assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
|
||||||
|
git.GlobalCommandArgs = oldGlobals
|
||||||
assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
|
assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +166,9 @@ func doGitCreateBranch(dstPath, branch string) func(*testing.T) {
|
|||||||
|
|
||||||
func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
|
func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
|
oldGlobals := allowLFSFilters()
|
||||||
_, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath)
|
_, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath)
|
||||||
|
git.GlobalCommandArgs = oldGlobals
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +182,9 @@ func doGitMerge(dstPath string, args ...string) func(*testing.T) {
|
|||||||
|
|
||||||
func doGitPull(dstPath string, args ...string) func(*testing.T) {
|
func doGitPull(dstPath string, args ...string) func(*testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
|
oldGlobals := allowLFSFilters()
|
||||||
_, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath)
|
_, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath)
|
||||||
|
git.GlobalCommandArgs = oldGlobals
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -135,6 +136,11 @@ func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string
|
|||||||
func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
|
func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
|
||||||
t.Run("LFS", func(t *testing.T) {
|
t.Run("LFS", func(t *testing.T) {
|
||||||
PrintCurrentTest(t)
|
PrintCurrentTest(t)
|
||||||
|
setting.CheckLFSVersion()
|
||||||
|
if !setting.LFS.StartServer {
|
||||||
|
t.Skip()
|
||||||
|
return
|
||||||
|
}
|
||||||
prefix := "lfs-data-file-"
|
prefix := "lfs-data-file-"
|
||||||
_, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
|
_, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -142,6 +148,21 @@ func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS strin
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = git.AddChanges(dstPath, false, ".gitattributes")
|
err = git.AddChanges(dstPath, false, ".gitattributes")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
oldGlobals := allowLFSFilters()
|
||||||
|
err = git.CommitChanges(dstPath, git.CommitChangesOptions{
|
||||||
|
Committer: &git.Signature{
|
||||||
|
Email: "user2@example.com",
|
||||||
|
Name: "User Two",
|
||||||
|
When: time.Now(),
|
||||||
|
},
|
||||||
|
Author: &git.Signature{
|
||||||
|
Email: "user2@example.com",
|
||||||
|
Name: "User Two",
|
||||||
|
When: time.Now(),
|
||||||
|
},
|
||||||
|
Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
|
||||||
|
})
|
||||||
|
git.GlobalCommandArgs = oldGlobals
|
||||||
|
|
||||||
littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix)
|
littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix)
|
||||||
|
|
||||||
@@ -185,20 +206,25 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s
|
|||||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
assert.Equal(t, littleSize, resp.Body.Len())
|
assert.Equal(t, littleSize, resp.Body.Len())
|
||||||
|
|
||||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
|
setting.CheckLFSVersion()
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
if setting.LFS.StartServer {
|
||||||
assert.NotEqual(t, littleSize, resp.Body.Len())
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
|
||||||
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
assert.NotEqual(t, littleSize, resp.Body.Len())
|
||||||
|
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
if !testing.Short() {
|
if !testing.Short() {
|
||||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
assert.Equal(t, bigSize, resp.Body.Len())
|
assert.Equal(t, bigSize, resp.Body.Len())
|
||||||
|
|
||||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
|
if setting.LFS.StartServer {
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
|
||||||
assert.NotEqual(t, bigSize, resp.Body.Len())
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
assert.NotEqual(t, bigSize, resp.Body.Len())
|
||||||
|
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -217,18 +243,23 @@ func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS
|
|||||||
resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||||
assert.Equal(t, littleSize, resp.Length)
|
assert.Equal(t, littleSize, resp.Length)
|
||||||
|
|
||||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
|
setting.CheckLFSVersion()
|
||||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
if setting.LFS.StartServer {
|
||||||
assert.Equal(t, littleSize, resp.Length)
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
|
||||||
|
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||||
|
assert.Equal(t, littleSize, resp.Length)
|
||||||
|
}
|
||||||
|
|
||||||
if !testing.Short() {
|
if !testing.Short() {
|
||||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
|
||||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||||
assert.Equal(t, bigSize, resp.Length)
|
assert.Equal(t, bigSize, resp.Length)
|
||||||
|
|
||||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
|
if setting.LFS.StartServer {
|
||||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
|
||||||
assert.Equal(t, bigSize, resp.Length)
|
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||||
|
assert.Equal(t, bigSize, resp.Length)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -274,6 +305,8 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Commit
|
//Commit
|
||||||
|
// Now here we should explicitly allow lfs filters to run
|
||||||
|
oldGlobals := allowLFSFilters()
|
||||||
err = git.AddChanges(repoPath, false, filepath.Base(tmpFile.Name()))
|
err = git.AddChanges(repoPath, false, filepath.Base(tmpFile.Name()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -291,6 +324,7 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
|
|||||||
},
|
},
|
||||||
Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
|
Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
|
||||||
})
|
})
|
||||||
|
git.GlobalCommandArgs = oldGlobals
|
||||||
return filepath.Base(tmpFile.Name()), err
|
return filepath.Base(tmpFile.Name()), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string
|
|||||||
|
|
||||||
func doLfs(t *testing.T, content *[]byte, expectGzip bool) {
|
func doLfs(t *testing.T, content *[]byte, expectGzip bool) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
setting.CheckLFSVersion()
|
||||||
|
if !setting.LFS.StartServer {
|
||||||
|
t.Skip()
|
||||||
|
return
|
||||||
|
}
|
||||||
repo, err := models.GetRepositoryByOwnerAndName("user2", "repo1")
|
repo, err := models.GetRepositoryByOwnerAndName("user2", "repo1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
oid := storeObjectInRepo(t, repo.ID, content)
|
oid := storeObjectInRepo(t, repo.ID, content)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
act_user_id: 2
|
act_user_id: 2
|
||||||
repo_id: 2
|
repo_id: 2
|
||||||
is_private: true
|
is_private: true
|
||||||
created_unix: 1540139562
|
created_unix: 1571686356
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 2
|
id: 2
|
||||||
|
|||||||
159
models/issue.go
159
models/issue.go
@@ -1102,6 +1102,10 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
|
|||||||
if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil {
|
if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err = createMilestoneComment(e, doer, opts.Repo, opts.Issue, 0, opts.Issue.MilestoneID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the assignees
|
// Insert the assignees
|
||||||
@@ -1462,46 +1466,18 @@ func getParticipantsByIssueID(e Engine, issueID int64) ([]*User, error) {
|
|||||||
return users, e.In("id", userIDs).Find(&users)
|
return users, e.In("id", userIDs).Find(&users)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIssueMentions extracts mentioned people from content and
|
// UpdateIssueMentions updates issue-user relations for mentioned users.
|
||||||
// updates issue-user relations for them.
|
func UpdateIssueMentions(e Engine, issueID int64, mentions []*User) error {
|
||||||
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
|
|
||||||
if len(mentions) == 0 {
|
if len(mentions) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
ids := make([]int64, len(mentions))
|
||||||
for i := range mentions {
|
for i, u := range mentions {
|
||||||
mentions[i] = strings.ToLower(mentions[i])
|
ids[i] = u.ID
|
||||||
}
|
}
|
||||||
users := make([]*User, 0, len(mentions))
|
|
||||||
|
|
||||||
if err := e.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
|
|
||||||
return fmt.Errorf("find mentioned users: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ids := make([]int64, 0, len(mentions))
|
|
||||||
for _, user := range users {
|
|
||||||
ids = append(ids, user.ID)
|
|
||||||
if !user.IsOrganization() || user.NumMembers == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
memberIDs := make([]int64, 0, user.NumMembers)
|
|
||||||
orgUsers, err := getOrgUsersByOrgID(e, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("GetOrgUsersByOrgID [%d]: %v", user.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, orgUser := range orgUsers {
|
|
||||||
memberIDs = append(memberIDs, orgUser.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
ids = append(ids, memberIDs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := UpdateIssueUsersByMentions(e, issueID, ids); err != nil {
|
if err := UpdateIssueUsersByMentions(e, issueID, ids); err != nil {
|
||||||
return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
|
return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1854,3 +1830,120 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that
|
||||||
|
// don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
|
||||||
|
func (issue *Issue) ResolveMentionsByVisibility(e Engine, doer *User, mentions []string) (users []*User, err error) {
|
||||||
|
if len(mentions) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = issue.loadRepo(e); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolved := make(map[string]bool, 20)
|
||||||
|
names := make([]string, 0, 20)
|
||||||
|
resolved[doer.LowerName] = true
|
||||||
|
for _, name := range mentions {
|
||||||
|
name := strings.ToLower(name)
|
||||||
|
if _, ok := resolved[name]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resolved[name] = false
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := issue.Repo.getOwner(e); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.Repo.Owner.IsOrganization() {
|
||||||
|
// Since there can be users with names that match the name of a team,
|
||||||
|
// if the team exists and can read the issue, the team takes precedence.
|
||||||
|
teams := make([]*Team, 0, len(names))
|
||||||
|
if err := e.
|
||||||
|
Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
||||||
|
Where("team_repo.repo_id=?", issue.Repo.ID).
|
||||||
|
In("team.lower_name", names).
|
||||||
|
Find(&teams); err != nil {
|
||||||
|
return nil, fmt.Errorf("find mentioned teams: %v", err)
|
||||||
|
}
|
||||||
|
if len(teams) != 0 {
|
||||||
|
checked := make([]int64, 0, len(teams))
|
||||||
|
unittype := UnitTypeIssues
|
||||||
|
if issue.IsPull {
|
||||||
|
unittype = UnitTypePullRequests
|
||||||
|
}
|
||||||
|
for _, team := range teams {
|
||||||
|
if team.Authorize >= AccessModeOwner {
|
||||||
|
checked = append(checked, team.ID)
|
||||||
|
resolved[team.LowerName] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
has, err := e.Get(&TeamUnit{OrgID: issue.Repo.Owner.ID, TeamID: team.ID, Type: unittype})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get team units (%d): %v", team.ID, err)
|
||||||
|
}
|
||||||
|
if has {
|
||||||
|
checked = append(checked, team.ID)
|
||||||
|
resolved[team.LowerName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(checked) != 0 {
|
||||||
|
teamusers := make([]*User, 0, 20)
|
||||||
|
if err := e.
|
||||||
|
Join("INNER", "team_user", "team_user.uid = `user`.id").
|
||||||
|
In("`team_user`.team_id", checked).
|
||||||
|
And("`user`.is_active = ?", true).
|
||||||
|
And("`user`.prohibit_login = ?", false).
|
||||||
|
Find(&teamusers); err != nil {
|
||||||
|
return nil, fmt.Errorf("get teams users: %v", err)
|
||||||
|
}
|
||||||
|
if len(teamusers) > 0 {
|
||||||
|
users = make([]*User, 0, len(teamusers))
|
||||||
|
for _, user := range teamusers {
|
||||||
|
if already, ok := resolved[user.LowerName]; !ok || !already {
|
||||||
|
users = append(users, user)
|
||||||
|
resolved[user.LowerName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove names already in the list to avoid querying the database if pending names remain
|
||||||
|
names = make([]string, 0, len(resolved))
|
||||||
|
for name, already := range resolved {
|
||||||
|
if !already {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(names) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unchecked := make([]*User, 0, len(names))
|
||||||
|
if err := e.
|
||||||
|
Where("`user`.is_active = ?", true).
|
||||||
|
And("`user`.prohibit_login = ?", false).
|
||||||
|
In("`user`.lower_name", names).
|
||||||
|
Find(&unchecked); err != nil {
|
||||||
|
return nil, fmt.Errorf("find mentioned users: %v", err)
|
||||||
|
}
|
||||||
|
for _, user := range unchecked {
|
||||||
|
if already := resolved[user.LowerName]; already || user.IsOrganization() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Normal users must have read access to the referencing issue
|
||||||
|
perm, err := getUserRepoPermission(e, issue.Repo, user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getUserRepoPermission [%d]: %v", user.ID, err)
|
||||||
|
}
|
||||||
|
if !perm.CanReadIssuesOrPulls(issue.IsPull) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -387,11 +387,18 @@ func (c *Comment) MailParticipants(opType ActionType, issue *Issue) (err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
|
func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
|
||||||
mentions := markup.FindAllMentions(c.Content)
|
rawMentions := markup.FindAllMentions(c.Content)
|
||||||
if err = UpdateIssueMentions(e, c.IssueID, mentions); err != nil {
|
userMentions, err := issue.ResolveMentionsByVisibility(e, c.Poster, rawMentions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ResolveMentionsByVisibility [%d]: %v", c.IssueID, err)
|
||||||
|
}
|
||||||
|
if err = UpdateIssueMentions(e, c.IssueID, userMentions); err != nil {
|
||||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
|
return fmt.Errorf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
|
||||||
}
|
}
|
||||||
|
mentions := make([]string, len(userMentions))
|
||||||
|
for i, u := range userMentions {
|
||||||
|
mentions[i] = u.LowerName
|
||||||
|
}
|
||||||
if len(c.Content) > 0 {
|
if len(c.Content) > 0 {
|
||||||
if err = mailIssueCommentToParticipants(e, issue, c.Poster, c.Content, c, mentions); err != nil {
|
if err = mailIssueCommentToParticipants(e, issue, c.Poster, c.Content, c, mentions); err != nil {
|
||||||
log.Error("mailIssueCommentToParticipants: %v", err)
|
log.Error("mailIssueCommentToParticipants: %v", err)
|
||||||
|
|||||||
@@ -123,11 +123,18 @@ func (issue *Issue) MailParticipants(doer *User, opType ActionType) (err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (issue *Issue) mailParticipants(e Engine, doer *User, opType ActionType) (err error) {
|
func (issue *Issue) mailParticipants(e Engine, doer *User, opType ActionType) (err error) {
|
||||||
mentions := markup.FindAllMentions(issue.Content)
|
rawMentions := markup.FindAllMentions(issue.Content)
|
||||||
|
userMentions, err := issue.ResolveMentionsByVisibility(e, doer, rawMentions)
|
||||||
if err = UpdateIssueMentions(e, issue.ID, mentions); err != nil {
|
if err != nil {
|
||||||
|
return fmt.Errorf("ResolveMentionsByVisibility [%d]: %v", issue.ID, err)
|
||||||
|
}
|
||||||
|
if err = UpdateIssueMentions(e, issue.ID, userMentions); err != nil {
|
||||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
|
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
|
||||||
}
|
}
|
||||||
|
mentions := make([]string, len(userMentions))
|
||||||
|
for i, u := range userMentions {
|
||||||
|
mentions[i] = u.LowerName
|
||||||
|
}
|
||||||
|
|
||||||
if len(issue.Content) > 0 {
|
if len(issue.Content) > 0 {
|
||||||
if err = mailIssueCommentToParticipants(e, issue, doer, issue.Content, nil, mentions); err != nil {
|
if err = mailIssueCommentToParticipants(e, issue, doer, issue.Content, nil, mentions); err != nil {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@@ -290,6 +291,10 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.IsClosed = isClosed
|
m.IsClosed = isClosed
|
||||||
|
if isClosed {
|
||||||
|
m.ClosedDateUnix = util.TimeStamp(time.Now().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
if err = updateMilestone(sess, m); err != nil {
|
if err = updateMilestone(sess, m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,3 +320,35 @@ func TestIssue_SearchIssueIDsByKeyword(t *testing.T) {
|
|||||||
assert.EqualValues(t, 1, total)
|
assert.EqualValues(t, 1, total)
|
||||||
assert.EqualValues(t, []int64{1}, ids)
|
assert.EqualValues(t, []int64{1}, ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue_ResolveMentions(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
|
testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) {
|
||||||
|
o := AssertExistsAndLoadBean(t, &User{LowerName: owner}).(*User)
|
||||||
|
r := AssertExistsAndLoadBean(t, &Repository{OwnerID: o.ID, LowerName: repo}).(*Repository)
|
||||||
|
issue := &Issue{RepoID: r.ID}
|
||||||
|
d := AssertExistsAndLoadBean(t, &User{LowerName: doer}).(*User)
|
||||||
|
resolved, err := issue.ResolveMentionsByVisibility(x, d, mentions)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
ids := make([]int64, len(resolved))
|
||||||
|
for i, user := range resolved {
|
||||||
|
ids[i] = user.ID
|
||||||
|
}
|
||||||
|
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||||
|
assert.EqualValues(t, expected, ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public repo, existing user
|
||||||
|
testSuccess("user2", "repo1", "user1", []string{"user5"}, []int64{5})
|
||||||
|
// Public repo, non-existing user
|
||||||
|
testSuccess("user2", "repo1", "user1", []string{"nonexisting"}, []int64{})
|
||||||
|
// Public repo, doer
|
||||||
|
testSuccess("user2", "repo1", "user1", []string{"user1"}, []int64{})
|
||||||
|
// Private repo, team member
|
||||||
|
testSuccess("user17", "big_test_private_4", "user20", []string{"user2"}, []int64{2})
|
||||||
|
// Private repo, not a team member
|
||||||
|
testSuccess("user17", "big_test_private_4", "user20", []string{"user5"}, []int64{})
|
||||||
|
// Private repo, whole team
|
||||||
|
testSuccess("user17", "big_test_private_4", "user15", []string{"owners"}, []int64{18})
|
||||||
|
}
|
||||||
|
|||||||
@@ -384,9 +384,11 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
|
|||||||
}
|
}
|
||||||
for _, index := range res {
|
for _, index := range res {
|
||||||
indexName := index["column_name"]
|
indexName := index["column_name"]
|
||||||
_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
|
if len(indexName) > 0 {
|
||||||
if err != nil {
|
_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ func (t *Team) UnitEnabled(tp UnitType) bool {
|
|||||||
|
|
||||||
func (t *Team) unitEnabled(e Engine, tp UnitType) bool {
|
func (t *Team) unitEnabled(e Engine, tp UnitType) bool {
|
||||||
if err := t.getUnits(e); err != nil {
|
if err := t.getUnits(e); err != nil {
|
||||||
log.Warn("Error loading repository (ID: %d) units: %s", t.ID, err.Error())
|
log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, unit := range t.Units {
|
for _, unit := range t.Units {
|
||||||
|
|||||||
@@ -1063,7 +1063,7 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
|
_, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath)
|
||||||
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
||||||
return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err)
|
return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1486,6 +1486,13 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
|||||||
return fmt.Errorf("update owner: %v", err)
|
return fmt.Errorf("update owner: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update pull request headusername
|
||||||
|
if _, err := sess.Where("head_repo_id = ?", repo.ID).Update(&PullRequest{
|
||||||
|
HeadUserName: newOwner.LowerName,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("update pull request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Remove redundant collaborators.
|
// Remove redundant collaborators.
|
||||||
collaborators, err := repo.getCollaborators(sess)
|
collaborators, err := repo.getCollaborators(sess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1895,12 +1902,11 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if count > 1 {
|
if count > 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
oidPath := filepath.Join(v.Oid[0:2], v.Oid[2:4], v.Oid[4:len(v.Oid)])
|
oidPath := filepath.Join(setting.LFS.ContentPath, v.Oid[0:2], v.Oid[2:4], v.Oid[4:len(v.Oid)])
|
||||||
removeAllWithNotice(sess, "Delete orphaned LFS file", oidPath)
|
removeAllWithNotice(sess, "Delete orphaned LFS file", oidPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ func (m *Mirror) FullAddress() string {
|
|||||||
func (m *Mirror) SaveAddress(addr string) error {
|
func (m *Mirror) SaveAddress(addr string) error {
|
||||||
repoPath := m.Repo.RepoPath()
|
repoPath := m.Repo.RepoPath()
|
||||||
// Remove old origin
|
// Remove old origin
|
||||||
_, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
|
_, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath)
|
||||||
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,6 +315,18 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
|||||||
sshOpLocker.Lock()
|
sshOpLocker.Lock()
|
||||||
defer sshOpLocker.Unlock()
|
defer sshOpLocker.Unlock()
|
||||||
|
|
||||||
|
if setting.SSH.RootPath != "" {
|
||||||
|
// First of ensure that the RootPath is present, and if not make it with 0700 permissions
|
||||||
|
// This of course doesn't guarantee that this is the right directory for authorized_keys
|
||||||
|
// but at least if it's supposed to be this directory and it doesn't exist and we're the
|
||||||
|
// right user it will at least be created properly.
|
||||||
|
err := os.MkdirAll(setting.SSH.RootPath, 0700)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
||||||
f, err := os.OpenFile(fPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
f, err := os.OpenFile(fPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -602,6 +614,18 @@ func rewriteAllPublicKeys(e Engine) error {
|
|||||||
sshOpLocker.Lock()
|
sshOpLocker.Lock()
|
||||||
defer sshOpLocker.Unlock()
|
defer sshOpLocker.Unlock()
|
||||||
|
|
||||||
|
if setting.SSH.RootPath != "" {
|
||||||
|
// First of ensure that the RootPath is present, and if not make it with 0700 permissions
|
||||||
|
// This of course doesn't guarantee that this is the right directory for authorized_keys
|
||||||
|
// but at least if it's supposed to be this directory and it doesn't exist and we're the
|
||||||
|
// right user it will at least be created properly.
|
||||||
|
err := os.MkdirAll(setting.SSH.RootPath, 0700)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
||||||
tmpPath := fPath + ".tmp"
|
tmpPath := fPath + ".tmp"
|
||||||
t, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
t, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
|
|||||||
CountResult int
|
CountResult int
|
||||||
JSONResult string
|
JSONResult string
|
||||||
}{
|
}{
|
||||||
{2, 1, `[{"timestamp":1540080000,"contributions":1}]`},
|
{2, 1, `[{"timestamp":1571616000,"contributions":1}]`},
|
||||||
{3, 0, `[]`},
|
{3, 0, `[]`},
|
||||||
}
|
}
|
||||||
// Prepare
|
// Prepare
|
||||||
@@ -41,7 +41,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
|
|||||||
// Get the heatmap and compare
|
// Get the heatmap and compare
|
||||||
heatmap, err := GetUserHeatmapDataByUser(user)
|
heatmap, err := GetUserHeatmapDataByUser(user)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, len(actions), len(heatmap))
|
assert.Equal(t, len(actions), len(heatmap), "invalid action count: did the test data became too old?")
|
||||||
assert.Equal(t, tc.CountResult, len(heatmap))
|
assert.Equal(t, tc.CountResult, len(heatmap))
|
||||||
|
|
||||||
//Test JSON rendering
|
//Test JSON rendering
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
|||||||
|
|
||||||
// Force redirection when username is actually a user.
|
// Force redirection when username is actually a user.
|
||||||
if !org.IsOrganization() {
|
if !org.IsOrganization() {
|
||||||
ctx.Redirect("/" + org.Name)
|
ctx.Redirect(setting.AppSubURL + "/" + org.Name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
|
|||||||
if ctx.Req.URL.RawQuery != "" {
|
if ctx.Req.URL.RawQuery != "" {
|
||||||
redirectPath += "?" + ctx.Req.URL.RawQuery
|
redirectPath += "?" + ctx.Req.URL.RawQuery
|
||||||
}
|
}
|
||||||
ctx.Redirect(redirectPath)
|
ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
func repoAssignment(ctx *Context, repo *models.Repository) {
|
func repoAssignment(ctx *Context, repo *models.Repository) {
|
||||||
|
|||||||
@@ -28,8 +28,14 @@ func IsBranchExist(repoPath, name string) bool {
|
|||||||
|
|
||||||
// IsBranchExist returns true if given branch exists in current repository.
|
// IsBranchExist returns true if given branch exists in current repository.
|
||||||
func (repo *Repository) IsBranchExist(name string) bool {
|
func (repo *Repository) IsBranchExist(name string) bool {
|
||||||
_, err := repo.gogitRepo.Reference(plumbing.ReferenceName(BranchPrefix+name), true)
|
if name == "" {
|
||||||
return err == nil
|
return false
|
||||||
|
}
|
||||||
|
reference, err := repo.gogitRepo.Reference(plumbing.ReferenceName(BranchPrefix+name), true)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return reference.Type() != plumbing.InvalidReference
|
||||||
}
|
}
|
||||||
|
|
||||||
// Branch represents a Git branch.
|
// Branch represents a Git branch.
|
||||||
@@ -165,7 +171,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
|
|||||||
|
|
||||||
// RemoveRemote removes a remote from repository.
|
// RemoveRemote removes a remote from repository.
|
||||||
func (repo *Repository) RemoveRemote(name string) error {
|
func (repo *Repository) RemoveRemote(name string) error {
|
||||||
_, err := NewCommand("remote", "remove", name).RunInDir(repo.Path)
|
_, err := NewCommand("remote", "rm", name).RunInDir(repo.Path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,13 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mcuadros/go-version"
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,6 +65,11 @@ type CommitTreeOpts struct {
|
|||||||
|
|
||||||
// CommitTree creates a commit from a given tree id for the user with provided message
|
// CommitTree creates a commit from a given tree id for the user with provided message
|
||||||
func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) {
|
func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) {
|
||||||
|
binVersion, err := BinVersion()
|
||||||
|
if err != nil {
|
||||||
|
return SHA1{}, err
|
||||||
|
}
|
||||||
|
|
||||||
commitTimeStr := time.Now().Format(time.RFC3339)
|
commitTimeStr := time.Now().Format(time.RFC3339)
|
||||||
|
|
||||||
// Because this may call hooks we should pass in the environment
|
// Because this may call hooks we should pass in the environment
|
||||||
@@ -80,20 +87,24 @@ func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOp
|
|||||||
cmd.AddArguments("-p", parent)
|
cmd.AddArguments("-p", parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddArguments("-m", opts.Message)
|
messageBytes := new(bytes.Buffer)
|
||||||
|
_, _ = messageBytes.WriteString(opts.Message)
|
||||||
|
_, _ = messageBytes.WriteString("\n")
|
||||||
|
|
||||||
if opts.KeyID != "" {
|
if opts.KeyID != "" {
|
||||||
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID))
|
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.NoGPGSign {
|
if version.Compare(binVersion, "2.0.0", ">=") && opts.NoGPGSign {
|
||||||
cmd.AddArguments("--no-gpg-sign")
|
cmd.AddArguments("--no-gpg-sign")
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := cmd.RunInDirWithEnv(repo.Path, env)
|
stdout := new(bytes.Buffer)
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
err = cmd.RunInDirTimeoutEnvFullPipeline(env, -1, repo.Path, stdout, stderr, messageBytes)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SHA1{}, err
|
return SHA1{}, concatenateError(err, stderr.String())
|
||||||
}
|
}
|
||||||
return NewIDFromString(strings.TrimSpace(res))
|
return NewIDFromString(strings.TrimSpace(stdout.String()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ func PostLockHandler(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var req api.LFSLockRequest
|
var req api.LFSLockRequest
|
||||||
dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
|
bodyReader := ctx.Req.Body().ReadCloser()
|
||||||
|
defer bodyReader.Close()
|
||||||
|
dec := json.NewDecoder(bodyReader)
|
||||||
if err := dec.Decode(&req); err != nil {
|
if err := dec.Decode(&req); err != nil {
|
||||||
writeStatus(ctx, 400)
|
writeStatus(ctx, 400)
|
||||||
return
|
return
|
||||||
@@ -269,7 +271,9 @@ func UnLockHandler(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var req api.LFSLockDeleteRequest
|
var req api.LFSLockDeleteRequest
|
||||||
dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
|
bodyReader := ctx.Req.Body().ReadCloser()
|
||||||
|
defer bodyReader.Close()
|
||||||
|
dec := json.NewDecoder(bodyReader)
|
||||||
if err := dec.Decode(&req); err != nil {
|
if err := dec.Decode(&req); err != nil {
|
||||||
writeStatus(ctx, 400)
|
writeStatus(ctx, 400)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -327,7 +327,9 @@ func PutHandler(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentStore := &ContentStore{BasePath: setting.LFS.ContentPath}
|
contentStore := &ContentStore{BasePath: setting.LFS.ContentPath}
|
||||||
if err := contentStore.Put(meta, ctx.Req.Body().ReadCloser()); err != nil {
|
bodyReader := ctx.Req.Body().ReadCloser()
|
||||||
|
defer bodyReader.Close()
|
||||||
|
if err := contentStore.Put(meta, bodyReader); err != nil {
|
||||||
ctx.Resp.WriteHeader(500)
|
ctx.Resp.WriteHeader(500)
|
||||||
fmt.Fprintf(ctx.Resp, `{"message":"%s"}`, err)
|
fmt.Fprintf(ctx.Resp, `{"message":"%s"}`, err)
|
||||||
if err = repository.RemoveLFSMetaObjectByOid(rv.Oid); err != nil {
|
if err = repository.RemoveLFSMetaObjectByOid(rv.Oid); err != nil {
|
||||||
@@ -434,7 +436,9 @@ func unpack(ctx *context.Context) *RequestVars {
|
|||||||
|
|
||||||
if r.Method == "POST" { // Maybe also check if +json
|
if r.Method == "POST" { // Maybe also check if +json
|
||||||
var p RequestVars
|
var p RequestVars
|
||||||
dec := json.NewDecoder(r.Body().ReadCloser())
|
bodyReader := r.Body().ReadCloser()
|
||||||
|
defer bodyReader.Close()
|
||||||
|
dec := json.NewDecoder(bodyReader)
|
||||||
err := dec.Decode(&p)
|
err := dec.Decode(&p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rv
|
return rv
|
||||||
@@ -453,7 +457,9 @@ func unpackbatch(ctx *context.Context) *BatchVars {
|
|||||||
r := ctx.Req
|
r := ctx.Req
|
||||||
var bv BatchVars
|
var bv BatchVars
|
||||||
|
|
||||||
dec := json.NewDecoder(r.Body().ReadCloser())
|
bodyReader := r.Body().ReadCloser()
|
||||||
|
defer bodyReader.Close()
|
||||||
|
dec := json.NewDecoder(bodyReader)
|
||||||
err := dec.Decode(&bv)
|
err := dec.Decode(&bv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &bv
|
return &bv
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
// 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
|
// 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.
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -93,6 +95,14 @@ func (pm *Manager) ExecDir(timeout time.Duration, dir, desc, cmdName string, arg
|
|||||||
// Returns its complete stdout and stderr
|
// Returns its complete stdout and stderr
|
||||||
// outputs and an error, if any (including timeout)
|
// outputs and an error, if any (including timeout)
|
||||||
func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []string, cmdName string, args ...string) (string, string, error) {
|
func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []string, cmdName string, args ...string) (string, string, error) {
|
||||||
|
return pm.ExecDirEnvStdIn(timeout, dir, desc, env, nil, cmdName, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecDirEnvStdIn runs a command in given path and environment variables with provided stdIN, and waits for its completion
|
||||||
|
// up to the given timeout (or DefaultTimeout if -1 is given).
|
||||||
|
// Returns its complete stdout and stderr
|
||||||
|
// outputs and an error, if any (including timeout)
|
||||||
|
func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env []string, stdIn io.Reader, cmdName string, args ...string) (string, string, error) {
|
||||||
if timeout == -1 {
|
if timeout == -1 {
|
||||||
timeout = 60 * time.Second
|
timeout = 60 * time.Second
|
||||||
}
|
}
|
||||||
@@ -108,6 +118,10 @@ func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []str
|
|||||||
cmd.Env = env
|
cmd.Env = env
|
||||||
cmd.Stdout = stdOut
|
cmd.Stdout = stdOut
|
||||||
cmd.Stderr = stdErr
|
cmd.Stderr = stdErr
|
||||||
|
if stdIn != nil {
|
||||||
|
cmd.Stdin = stdIn
|
||||||
|
}
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -22,6 +21,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"github.com/mcuadros/go-version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Merge merges pull request to base repository.
|
// Merge merges pull request to base repository.
|
||||||
@@ -66,20 +66,17 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
|||||||
|
|
||||||
headRepoPath := models.RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
|
headRepoPath := models.RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
|
||||||
|
|
||||||
if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{
|
if err := git.InitRepository(tmpBasePath, false); err != nil {
|
||||||
Shared: true,
|
return fmt.Errorf("git init: %v", err)
|
||||||
NoCheckout: true,
|
|
||||||
Branch: pr.BaseBranch,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("git clone: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteRepoName := "head_repo"
|
remoteRepoName := "head_repo"
|
||||||
|
baseBranch := "base"
|
||||||
|
|
||||||
// Add head repo remote.
|
// Add head repo remote.
|
||||||
addCacheRepo := func(staging, cache string) error {
|
addCacheRepo := func(staging, cache string) error {
|
||||||
p := filepath.Join(staging, ".git", "objects", "info", "alternates")
|
p := filepath.Join(staging, ".git", "objects", "info", "alternates")
|
||||||
f, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY, 0600)
|
f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -91,25 +88,41 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
|
if err := addCacheRepo(tmpBasePath, baseGitRepo.Path); err != nil {
|
||||||
return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
|
return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var errbuf strings.Builder
|
var errbuf strings.Builder
|
||||||
|
if err := git.NewCommand("remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseGitRepo.Path).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
|
return fmt.Errorf("git remote add [%s -> %s]: %s", baseGitRepo.Path, tmpBasePath, errbuf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.NewCommand("fetch", "origin", "--no-tags", pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
|
return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.NewCommand("symbolic-ref", "HEAD", git.BranchPrefix+baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
|
return fmt.Errorf("git symbolic-ref HEAD base [%s]: %s", tmpBasePath, errbuf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
|
||||||
|
return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := git.NewCommand("remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := git.NewCommand("remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackingBranch := "tracking"
|
||||||
// Fetch head branch
|
// Fetch head branch
|
||||||
if err := git.NewCommand("fetch", remoteRepoName, fmt.Sprintf("%s:refs/remotes/%s/%s", pr.HeadBranch, remoteRepoName, pr.HeadBranch)).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := git.NewCommand("fetch", "--no-tags", remoteRepoName, pr.HeadBranch+":"+trackingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
trackingBranch := path.Join(remoteRepoName, pr.HeadBranch)
|
stagingBranch := "staging"
|
||||||
stagingBranch := fmt.Sprintf("%s_%s", remoteRepoName, pr.HeadBranch)
|
|
||||||
|
|
||||||
// Enable sparse-checkout
|
// Enable sparse-checkout
|
||||||
sparseCheckoutList, err := getDiffTree(tmpBasePath, pr.BaseBranch, trackingBranch)
|
sparseCheckoutList, err := getDiffTree(tmpBasePath, baseBranch, trackingBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("getDiffTree: %v", err)
|
return fmt.Errorf("getDiffTree: %v", err)
|
||||||
}
|
}
|
||||||
@@ -123,21 +136,37 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
|||||||
return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err)
|
return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gitConfigCommand := func() func() *git.Command {
|
||||||
|
binVersion, err := git.BinVersion()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error retrieving git version: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if version.Compare(binVersion, "1.8.0", ">=") {
|
||||||
|
return func() *git.Command {
|
||||||
|
return git.NewCommand("config", "--local")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func() *git.Command {
|
||||||
|
return git.NewCommand("config")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Switch off LFS process (set required, clean and smudge here also)
|
// Switch off LFS process (set required, clean and smudge here also)
|
||||||
if err := git.NewCommand("config", "--local", "filter.lfs.process", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := gitConfigCommand().AddArguments("filter.lfs.process", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git config [filter.lfs.process -> <> ]: %v", errbuf.String())
|
return fmt.Errorf("git config [filter.lfs.process -> <> ]: %v", errbuf.String())
|
||||||
}
|
}
|
||||||
if err := git.NewCommand("config", "--local", "filter.lfs.required", "false").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := gitConfigCommand().AddArguments("filter.lfs.required", "false").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git config [filter.lfs.required -> <false> ]: %v", errbuf.String())
|
return fmt.Errorf("git config [filter.lfs.required -> <false> ]: %v", errbuf.String())
|
||||||
}
|
}
|
||||||
if err := git.NewCommand("config", "--local", "filter.lfs.clean", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := gitConfigCommand().AddArguments("filter.lfs.clean", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git config [filter.lfs.clean -> <> ]: %v", errbuf.String())
|
return fmt.Errorf("git config [filter.lfs.clean -> <> ]: %v", errbuf.String())
|
||||||
}
|
}
|
||||||
if err := git.NewCommand("config", "--local", "filter.lfs.smudge", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := gitConfigCommand().AddArguments("filter.lfs.smudge", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git config [filter.lfs.smudge -> <> ]: %v", errbuf.String())
|
return fmt.Errorf("git config [filter.lfs.smudge -> <> ]: %v", errbuf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := git.NewCommand("config", "--local", "core.sparseCheckout", "true").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := gitConfigCommand().AddArguments("core.sparseCheckout", "true").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", errbuf.String())
|
return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", errbuf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,11 +192,11 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
|||||||
return fmt.Errorf("git checkout: %s", errbuf.String())
|
return fmt.Errorf("git checkout: %s", errbuf.String())
|
||||||
}
|
}
|
||||||
// Rebase before merging
|
// Rebase before merging
|
||||||
if err := git.NewCommand("rebase", "-q", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := git.NewCommand("rebase", "-q", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
||||||
}
|
}
|
||||||
// Checkout base branch again
|
// Checkout base branch again
|
||||||
if err := git.NewCommand("checkout", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git checkout: %s", errbuf.String())
|
return fmt.Errorf("git checkout: %s", errbuf.String())
|
||||||
}
|
}
|
||||||
// Merge fast forward
|
// Merge fast forward
|
||||||
@@ -180,11 +209,11 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
|||||||
return fmt.Errorf("git checkout: %s", errbuf.String())
|
return fmt.Errorf("git checkout: %s", errbuf.String())
|
||||||
}
|
}
|
||||||
// Rebase before merging
|
// Rebase before merging
|
||||||
if err := git.NewCommand("rebase", "-q", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := git.NewCommand("rebase", "-q", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
|
||||||
}
|
}
|
||||||
// Checkout base branch again
|
// Checkout base branch again
|
||||||
if err := git.NewCommand("checkout", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git checkout: %s", errbuf.String())
|
return fmt.Errorf("git checkout: %s", errbuf.String())
|
||||||
}
|
}
|
||||||
// Prepare merge with commit
|
// Prepare merge with commit
|
||||||
@@ -216,7 +245,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to get full commit id for HEAD: %v", err)
|
return fmt.Errorf("Failed to get full commit id for HEAD: %v", err)
|
||||||
}
|
}
|
||||||
mergeBaseSHA, err := git.GetFullCommitID(tmpBasePath, "origin/"+pr.BaseBranch)
|
mergeBaseSHA, err := git.GetFullCommitID(tmpBasePath, "original_"+baseBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to get full commit id for origin/%s: %v", pr.BaseBranch, err)
|
return fmt.Errorf("Failed to get full commit id for origin/%s: %v", pr.BaseBranch, err)
|
||||||
}
|
}
|
||||||
@@ -249,7 +278,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Push back to upstream.
|
// Push back to upstream.
|
||||||
if err := git.NewCommand("push", "origin", pr.BaseBranch).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
|
if err := git.NewCommand("push", "origin", baseBranch+":"+pr.BaseBranch).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
|
||||||
return fmt.Errorf("git push: %s", errbuf.String())
|
return fmt.Errorf("git push: %s", errbuf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ func (ct *ContentType) String() string {
|
|||||||
// GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree
|
// GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree
|
||||||
// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
|
// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
|
||||||
func GetContentsOrList(repo *models.Repository, treePath, ref string) (interface{}, error) {
|
func GetContentsOrList(repo *models.Repository, treePath, ref string) (interface{}, error) {
|
||||||
|
if repo.IsEmpty {
|
||||||
|
return make([]interface{}, 0), nil
|
||||||
|
}
|
||||||
if ref == "" {
|
if ref == "" {
|
||||||
ref = repo.DefaultBranch
|
ref = repo.DefaultBranch
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,3 +190,19 @@ func TestGetContentsOrListErrors(t *testing.T) {
|
|||||||
assert.Nil(t, fileContentResponse)
|
assert.Nil(t, fileContentResponse)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetContentsOrListOfEmptyRepos(t *testing.T) {
|
||||||
|
models.PrepareTestEnv(t)
|
||||||
|
ctx := test.MockContext(t, "user2/repo15")
|
||||||
|
ctx.SetParams(":id", "15")
|
||||||
|
test.LoadRepo(t, ctx, 15)
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
test.LoadGitRepo(t, ctx)
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
|
t.Run("empty repo", func(t *testing.T) {
|
||||||
|
contents, err := GetContentsOrList(repo, "", "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, contents)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/process"
|
"code.gitea.io/gitea/modules/process"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"github.com/mcuadros/go-version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone
|
// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone
|
||||||
@@ -253,6 +254,11 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
|
|||||||
authorSig := author.NewGitSig()
|
authorSig := author.NewGitSig()
|
||||||
committerSig := committer.NewGitSig()
|
committerSig := committer.NewGitSig()
|
||||||
|
|
||||||
|
binVersion, err := git.BinVersion()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Unable to get git version: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Should we add SSH_ORIGINAL_COMMAND to this
|
// FIXME: Should we add SSH_ORIGINAL_COMMAND to this
|
||||||
// Because this may call hooks we should pass in the environment
|
// Because this may call hooks we should pass in the environment
|
||||||
env := append(os.Environ(),
|
env := append(os.Environ(),
|
||||||
@@ -263,11 +269,21 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
|
|||||||
"GIT_COMMITTER_EMAIL="+committerSig.Email,
|
"GIT_COMMITTER_EMAIL="+committerSig.Email,
|
||||||
"GIT_COMMITTER_DATE="+commitTimeStr,
|
"GIT_COMMITTER_DATE="+commitTimeStr,
|
||||||
)
|
)
|
||||||
commitHash, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute,
|
messageBytes := new(bytes.Buffer)
|
||||||
|
_, _ = messageBytes.WriteString(message)
|
||||||
|
_, _ = messageBytes.WriteString("\n")
|
||||||
|
|
||||||
|
args := []string{"commit-tree", treeHash, "-p", "HEAD"}
|
||||||
|
if version.Compare(binVersion, "2.0.0", ">=") {
|
||||||
|
args = append(args, "--no-gpg-sign")
|
||||||
|
}
|
||||||
|
|
||||||
|
commitHash, stderr, err := process.GetManager().ExecDirEnvStdIn(5*time.Minute,
|
||||||
t.basePath,
|
t.basePath,
|
||||||
fmt.Sprintf("commitTree (git commit-tree): %s", t.basePath),
|
fmt.Sprintf("commitTree (git commit-tree): %s", t.basePath),
|
||||||
env,
|
env,
|
||||||
git.GitExecutable, "commit-tree", treeHash, "-p", "HEAD", "-m", message)
|
messageBytes,
|
||||||
|
git.GitExecutable, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("git commit-tree: %s", stderr)
|
return "", fmt.Errorf("git commit-tree: %s", stderr)
|
||||||
}
|
}
|
||||||
@@ -327,6 +343,12 @@ func (t *TemporaryUploadRepository) DiffIndex() (diff *models.Diff, err error) {
|
|||||||
|
|
||||||
// CheckAttribute checks the given attribute of the provided files
|
// CheckAttribute checks the given attribute of the provided files
|
||||||
func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...string) (map[string]map[string]string, error) {
|
func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...string) (map[string]map[string]string, error) {
|
||||||
|
binVersion, err := git.BinVersion()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error retrieving git version: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
stdOut := new(bytes.Buffer)
|
stdOut := new(bytes.Buffer)
|
||||||
stdErr := new(bytes.Buffer)
|
stdErr := new(bytes.Buffer)
|
||||||
|
|
||||||
@@ -334,7 +356,14 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cmdArgs := []string{"check-attr", "-z", attribute, "--cached", "--"}
|
cmdArgs := []string{"check-attr", "-z", attribute}
|
||||||
|
|
||||||
|
// git check-attr --cached first appears in git 1.7.8
|
||||||
|
if version.Compare(binVersion, "1.7.8", ">=") {
|
||||||
|
cmdArgs = append(cmdArgs, "--cached")
|
||||||
|
}
|
||||||
|
cmdArgs = append(cmdArgs, "--")
|
||||||
|
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if arg != "" {
|
if arg != "" {
|
||||||
cmdArgs = append(cmdArgs, arg)
|
cmdArgs = append(cmdArgs, arg)
|
||||||
@@ -352,7 +381,7 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str
|
|||||||
}
|
}
|
||||||
|
|
||||||
pid := process.GetManager().Add(desc, cmd)
|
pid := process.GetManager().Add(desc, cmd)
|
||||||
err := cmd.Wait()
|
err = cmd.Wait()
|
||||||
process.GetManager().Remove(pid)
|
process.GetManager().Remove(pid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -311,12 +311,6 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check there is no way this can return multiple infos
|
|
||||||
filename2attribute2info, err := t.CheckAttribute("filter", treePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
content := opts.Content
|
content := opts.Content
|
||||||
if bom {
|
if bom {
|
||||||
content = string(base.UTF8BOM) + content
|
content = string(base.UTF8BOM) + content
|
||||||
@@ -339,16 +333,23 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
|
|||||||
opts.Content = content
|
opts.Content = content
|
||||||
var lfsMetaObject *models.LFSMetaObject
|
var lfsMetaObject *models.LFSMetaObject
|
||||||
|
|
||||||
if setting.LFS.StartServer && filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" {
|
if setting.LFS.StartServer {
|
||||||
// OK so we are supposed to LFS this data!
|
// Check there is no way this can return multiple infos
|
||||||
oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content))
|
filename2attribute2info, err := t.CheckAttribute("filter", treePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(opts.Content)), RepositoryID: repo.ID}
|
|
||||||
content = lfsMetaObject.Pointer()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" {
|
||||||
|
// OK so we are supposed to LFS this data!
|
||||||
|
oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(opts.Content)), RepositoryID: repo.ID}
|
||||||
|
content = lfsMetaObject.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
// Add the object to the database
|
// Add the object to the database
|
||||||
objectHash, err := t.HashObject(strings.NewReader(content))
|
objectHash, err := t.HashObject(strings.NewReader(content))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -74,9 +74,12 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
|
|||||||
infos[i] = uploadInfo{upload: upload}
|
infos[i] = uploadInfo{upload: upload}
|
||||||
}
|
}
|
||||||
|
|
||||||
filename2attribute2info, err := t.CheckAttribute("filter", names...)
|
var filename2attribute2info map[string]map[string]string
|
||||||
if err != nil {
|
if setting.LFS.StartServer {
|
||||||
return err
|
filename2attribute2info, err = t.CheckAttribute("filter", names...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy uploaded files into repository.
|
// Copy uploaded files into repository.
|
||||||
@@ -88,7 +91,7 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
|
|||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
var objectHash string
|
var objectHash string
|
||||||
if filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" {
|
if setting.LFS.StartServer && filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" {
|
||||||
// Handle LFS
|
// Handle LFS
|
||||||
// FIXME: Inefficient! this should probably happen in models.Upload
|
// FIXME: Inefficient! this should probably happen in models.Upload
|
||||||
oid, err := models.GenerateLFSOid(file)
|
oid, err := models.GenerateLFSOid(file)
|
||||||
|
|||||||
@@ -231,12 +231,9 @@ func ToTeam(team *models.Team) *api.Team {
|
|||||||
// ToUser convert models.User to api.User
|
// ToUser convert models.User to api.User
|
||||||
func ToUser(user *models.User, signed, authed bool) *api.User {
|
func ToUser(user *models.User, signed, authed bool) *api.User {
|
||||||
result := &api.User{
|
result := &api.User{
|
||||||
ID: user.ID,
|
|
||||||
UserName: user.Name,
|
UserName: user.Name,
|
||||||
AvatarURL: user.AvatarLink(),
|
AvatarURL: user.AvatarLink(),
|
||||||
FullName: markup.Sanitize(user.FullName),
|
FullName: markup.Sanitize(user.FullName),
|
||||||
IsAdmin: user.IsAdmin,
|
|
||||||
LastLogin: user.LastLoginUnix.AsTime(),
|
|
||||||
Created: user.CreatedUnix.AsTime(),
|
Created: user.CreatedUnix.AsTime(),
|
||||||
}
|
}
|
||||||
// hide primary email if API caller isn't user itself or an admin
|
// hide primary email if API caller isn't user itself or an admin
|
||||||
@@ -244,8 +241,11 @@ func ToUser(user *models.User, signed, authed bool) *api.User {
|
|||||||
result.Email = ""
|
result.Email = ""
|
||||||
} else if user.KeepEmailPrivate && !authed {
|
} else if user.KeepEmailPrivate && !authed {
|
||||||
result.Email = user.GetEmail()
|
result.Email = user.GetEmail()
|
||||||
} else {
|
} else { // only user himself and admin could visit these information
|
||||||
|
result.ID = user.ID
|
||||||
result.Email = user.Email
|
result.Email = user.Email
|
||||||
|
result.IsAdmin = user.IsAdmin
|
||||||
|
result.LastLogin = user.LastLoginUnix.AsTime()
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -310,14 +310,13 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the deadline
|
// Update the deadline
|
||||||
var deadlineUnix util.TimeStamp
|
|
||||||
if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.CanWrite(models.UnitTypeIssues) {
|
if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.CanWrite(models.UnitTypeIssues) {
|
||||||
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
|
deadlineUnix := util.TimeStamp(form.Deadline.Unix())
|
||||||
}
|
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
|
||||||
|
ctx.Error(500, "UpdateIssueDeadline", err)
|
||||||
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
|
return
|
||||||
ctx.Error(500, "UpdateIssueDeadline", err)
|
}
|
||||||
return
|
issue.DeadlineUnix = deadlineUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add/delete assignees
|
// Add/delete assignees
|
||||||
|
|||||||
@@ -375,14 +375,13 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update Deadline
|
// Update Deadline
|
||||||
var deadlineUnix util.TimeStamp
|
|
||||||
if form.Deadline != nil && !form.Deadline.IsZero() {
|
if form.Deadline != nil && !form.Deadline.IsZero() {
|
||||||
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
|
deadlineUnix := util.TimeStamp(form.Deadline.Unix())
|
||||||
}
|
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
|
||||||
|
ctx.Error(500, "UpdateIssueDeadline", err)
|
||||||
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
|
return
|
||||||
ctx.Error(500, "UpdateIssueDeadline", err)
|
}
|
||||||
return
|
issue.DeadlineUnix = deadlineUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add/delete assignees
|
// Add/delete assignees
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func GetInfo(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, convert.ToUser(u, ctx.IsSigned, ctx.User.ID == u.ID || ctx.User.IsAdmin))
|
ctx.JSON(200, convert.ToUser(u, ctx.IsSigned, ctx.User != nil && (ctx.User.ID == u.ID || ctx.User.IsAdmin)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthenticatedUser get current user's information
|
// GetAuthenticatedUser get current user's information
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ func ExploreOrganizations(ctx *context.Context) {
|
|||||||
// ExploreCode render explore code page
|
// ExploreCode render explore code page
|
||||||
func ExploreCode(ctx *context.Context) {
|
func ExploreCode(ctx *context.Context) {
|
||||||
if !setting.Indexer.RepoIndexerEnabled {
|
if !setting.Indexer.RepoIndexerEnabled {
|
||||||
ctx.Redirect("/explore", 302)
|
ctx.Redirect(setting.AppSubURL+"/explore", 302)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err = lfsDataRc.Close(); err != nil {
|
||||||
|
log.Error("ServeBlobOrLFS: Close: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
return ServeData(ctx, ctx.Repo.TreePath, lfsDataRc)
|
return ServeData(ctx, ctx.Repo.TreePath, lfsDataRc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<p>{{.i18n.Tr "auth.authroize_redirect_notice" .ApplicationRedirectDomainHTML | Str2html}}</p>
|
<p>{{.i18n.Tr "auth.authroize_redirect_notice" .ApplicationRedirectDomainHTML | Str2html}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
<form method="post" action="{{.AppSubUrl}}/login/oauth/grant">
|
<form method="post" action="{{AppSubUrl}}/login/oauth/grant">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
<input type="hidden" name="client_id" value="{{.Application.ClientID}}">
|
<input type="hidden" name="client_id" value="{{.Application.ClientID}}">
|
||||||
<input type="hidden" name="state" value="{{.State}}">
|
<input type="hidden" name="state" value="{{.State}}">
|
||||||
|
|||||||
Reference in New Issue
Block a user