* Add options to git.Clone to make it more capable * Begin the process of removing the local copy and tidy up * Remove Wiki LocalCopy Checkouts * Remove the last LocalRepo helpers * Remove WithTemporaryFile * Enable push-hooks for these routes * Ensure tests cope with hooks Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove Repository.LocalCopyPath() * Move temporary repo to use the standard temporary path * Fix the tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove LocalWikiPath * Fix missing remove Signed-off-by: Andrew Thornton <art27@cantab.net> * Use AppURL for Oauth user link (#6894) * Use AppURL for Oauth user link Fix #6843 * Update oauth.go * Update oauth.go * internal/ssh: ignore env command totally (#6825) * ssh: ignore env command totally * Remove commented code Needed fix described in issue #6889 * Escape the commit message on issues update and title in telegram hook (#6901) * update sdk to latest (#6903) * improve description of branch protection (fix #6886) (#6906) The branch protection description text were not quite accurate. * Fix logging documentation (#6904) * ENABLE_MACARON_REDIRECT should be REDIRECT_MACARON_LOG * Allow DISABLE_ROUTER_LOG to be set in the [log] section * [skip ci] Updated translations via Crowdin * Move sdk structs to modules/structs (#6905) * move sdk structs to moduels/structs * fix tests * fix fmt * fix swagger * fix vendortags/v1.9.0-rc1
models.LoadFixtures() | models.LoadFixtures() | ||||
os.RemoveAll(setting.RepoRootPath) | os.RemoveAll(setting.RepoRootPath) | ||||
os.RemoveAll(models.LocalCopyPath()) | os.RemoveAll(models.LocalCopyPath()) | ||||
os.RemoveAll(models.LocalWikiPath()) | |||||
com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath) | com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath) | ||||
log.Printf("[PR] Setting up router\n") | log.Printf("[PR] Setting up router\n") |
import ( | import ( | ||||
"net/http" | "net/http" | ||||
"net/url" | |||||
"path/filepath" | "path/filepath" | ||||
"testing" | "testing" | ||||
} | } | ||||
func TestAPIGetFileContents(t *testing.T) { | func TestAPIGetFileContents(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
onGiteaRun(t, testAPIGetFileContents) | |||||
} | |||||
func testAPIGetFileContents(t *testing.T, u *url.URL) { | |||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 | user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 | ||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org | user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org | ||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos | user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos |
"encoding/base64" | "encoding/base64" | ||||
"fmt" | "fmt" | ||||
"net/http" | "net/http" | ||||
"net/url" | |||||
"path/filepath" | "path/filepath" | ||||
"testing" | "testing" | ||||
} | } | ||||
func TestAPICreateFile(t *testing.T) { | func TestAPICreateFile(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 | |||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org | |||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos | |||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo | |||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo | |||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo | |||||
fileID := 0 | |||||
// Get user2's token | |||||
session := loginUser(t, user2.Name) | |||||
token2 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Get user4's token | |||||
session = loginUser(t, user4.Name) | |||||
token4 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Test creating a file in repo1 which user2 owns, try both with branch and empty branch | |||||
for _, branch := range [...]string{ | |||||
"master", // Branch | |||||
"", // Empty branch | |||||
} { | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 | |||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org | |||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos | |||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo | |||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo | |||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo | |||||
fileID := 0 | |||||
// Get user2's token | |||||
session := loginUser(t, user2.Name) | |||||
token2 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Get user4's token | |||||
session = loginUser(t, user4.Name) | |||||
token4 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Test creating a file in repo1 which user2 owns, try both with branch and empty branch | |||||
for _, branch := range [...]string{ | |||||
"master", // Branch | |||||
"", // Empty branch | |||||
} { | |||||
createFileOptions := getCreateFileOptions() | |||||
createFileOptions.BranchName = branch | |||||
fileID++ | |||||
treePath := fmt.Sprintf("new/file%d.txt", fileID) | |||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req := NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
resp := session.MakeRequest(t, req, http.StatusCreated) | |||||
gitRepo, _ := git.OpenRepository(repo1.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) | |||||
expectedFileResponse := getExpectedFileResponseForCreate(commitID, treePath) | |||||
var fileResponse api.FileResponse | |||||
DecodeJSON(t, resp, &fileResponse) | |||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) | |||||
} | |||||
// Test creating a file in a new branch | |||||
createFileOptions := getCreateFileOptions() | createFileOptions := getCreateFileOptions() | ||||
createFileOptions.BranchName = branch | |||||
createFileOptions.BranchName = repo1.DefaultBranch | |||||
createFileOptions.NewBranchName = "new_branch" | |||||
fileID++ | fileID++ | ||||
treePath := fmt.Sprintf("new/file%d.txt", fileID) | treePath := fmt.Sprintf("new/file%d.txt", fileID) | ||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | ||||
req := NewRequestWithJSON(t, "POST", url, &createFileOptions) | req := NewRequestWithJSON(t, "POST", url, &createFileOptions) | ||||
resp := session.MakeRequest(t, req, http.StatusCreated) | resp := session.MakeRequest(t, req, http.StatusCreated) | ||||
gitRepo, _ := git.OpenRepository(repo1.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) | |||||
expectedFileResponse := getExpectedFileResponseForCreate(commitID, treePath) | |||||
var fileResponse api.FileResponse | var fileResponse api.FileResponse | ||||
DecodeJSON(t, resp, &fileResponse) | DecodeJSON(t, resp, &fileResponse) | ||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) | |||||
} | |||||
expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf" | |||||
expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/new/file%d.txt", fileID) | |||||
expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID) | |||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) | |||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) | |||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||||
// Test trying to create a file that already exists, should fail | |||||
createFileOptions = getCreateFileOptions() | |||||
treePath = "README.md" | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError) | |||||
expectedAPIError := context.APIError{ | |||||
Message: "repository file already exists [path: " + treePath + "]", | |||||
URL: base.DocURL, | |||||
} | |||||
var apiError context.APIError | |||||
DecodeJSON(t, resp, &apiError) | |||||
assert.Equal(t, expectedAPIError, apiError) | |||||
// Test creating a file in repo1 by user4 who does not have write access | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test creating a file in a new branch | |||||
createFileOptions := getCreateFileOptions() | |||||
createFileOptions.BranchName = repo1.DefaultBranch | |||||
createFileOptions.NewBranchName = "new_branch" | |||||
fileID++ | |||||
treePath := fmt.Sprintf("new/file%d.txt", fileID) | |||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req := NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
resp := session.MakeRequest(t, req, http.StatusCreated) | |||||
var fileResponse api.FileResponse | |||||
DecodeJSON(t, resp, &fileResponse) | |||||
expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf" | |||||
expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/new/file%d.txt", fileID) | |||||
expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID) | |||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) | |||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) | |||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||||
// Test trying to create a file that already exists, should fail | |||||
createFileOptions = getCreateFileOptions() | |||||
treePath = "README.md" | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError) | |||||
expectedAPIError := context.APIError{ | |||||
Message: "repository file already exists [path: " + treePath + "]", | |||||
URL: base.DocURL, | |||||
} | |||||
var apiError context.APIError | |||||
DecodeJSON(t, resp, &apiError) | |||||
assert.Equal(t, expectedAPIError, apiError) | |||||
// Test creating a file in repo1 by user4 who does not have write access | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Tests a repo with no token given so will fail | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using access token for a private repo that the user of the token owns | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusCreated) | |||||
// Test using org repo "user3/repo3" where user2 is a collaborator | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusCreated) | |||||
// Test using org repo "user3/repo3" with no user token | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using repo "user2/repo1" where user4 is a NOT collaborator | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusForbidden) | |||||
// Tests a repo with no token given so will fail | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using access token for a private repo that the user of the token owns | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusCreated) | |||||
// Test using org repo "user3/repo3" where user2 is a collaborator | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusCreated) | |||||
// Test using org repo "user3/repo3" with no user token | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using repo "user2/repo1" where user4 is a NOT collaborator | |||||
createFileOptions = getCreateFileOptions() | |||||
fileID++ | |||||
treePath = fmt.Sprintf("new/file%d.txt", fileID) | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions) | |||||
session.MakeRequest(t, req, http.StatusForbidden) | |||||
}) | |||||
} | } |
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"net/http" | "net/http" | ||||
"net/url" | |||||
"testing" | "testing" | ||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
} | } | ||||
func TestAPIDeleteFile(t *testing.T) { | func TestAPIDeleteFile(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 | |||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org | |||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos | |||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo | |||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo | |||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo | |||||
fileID := 0 | |||||
// Get user2's token | |||||
session := loginUser(t, user2.Name) | |||||
token2 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Get user4's token | |||||
session = loginUser(t, user4.Name) | |||||
token4 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Test deleting a file in repo1 which user2 owns, try both with branch and empty branch | |||||
for _, branch := range [...]string{ | |||||
"master", // Branch | |||||
"", // Empty branch | |||||
} { | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 | |||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org | |||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos | |||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo | |||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo | |||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo | |||||
fileID := 0 | |||||
// Get user2's token | |||||
session := loginUser(t, user2.Name) | |||||
token2 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Get user4's token | |||||
session = loginUser(t, user4.Name) | |||||
token4 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Test deleting a file in repo1 which user2 owns, try both with branch and empty branch | |||||
for _, branch := range [...]string{ | |||||
"master", // Branch | |||||
"", // Empty branch | |||||
} { | |||||
fileID++ | |||||
treePath := fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
deleteFileOptions := getDeleteFileOptions() | |||||
deleteFileOptions.BranchName = branch | |||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req := NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
resp := session.MakeRequest(t, req, http.StatusOK) | |||||
var fileResponse api.FileResponse | |||||
DecodeJSON(t, resp, &fileResponse) | |||||
assert.NotNil(t, fileResponse) | |||||
assert.Nil(t, fileResponse.Content) | |||||
} | |||||
// Test deleting file and making the delete in a new branch | |||||
fileID++ | fileID++ | ||||
treePath := fmt.Sprintf("delete/file%d.txt", fileID) | treePath := fmt.Sprintf("delete/file%d.txt", fileID) | ||||
createFile(user2, repo1, treePath) | createFile(user2, repo1, treePath) | ||||
deleteFileOptions := getDeleteFileOptions() | deleteFileOptions := getDeleteFileOptions() | ||||
deleteFileOptions.BranchName = branch | |||||
deleteFileOptions.BranchName = repo1.DefaultBranch | |||||
deleteFileOptions.NewBranchName = "new_branch" | |||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | ||||
req := NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | req := NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | ||||
resp := session.MakeRequest(t, req, http.StatusOK) | resp := session.MakeRequest(t, req, http.StatusOK) | ||||
DecodeJSON(t, resp, &fileResponse) | DecodeJSON(t, resp, &fileResponse) | ||||
assert.NotNil(t, fileResponse) | assert.NotNil(t, fileResponse) | ||||
assert.Nil(t, fileResponse.Content) | assert.Nil(t, fileResponse.Content) | ||||
} | |||||
// Test deleting file and making the delete in a new branch | |||||
fileID++ | |||||
treePath := fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
deleteFileOptions := getDeleteFileOptions() | |||||
deleteFileOptions.BranchName = repo1.DefaultBranch | |||||
deleteFileOptions.NewBranchName = "new_branch" | |||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req := NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
resp := session.MakeRequest(t, req, http.StatusOK) | |||||
var fileResponse api.FileResponse | |||||
DecodeJSON(t, resp, &fileResponse) | |||||
assert.NotNil(t, fileResponse) | |||||
assert.Nil(t, fileResponse.Content) | |||||
// Test deleting a file with the wrong SHA | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
correctSHA := deleteFileOptions.SHA | |||||
deleteFileOptions.SHA = "badsha" | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError) | |||||
expectedAPIError := context.APIError{ | |||||
Message: "sha does not match [given: " + deleteFileOptions.SHA + ", expected: " + correctSHA + "]", | |||||
URL: base.DocURL, | |||||
} | |||||
var apiError context.APIError | |||||
DecodeJSON(t, resp, &apiError) | |||||
assert.Equal(t, expectedAPIError, apiError) | |||||
// Test creating a file in repo1 by user4 who does not have write access | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Tests a repo with no token given so will fail | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using access token for a private repo that the user of the token owns | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
// Test using org repo "user3/repo3" where user2 is a collaborator | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user3, repo3, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
// Test using org repo "user3/repo3" with no user token | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user3, repo3, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using repo "user2/repo1" where user4 is a NOT collaborator | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusForbidden) | |||||
// Test deleting a file with the wrong SHA | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
correctSHA := deleteFileOptions.SHA | |||||
deleteFileOptions.SHA = "badsha" | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError) | |||||
expectedAPIError := context.APIError{ | |||||
Message: "sha does not match [given: " + deleteFileOptions.SHA + ", expected: " + correctSHA + "]", | |||||
URL: base.DocURL, | |||||
} | |||||
var apiError context.APIError | |||||
DecodeJSON(t, resp, &apiError) | |||||
assert.Equal(t, expectedAPIError, apiError) | |||||
// Test creating a file in repo1 by user4 who does not have write access | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Tests a repo with no token given so will fail | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using access token for a private repo that the user of the token owns | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
// Test using org repo "user3/repo3" where user2 is a collaborator | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user3, repo3, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
// Test using org repo "user3/repo3" with no user token | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user3, repo3, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using repo "user2/repo1" where user4 is a NOT collaborator | |||||
fileID++ | |||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
deleteFileOptions = getDeleteFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) | |||||
session.MakeRequest(t, req, http.StatusForbidden) | |||||
}) | |||||
} | } |
"encoding/base64" | "encoding/base64" | ||||
"fmt" | "fmt" | ||||
"net/http" | "net/http" | ||||
"net/url" | |||||
"path/filepath" | "path/filepath" | ||||
"testing" | "testing" | ||||
} | } | ||||
func TestAPIUpdateFile(t *testing.T) { | func TestAPIUpdateFile(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 | |||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org | |||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos | |||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo | |||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo | |||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo | |||||
fileID := 0 | |||||
// Get user2's token | |||||
session := loginUser(t, user2.Name) | |||||
token2 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Get user4's token | |||||
session = loginUser(t, user4.Name) | |||||
token4 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Test updating a file in repo1 which user2 owns, try both with branch and empty branch | |||||
for _, branch := range [...]string{ | |||||
"master", // Branch | |||||
"", // Empty branch | |||||
} { | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 | |||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org | |||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos | |||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo | |||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo | |||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo | |||||
fileID := 0 | |||||
// Get user2's token | |||||
session := loginUser(t, user2.Name) | |||||
token2 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Get user4's token | |||||
session = loginUser(t, user4.Name) | |||||
token4 := getTokenForLoggedInUser(t, session) | |||||
session = emptyTestSession(t) | |||||
// Test updating a file in repo1 which user2 owns, try both with branch and empty branch | |||||
for _, branch := range [...]string{ | |||||
"master", // Branch | |||||
"", // Empty branch | |||||
} { | |||||
fileID++ | |||||
treePath := fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
updateFileOptions := getUpdateFileOptions() | |||||
updateFileOptions.BranchName = branch | |||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req := NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
resp := session.MakeRequest(t, req, http.StatusOK) | |||||
gitRepo, _ := git.OpenRepository(repo1.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName) | |||||
expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath) | |||||
var fileResponse api.FileResponse | |||||
DecodeJSON(t, resp, &fileResponse) | |||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) | |||||
} | |||||
// Test updating a file in a new branch | |||||
updateFileOptions := getUpdateFileOptions() | |||||
updateFileOptions.BranchName = repo1.DefaultBranch | |||||
updateFileOptions.NewBranchName = "new_branch" | |||||
fileID++ | fileID++ | ||||
treePath := fmt.Sprintf("update/file%d.txt", fileID) | treePath := fmt.Sprintf("update/file%d.txt", fileID) | ||||
createFile(user2, repo1, treePath) | createFile(user2, repo1, treePath) | ||||
updateFileOptions := getUpdateFileOptions() | |||||
updateFileOptions.BranchName = branch | |||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | ||||
req := NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | req := NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | ||||
resp := session.MakeRequest(t, req, http.StatusOK) | resp := session.MakeRequest(t, req, http.StatusOK) | ||||
gitRepo, _ := git.OpenRepository(repo1.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName) | |||||
expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath) | |||||
var fileResponse api.FileResponse | var fileResponse api.FileResponse | ||||
DecodeJSON(t, resp, &fileResponse) | DecodeJSON(t, resp, &fileResponse) | ||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) | |||||
} | |||||
// Test updating a file in a new branch | |||||
updateFileOptions := getUpdateFileOptions() | |||||
updateFileOptions.BranchName = repo1.DefaultBranch | |||||
updateFileOptions.NewBranchName = "new_branch" | |||||
fileID++ | |||||
treePath := fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req := NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
resp := session.MakeRequest(t, req, http.StatusOK) | |||||
var fileResponse api.FileResponse | |||||
DecodeJSON(t, resp, &fileResponse) | |||||
expectedSHA := "08bd14b2e2852529157324de9c226b3364e76136" | |||||
expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/update/file%d.txt", fileID) | |||||
expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID) | |||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) | |||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) | |||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||||
// Test updating a file and renaming it | |||||
updateFileOptions = getUpdateFileOptions() | |||||
updateFileOptions.BranchName = repo1.DefaultBranch | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
updateFileOptions.FromPath = treePath | |||||
treePath = "rename/" + treePath | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
DecodeJSON(t, resp, &fileResponse) | |||||
expectedSHA = "08bd14b2e2852529157324de9c226b3364e76136" | |||||
expectedHTMLURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/master/rename/update/file%d.txt", fileID) | |||||
expectedDownloadURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID) | |||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) | |||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) | |||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||||
// Test updating a file with the wrong SHA | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
correctSHA := updateFileOptions.SHA | |||||
updateFileOptions.SHA = "badsha" | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError) | |||||
expectedAPIError := context.APIError{ | |||||
Message: "sha does not match [given: " + updateFileOptions.SHA + ", expected: " + correctSHA + "]", | |||||
URL: base.DocURL, | |||||
} | |||||
var apiError context.APIError | |||||
DecodeJSON(t, resp, &apiError) | |||||
assert.Equal(t, expectedAPIError, apiError) | |||||
// Test creating a file in repo1 by user4 who does not have write access | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Tests a repo with no token given so will fail | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using access token for a private repo that the user of the token owns | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
// Test using org repo "user3/repo3" where user2 is a collaborator | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user3, repo3, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
// Test using org repo "user3/repo3" with no user token | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user3, repo3, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using repo "user2/repo1" where user4 is a NOT collaborator | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusForbidden) | |||||
expectedSHA := "08bd14b2e2852529157324de9c226b3364e76136" | |||||
expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/update/file%d.txt", fileID) | |||||
expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID) | |||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) | |||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) | |||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||||
// Test updating a file and renaming it | |||||
updateFileOptions = getUpdateFileOptions() | |||||
updateFileOptions.BranchName = repo1.DefaultBranch | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
updateFileOptions.FromPath = treePath | |||||
treePath = "rename/" + treePath | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
DecodeJSON(t, resp, &fileResponse) | |||||
expectedSHA = "08bd14b2e2852529157324de9c226b3364e76136" | |||||
expectedHTMLURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/master/rename/update/file%d.txt", fileID) | |||||
expectedDownloadURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID) | |||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) | |||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) | |||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||||
// Test updating a file with the wrong SHA | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
correctSHA := updateFileOptions.SHA | |||||
updateFileOptions.SHA = "badsha" | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError) | |||||
expectedAPIError := context.APIError{ | |||||
Message: "sha does not match [given: " + updateFileOptions.SHA + ", expected: " + correctSHA + "]", | |||||
URL: base.DocURL, | |||||
} | |||||
var apiError context.APIError | |||||
DecodeJSON(t, resp, &apiError) | |||||
assert.Equal(t, expectedAPIError, apiError) | |||||
// Test creating a file in repo1 by user4 who does not have write access | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Tests a repo with no token given so will fail | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using access token for a private repo that the user of the token owns | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo16, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
// Test using org repo "user3/repo3" where user2 is a collaborator | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user3, repo3, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
// Test using org repo "user3/repo3" with no user token | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user3, repo3, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusNotFound) | |||||
// Test using repo "user2/repo1" where user4 is a NOT collaborator | |||||
fileID++ | |||||
treePath = fmt.Sprintf("update/file%d.txt", fileID) | |||||
createFile(user2, repo1, treePath) | |||||
updateFileOptions = getUpdateFileOptions() | |||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) | |||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) | |||||
session.MakeRequest(t, req, http.StatusForbidden) | |||||
}) | |||||
} | } |
import ( | import ( | ||||
"net/http" | "net/http" | ||||
"net/http/httptest" | "net/http/httptest" | ||||
"net/url" | |||||
"path" | "path" | ||||
"testing" | "testing" | ||||
) | ) | ||||
func TestCreateFile(t *testing.T) { | func TestCreateFile(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
session := loginUser(t, "user2") | |||||
session := loginUser(t, "user2") | |||||
// Request editor page | |||||
req := NewRequest(t, "GET", "/user2/repo1/_new/master/") | |||||
resp := session.MakeRequest(t, req, http.StatusOK) | |||||
// Request editor page | |||||
req := NewRequest(t, "GET", "/user2/repo1/_new/master/") | |||||
resp := session.MakeRequest(t, req, http.StatusOK) | |||||
doc := NewHTMLParser(t, resp.Body) | |||||
lastCommit := doc.GetInputValueByName("last_commit") | |||||
assert.NotEmpty(t, lastCommit) | |||||
doc := NewHTMLParser(t, resp.Body) | |||||
lastCommit := doc.GetInputValueByName("last_commit") | |||||
assert.NotEmpty(t, lastCommit) | |||||
// Save new file to master branch | |||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ | |||||
"_csrf": doc.GetCSRF(), | |||||
"last_commit": lastCommit, | |||||
"tree_path": "test.txt", | |||||
"content": "Content", | |||||
"commit_choice": "direct", | |||||
// Save new file to master branch | |||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ | |||||
"_csrf": doc.GetCSRF(), | |||||
"last_commit": lastCommit, | |||||
"tree_path": "test.txt", | |||||
"content": "Content", | |||||
"commit_choice": "direct", | |||||
}) | |||||
resp = session.MakeRequest(t, req, http.StatusFound) | |||||
}) | }) | ||||
resp = session.MakeRequest(t, req, http.StatusFound) | |||||
} | } | ||||
func TestCreateFileOnProtectedBranch(t *testing.T) { | func TestCreateFileOnProtectedBranch(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user2") | |||||
csrf := GetCSRF(t, session, "/user2/repo1/settings/branches") | |||||
// Change master branch to protected | |||||
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{ | |||||
"_csrf": csrf, | |||||
"protected": "on", | |||||
}) | |||||
resp := session.MakeRequest(t, req, http.StatusFound) | |||||
// Check if master branch has been locked successfully | |||||
flashCookie := session.GetCookie("macaron_flash") | |||||
assert.NotNil(t, flashCookie) | |||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) | |||||
// Request editor page | |||||
req = NewRequest(t, "GET", "/user2/repo1/_new/master/") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
doc := NewHTMLParser(t, resp.Body) | |||||
lastCommit := doc.GetInputValueByName("last_commit") | |||||
assert.NotEmpty(t, lastCommit) | |||||
// Save new file to master branch | |||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ | |||||
"_csrf": doc.GetCSRF(), | |||||
"last_commit": lastCommit, | |||||
"tree_path": "test.txt", | |||||
"content": "Content", | |||||
"commit_choice": "direct", | |||||
}) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
// Check body for error message | |||||
assert.Contains(t, resp.Body.String(), "Cannot commit to protected branch 'master'.") | |||||
// remove the protected branch | |||||
csrf = GetCSRF(t, session, "/user2/repo1/settings/branches") | |||||
// Change master branch to protected | |||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{ | |||||
"_csrf": csrf, | |||||
"protected": "off", | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
session := loginUser(t, "user2") | |||||
csrf := GetCSRF(t, session, "/user2/repo1/settings/branches") | |||||
// Change master branch to protected | |||||
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{ | |||||
"_csrf": csrf, | |||||
"protected": "on", | |||||
}) | |||||
resp := session.MakeRequest(t, req, http.StatusFound) | |||||
// Check if master branch has been locked successfully | |||||
flashCookie := session.GetCookie("macaron_flash") | |||||
assert.NotNil(t, flashCookie) | |||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) | |||||
// Request editor page | |||||
req = NewRequest(t, "GET", "/user2/repo1/_new/master/") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
doc := NewHTMLParser(t, resp.Body) | |||||
lastCommit := doc.GetInputValueByName("last_commit") | |||||
assert.NotEmpty(t, lastCommit) | |||||
// Save new file to master branch | |||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ | |||||
"_csrf": doc.GetCSRF(), | |||||
"last_commit": lastCommit, | |||||
"tree_path": "test.txt", | |||||
"content": "Content", | |||||
"commit_choice": "direct", | |||||
}) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
// Check body for error message | |||||
assert.Contains(t, resp.Body.String(), "Cannot commit to protected branch 'master'.") | |||||
// remove the protected branch | |||||
csrf = GetCSRF(t, session, "/user2/repo1/settings/branches") | |||||
// Change master branch to protected | |||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{ | |||||
"_csrf": csrf, | |||||
"protected": "off", | |||||
}) | |||||
resp = session.MakeRequest(t, req, http.StatusFound) | |||||
// Check if master branch has been locked successfully | |||||
flashCookie = session.GetCookie("macaron_flash") | |||||
assert.NotNil(t, flashCookie) | |||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bdisabled.", flashCookie.Value) | |||||
}) | }) | ||||
resp = session.MakeRequest(t, req, http.StatusFound) | |||||
// Check if master branch has been locked successfully | |||||
flashCookie = session.GetCookie("macaron_flash") | |||||
assert.NotNil(t, flashCookie) | |||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bdisabled.", flashCookie.Value) | |||||
} | } | ||||
func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) *httptest.ResponseRecorder { | func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) *httptest.ResponseRecorder { | ||||
} | } | ||||
func TestEditFile(t *testing.T) { | func TestEditFile(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user2") | |||||
testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
session := loginUser(t, "user2") | |||||
testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
}) | |||||
} | } | ||||
func TestEditFileToNewBranch(t *testing.T) { | func TestEditFileToNewBranch(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user2") | |||||
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n") | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
session := loginUser(t, "user2") | |||||
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n") | |||||
}) | |||||
} | } |
assert.NoError(t, models.LoadFixtures()) | assert.NoError(t, models.LoadFixtures()) | ||||
assert.NoError(t, os.RemoveAll(setting.RepoRootPath)) | assert.NoError(t, os.RemoveAll(setting.RepoRootPath)) | ||||
assert.NoError(t, os.RemoveAll(models.LocalCopyPath())) | assert.NoError(t, os.RemoveAll(models.LocalCopyPath())) | ||||
assert.NoError(t, os.RemoveAll(models.LocalWikiPath())) | |||||
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), | assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), | ||||
setting.RepoRootPath)) | setting.RepoRootPath)) |
import ( | import ( | ||||
"net/http" | "net/http" | ||||
"net/http/httptest" | "net/http/httptest" | ||||
"net/url" | |||||
"path" | "path" | ||||
"strings" | "strings" | ||||
"testing" | "testing" | ||||
} | } | ||||
func TestPullCreate(t *testing.T) { | func TestPullCreate(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
// check the redirected URL | |||||
url := resp.HeaderMap.Get("Location") | |||||
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url) | |||||
// check .diff can be accessed and matches performed change | |||||
req := NewRequest(t, "GET", url+".diff") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body) | |||||
assert.Regexp(t, "^diff", resp.Body) | |||||
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one | |||||
// check .patch can be accessed and matches performed change | |||||
req = NewRequest(t, "GET", url+".patch") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body) | |||||
assert.Regexp(t, "diff", resp.Body) | |||||
assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body) | |||||
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
// check the redirected URL | |||||
url := resp.HeaderMap.Get("Location") | |||||
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url) | |||||
// check .diff can be accessed and matches performed change | |||||
req := NewRequest(t, "GET", url+".diff") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body) | |||||
assert.Regexp(t, "^diff", resp.Body) | |||||
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one | |||||
// check .patch can be accessed and matches performed change | |||||
req = NewRequest(t, "GET", url+".patch") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body) | |||||
assert.Regexp(t, "diff", resp.Body) | |||||
assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body) | |||||
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one | |||||
}) | |||||
} | } | ||||
func TestPullCreate_TitleEscape(t *testing.T) { | func TestPullCreate_TitleEscape(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "<i>XSS PR</i>") | |||||
// check the redirected URL | |||||
url := resp.HeaderMap.Get("Location") | |||||
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url) | |||||
// Edit title | |||||
req := NewRequest(t, "GET", url) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url") | |||||
assert.True(t, exists, "The template has changed") | |||||
req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{ | |||||
"_csrf": htmlDoc.GetCSRF(), | |||||
"title": "<u>XSS PR</u>", | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "<i>XSS PR</i>") | |||||
// check the redirected URL | |||||
url := resp.HeaderMap.Get("Location") | |||||
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url) | |||||
// Edit title | |||||
req := NewRequest(t, "GET", url) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url") | |||||
assert.True(t, exists, "The template has changed") | |||||
req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{ | |||||
"_csrf": htmlDoc.GetCSRF(), | |||||
"title": "<u>XSS PR</u>", | |||||
}) | |||||
session.MakeRequest(t, req, http.StatusOK) | |||||
req = NewRequest(t, "GET", url) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc = NewHTMLParser(t, resp.Body) | |||||
titleHTML, err := htmlDoc.doc.Find(".comments .event .text b").First().Html() | |||||
assert.NoError(t, err) | |||||
assert.Equal(t, "<i>XSS PR</i>", titleHTML) | |||||
titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html() | |||||
assert.NoError(t, err) | |||||
assert.Equal(t, "<u>XSS PR</u>", titleHTML) | |||||
}) | }) | ||||
session.MakeRequest(t, req, http.StatusOK) | |||||
req = NewRequest(t, "GET", url) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc = NewHTMLParser(t, resp.Body) | |||||
titleHTML, err := htmlDoc.doc.Find(".comments .event .text b").First().Html() | |||||
assert.NoError(t, err) | |||||
assert.Equal(t, "<i>XSS PR</i>", titleHTML) | |||||
titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html() | |||||
assert.NoError(t, err) | |||||
assert.Equal(t, "<u>XSS PR</u>", titleHTML) | |||||
} | } |
import ( | import ( | ||||
"net/http" | "net/http" | ||||
"net/http/httptest" | "net/http/httptest" | ||||
"net/url" | |||||
"path" | "path" | ||||
"strings" | "strings" | ||||
"testing" | "testing" | ||||
} | } | ||||
func TestPullMerge(t *testing.T) { | func TestPullMerge(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) | |||||
}) | |||||
} | } | ||||
func TestPullRebase(t *testing.T) { | func TestPullRebase(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase) | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase) | |||||
}) | |||||
} | } | ||||
func TestPullRebaseMerge(t *testing.T) { | func TestPullRebaseMerge(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { | |||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebaseMerge) | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebaseMerge) | |||||
}) | |||||
} | } | ||||
func TestPullSquash(t *testing.T) { | func TestPullSquash(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash) | |||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { | |||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash) | |||||
}) | |||||
} | } | ||||
func TestPullCleanUpAfterMerge(t *testing.T) { | func TestPullCleanUpAfterMerge(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n") | |||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { | |||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title") | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) | |||||
// Check PR branch deletion | |||||
resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4]) | |||||
respJSON := struct { | |||||
Redirect string | |||||
}{} | |||||
DecodeJSON(t, resp, &respJSON) | |||||
// Check PR branch deletion | |||||
resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4]) | |||||
respJSON := struct { | |||||
Redirect string | |||||
}{} | |||||
DecodeJSON(t, resp, &respJSON) | |||||
assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found") | |||||
assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found") | |||||
elem = strings.Split(respJSON.Redirect, "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
elem = strings.Split(respJSON.Redirect, "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
// Check branch deletion result | |||||
req := NewRequest(t, "GET", respJSON.Redirect) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
// Check branch deletion result | |||||
req := NewRequest(t, "GET", respJSON.Redirect) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
resultMsg := htmlDoc.doc.Find(".ui.message>p").Text() | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
resultMsg := htmlDoc.doc.Find(".ui.message>p").Text() | |||||
assert.EqualValues(t, "Branch 'user1/feature/test' has been deleted.", resultMsg) | |||||
assert.EqualValues(t, "Branch 'user1/feature/test' has been deleted.", resultMsg) | |||||
}) | |||||
} | } | ||||
func TestCantMergeWorkInProgress(t *testing.T) { | func TestCantMergeWorkInProgress(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "[wip] This is a pull title") | |||||
req := NewRequest(t, "GET", resp.Header().Get("Location")) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
text := strings.TrimSpace(htmlDoc.doc.Find(".merge.segment > .text.grey").Text()) | |||||
assert.NotEmpty(t, text, "Can't find WIP text") | |||||
// remove <strong /> from lang | |||||
expected := i18n.Tr("en", "repo.pulls.cannot_merge_work_in_progress", "[wip]") | |||||
replacer := strings.NewReplacer("<strong>", "", "</strong>", "") | |||||
assert.Equal(t, replacer.Replace(expected), text, "Unable to find WIP text") | |||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { | |||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "[wip] This is a pull title") | |||||
req := NewRequest(t, "GET", resp.Header().Get("Location")) | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
text := strings.TrimSpace(htmlDoc.doc.Find(".merge.segment > .text.grey").Text()) | |||||
assert.NotEmpty(t, text, "Can't find WIP text") | |||||
// remove <strong /> from lang | |||||
expected := i18n.Tr("en", "repo.pulls.cannot_merge_work_in_progress", "[wip]") | |||||
replacer := strings.NewReplacer("<strong>", "", "</strong>", "") | |||||
assert.Equal(t, replacer.Replace(expected), text, "Unable to find WIP text") | |||||
}) | |||||
} | } |
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"net/http" | "net/http" | ||||
"net/url" | |||||
"path" | "path" | ||||
"testing" | "testing" | ||||
) | ) | ||||
func TestPullCreate_CommitStatus(t *testing.T) { | func TestPullCreate_CommitStatus(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1") | |||||
url := path.Join("user1", "repo1", "compare", "master...status1") | |||||
req := NewRequestWithValues(t, "POST", url, | |||||
map[string]string{ | |||||
"_csrf": GetCSRF(t, session, url), | |||||
"title": "pull request from status1", | |||||
}, | |||||
) | |||||
session.MakeRequest(t, req, http.StatusFound) | |||||
req = NewRequest(t, "GET", "/user1/repo1/pulls") | |||||
resp := session.MakeRequest(t, req, http.StatusOK) | |||||
doc := NewHTMLParser(t, resp.Body) | |||||
// Request repository commits page | |||||
req = NewRequest(t, "GET", "/user1/repo1/pulls/1/commits") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
doc = NewHTMLParser(t, resp.Body) | |||||
// Get first commit URL | |||||
commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") | |||||
assert.True(t, exists) | |||||
assert.NotEmpty(t, commitURL) | |||||
commitID := path.Base(commitURL) | |||||
statusList := []models.CommitStatusState{ | |||||
models.CommitStatusPending, | |||||
models.CommitStatusError, | |||||
models.CommitStatusFailure, | |||||
models.CommitStatusWarning, | |||||
models.CommitStatusSuccess, | |||||
} | |||||
statesIcons := map[models.CommitStatusState]string{ | |||||
models.CommitStatusPending: "circle icon yellow", | |||||
models.CommitStatusSuccess: "check icon green", | |||||
models.CommitStatusError: "warning icon red", | |||||
models.CommitStatusFailure: "remove icon red", | |||||
models.CommitStatusWarning: "warning sign icon yellow", | |||||
} | |||||
// Update commit status, and check if icon is updated as well | |||||
for _, status := range statusList { | |||||
// Call API to add status for commit | |||||
token := getTokenForLoggedInUser(t, session) | |||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/user1/repo1/statuses/%s?token=%s", commitID, token), | |||||
api.CreateStatusOption{ | |||||
State: api.StatusState(status), | |||||
TargetURL: "http://test.ci/", | |||||
Description: "", | |||||
Context: "testci", | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
session := loginUser(t, "user1") | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1") | |||||
url := path.Join("user1", "repo1", "compare", "master...status1") | |||||
req := NewRequestWithValues(t, "POST", url, | |||||
map[string]string{ | |||||
"_csrf": GetCSRF(t, session, url), | |||||
"title": "pull request from status1", | |||||
}, | }, | ||||
) | ) | ||||
session.MakeRequest(t, req, http.StatusCreated) | |||||
session.MakeRequest(t, req, http.StatusFound) | |||||
req = NewRequestf(t, "GET", "/user1/repo1/pulls/1/commits") | |||||
req = NewRequest(t, "GET", "/user1/repo1/pulls") | |||||
resp := session.MakeRequest(t, req, http.StatusOK) | |||||
doc := NewHTMLParser(t, resp.Body) | |||||
// Request repository commits page | |||||
req = NewRequest(t, "GET", "/user1/repo1/pulls/1/commits") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | resp = session.MakeRequest(t, req, http.StatusOK) | ||||
doc = NewHTMLParser(t, resp.Body) | doc = NewHTMLParser(t, resp.Body) | ||||
commitURL, exists = doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") | |||||
// Get first commit URL | |||||
commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") | |||||
assert.True(t, exists) | assert.True(t, exists) | ||||
assert.NotEmpty(t, commitURL) | assert.NotEmpty(t, commitURL) | ||||
assert.EqualValues(t, commitID, path.Base(commitURL)) | |||||
cls, ok := doc.doc.Find("#commits-table tbody tr td.message i.commit-status").Last().Attr("class") | |||||
assert.True(t, ok) | |||||
assert.EqualValues(t, "commit-status "+statesIcons[status], cls) | |||||
} | |||||
commitID := path.Base(commitURL) | |||||
statusList := []models.CommitStatusState{ | |||||
models.CommitStatusPending, | |||||
models.CommitStatusError, | |||||
models.CommitStatusFailure, | |||||
models.CommitStatusWarning, | |||||
models.CommitStatusSuccess, | |||||
} | |||||
statesIcons := map[models.CommitStatusState]string{ | |||||
models.CommitStatusPending: "circle icon yellow", | |||||
models.CommitStatusSuccess: "check icon green", | |||||
models.CommitStatusError: "warning icon red", | |||||
models.CommitStatusFailure: "remove icon red", | |||||
models.CommitStatusWarning: "warning sign icon yellow", | |||||
} | |||||
// Update commit status, and check if icon is updated as well | |||||
for _, status := range statusList { | |||||
// Call API to add status for commit | |||||
token := getTokenForLoggedInUser(t, session) | |||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/user1/repo1/statuses/%s?token=%s", commitID, token), | |||||
api.CreateStatusOption{ | |||||
State: api.StatusState(status), | |||||
TargetURL: "http://test.ci/", | |||||
Description: "", | |||||
Context: "testci", | |||||
}, | |||||
) | |||||
session.MakeRequest(t, req, http.StatusCreated) | |||||
req = NewRequestf(t, "GET", "/user1/repo1/pulls/1/commits") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
doc = NewHTMLParser(t, resp.Body) | |||||
commitURL, exists = doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") | |||||
assert.True(t, exists) | |||||
assert.NotEmpty(t, commitURL) | |||||
assert.EqualValues(t, commitID, path.Base(commitURL)) | |||||
cls, ok := doc.doc.Find("#commits-table tbody tr td.message i.commit-status").Last().Attr("class") | |||||
assert.True(t, ok) | |||||
assert.EqualValues(t, "commit-status "+statesIcons[status], cls) | |||||
} | |||||
}) | |||||
} | } |
import ( | import ( | ||||
"net/http" | "net/http" | ||||
"net/url" | |||||
"strings" | "strings" | ||||
"testing" | "testing" | ||||
) | ) | ||||
func TestRepoActivity(t *testing.T) { | func TestRepoActivity(t *testing.T) { | ||||
prepareTestEnv(t) | |||||
session := loginUser(t, "user1") | |||||
// Create PRs (1 merged & 2 proposed) | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) | |||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n") | |||||
testPullCreate(t, session, "user1", "repo1", "feat/better_readme", "This is a pull title") | |||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n") | |||||
testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme", "This is a pull title") | |||||
// Create issues (3 new issues) | |||||
testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1") | |||||
testNewIssue(t, session, "user2", "repo1", "Issue 2", "Description 2") | |||||
testNewIssue(t, session, "user2", "repo1", "Issue 3", "Description 3") | |||||
// Create releases (1 new release) | |||||
createNewRelease(t, session, "/user2/repo1", "v1.0.0", "v1.0.0", false, false) | |||||
// Open Activity page and check stats | |||||
req := NewRequest(t, "GET", "/user2/repo1/activity") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
// Should be 1 published release | |||||
list := htmlDoc.doc.Find("#published-releases").Next().Find("p.desc") | |||||
assert.Len(t, list.Nodes, 1) | |||||
// Should be 1 merged pull request | |||||
list = htmlDoc.doc.Find("#merged-pull-requests").Next().Find("p.desc") | |||||
assert.Len(t, list.Nodes, 1) | |||||
// Should be 2 merged proposed pull requests | |||||
list = htmlDoc.doc.Find("#proposed-pull-requests").Next().Find("p.desc") | |||||
assert.Len(t, list.Nodes, 2) | |||||
// Should be 3 new issues | |||||
list = htmlDoc.doc.Find("#new-issues").Next().Find("p.desc") | |||||
assert.Len(t, list.Nodes, 3) | |||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { | |||||
session := loginUser(t, "user1") | |||||
// Create PRs (1 merged & 2 proposed) | |||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") | |||||
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") | |||||
elem := strings.Split(test.RedirectURL(resp), "/") | |||||
assert.EqualValues(t, "pulls", elem[3]) | |||||
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) | |||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n") | |||||
testPullCreate(t, session, "user1", "repo1", "feat/better_readme", "This is a pull title") | |||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n") | |||||
testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme", "This is a pull title") | |||||
// Create issues (3 new issues) | |||||
testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1") | |||||
testNewIssue(t, session, "user2", "repo1", "Issue 2", "Description 2") | |||||
testNewIssue(t, session, "user2", "repo1", "Issue 3", "Description 3") | |||||
// Create releases (1 new release) | |||||
createNewRelease(t, session, "/user2/repo1", "v1.0.0", "v1.0.0", false, false) | |||||
// Open Activity page and check stats | |||||
req := NewRequest(t, "GET", "/user2/repo1/activity") | |||||
resp = session.MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
// Should be 1 published release | |||||
list := htmlDoc.doc.Find("#published-releases").Next().Find("p.desc") | |||||
assert.Len(t, list.Nodes, 1) | |||||
// Should be 1 merged pull request | |||||
list = htmlDoc.doc.Find("#merged-pull-requests").Next().Find("p.desc") | |||||
assert.Len(t, list.Nodes, 1) | |||||
// Should be 2 merged proposed pull requests | |||||
list = htmlDoc.doc.Find("#proposed-pull-requests").Next().Find("p.desc") | |||||
assert.Len(t, list.Nodes, 2) | |||||
// Should be 3 new issues | |||||
list = htmlDoc.doc.Find("#new-issues").Next().Find("p.desc") | |||||
assert.Len(t, list.Nodes, 3) | |||||
}) | |||||
} | } |
import ( | import ( | ||||
"net/http" | "net/http" | ||||
"net/url" | |||||
"path" | "path" | ||||
"strings" | "strings" | ||||
"testing" | "testing" | ||||
} | } | ||||
func TestCreateBranch(t *testing.T) { | func TestCreateBranch(t *testing.T) { | ||||
onGiteaRun(t, testCreateBranches) | |||||
} | |||||
func testCreateBranches(t *testing.T, giteaURL *url.URL) { | |||||
tests := []struct { | tests := []struct { | ||||
OldRefSubURL string | OldRefSubURL string | ||||
NewBranch string | NewBranch string |
// 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. | ||||
package repofiles | |||||
package integrations | |||||
import ( | import ( | ||||
"net/url" | |||||
"testing" | "testing" | ||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/repofiles" | |||||
api "code.gitea.io/gitea/modules/structs" | api "code.gitea.io/gitea/modules/structs" | ||||
"code.gitea.io/gitea/modules/test" | "code.gitea.io/gitea/modules/test" | ||||
"github.com/stretchr/testify/assert" | "github.com/stretchr/testify/assert" | ||||
) | ) | ||||
func getDeleteRepoFileOptions(repo *models.Repository) *DeleteRepoFileOptions { | |||||
return &DeleteRepoFileOptions{ | |||||
func getDeleteRepoFileOptions(repo *models.Repository) *repofiles.DeleteRepoFileOptions { | |||||
return &repofiles.DeleteRepoFileOptions{ | |||||
LastCommitID: "", | LastCommitID: "", | ||||
OldBranch: repo.DefaultBranch, | OldBranch: repo.DefaultBranch, | ||||
NewBranch: repo.DefaultBranch, | NewBranch: repo.DefaultBranch, | ||||
} | } | ||||
} | } | ||||
func getExpectedDeleteFileResponse() *api.FileResponse { | |||||
func getExpectedDeleteFileResponse(u *url.URL) *api.FileResponse { | |||||
return &api.FileResponse{ | return &api.FileResponse{ | ||||
Content: nil, | Content: nil, | ||||
Commit: &api.FileCommitResponse{ | Commit: &api.FileCommitResponse{ | ||||
CommitMeta: api.CommitMeta{ | CommitMeta: api.CommitMeta{ | ||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
URL: u.String() + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", | SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", | ||||
}, | }, | ||||
HTMLURL: "https://try.gitea.io/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
HTMLURL: u.String() + "user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
Author: &api.CommitUser{ | Author: &api.CommitUser{ | ||||
Identity: api.Identity{ | Identity: api.Identity{ | ||||
Name: "user1", | Name: "user1", | ||||
Parents: []*api.CommitMeta{}, | Parents: []*api.CommitMeta{}, | ||||
Message: "Initial commit\n", | Message: "Initial commit\n", | ||||
Tree: &api.CommitMeta{ | Tree: &api.CommitMeta{ | ||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6", | |||||
URL: u.String() + "api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6", | |||||
SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6", | SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6", | ||||
}, | }, | ||||
}, | }, | ||||
} | } | ||||
func TestDeleteRepoFile(t *testing.T) { | func TestDeleteRepoFile(t *testing.T) { | ||||
onGiteaRun(t, testDeleteRepoFile) | |||||
} | |||||
func testDeleteRepoFile(t *testing.T, u *url.URL) { | |||||
// setup | // setup | ||||
models.PrepareTestEnv(t) | models.PrepareTestEnv(t) | ||||
ctx := test.MockContext(t, "user2/repo1") | ctx := test.MockContext(t, "user2/repo1") | ||||
opts := getDeleteRepoFileOptions(repo) | opts := getDeleteRepoFileOptions(repo) | ||||
t.Run("Delete README.md file", func(t *testing.T) { | t.Run("Delete README.md file", func(t *testing.T) { | ||||
fileResponse, err := DeleteRepoFile(repo, doer, opts) | |||||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) | |||||
assert.Nil(t, err) | assert.Nil(t, err) | ||||
expectedFileResponse := getExpectedDeleteFileResponse() | |||||
expectedFileResponse := getExpectedDeleteFileResponse(u) | |||||
assert.EqualValues(t, expectedFileResponse, fileResponse) | assert.EqualValues(t, expectedFileResponse, fileResponse) | ||||
}) | }) | ||||
t.Run("Verify README.md has been deleted", func(t *testing.T) { | t.Run("Verify README.md has been deleted", func(t *testing.T) { | ||||
fileResponse, err := DeleteRepoFile(repo, doer, opts) | |||||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | assert.Nil(t, fileResponse) | ||||
expectedError := "repository file does not exist [path: " + opts.TreePath + "]" | expectedError := "repository file does not exist [path: " + opts.TreePath + "]" | ||||
assert.EqualError(t, err, expectedError) | assert.EqualError(t, err, expectedError) | ||||
// Test opts with branch names removed, same results | // Test opts with branch names removed, same results | ||||
func TestDeleteRepoFileWithoutBranchNames(t *testing.T) { | func TestDeleteRepoFileWithoutBranchNames(t *testing.T) { | ||||
onGiteaRun(t, testDeleteRepoFileWithoutBranchNames) | |||||
} | |||||
func testDeleteRepoFileWithoutBranchNames(t *testing.T, u *url.URL) { | |||||
// setup | // setup | ||||
models.PrepareTestEnv(t) | models.PrepareTestEnv(t) | ||||
ctx := test.MockContext(t, "user2/repo1") | ctx := test.MockContext(t, "user2/repo1") | ||||
opts.NewBranch = "" | opts.NewBranch = "" | ||||
t.Run("Delete README.md without Branch Name", func(t *testing.T) { | t.Run("Delete README.md without Branch Name", func(t *testing.T) { | ||||
fileResponse, err := DeleteRepoFile(repo, doer, opts) | |||||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) | |||||
assert.Nil(t, err) | assert.Nil(t, err) | ||||
expectedFileResponse := getExpectedDeleteFileResponse() | |||||
expectedFileResponse := getExpectedDeleteFileResponse(u) | |||||
assert.EqualValues(t, expectedFileResponse, fileResponse) | assert.EqualValues(t, expectedFileResponse, fileResponse) | ||||
}) | }) | ||||
} | } | ||||
t.Run("Bad branch", func(t *testing.T) { | t.Run("Bad branch", func(t *testing.T) { | ||||
opts := getDeleteRepoFileOptions(repo) | opts := getDeleteRepoFileOptions(repo) | ||||
opts.OldBranch = "bad_branch" | opts.OldBranch = "bad_branch" | ||||
fileResponse, err := DeleteRepoFile(repo, doer, opts) | |||||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) | |||||
assert.Error(t, err) | assert.Error(t, err) | ||||
assert.Nil(t, fileResponse) | assert.Nil(t, fileResponse) | ||||
expectedError := "branch does not exist [name: " + opts.OldBranch + "]" | expectedError := "branch does not exist [name: " + opts.OldBranch + "]" | ||||
opts := getDeleteRepoFileOptions(repo) | opts := getDeleteRepoFileOptions(repo) | ||||
origSHA := opts.SHA | origSHA := opts.SHA | ||||
opts.SHA = "bad_sha" | opts.SHA = "bad_sha" | ||||
fileResponse, err := DeleteRepoFile(repo, doer, opts) | |||||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | assert.Nil(t, fileResponse) | ||||
assert.Error(t, err) | assert.Error(t, err) | ||||
expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]" | expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]" | ||||
t.Run("New branch already exists", func(t *testing.T) { | t.Run("New branch already exists", func(t *testing.T) { | ||||
opts := getDeleteRepoFileOptions(repo) | opts := getDeleteRepoFileOptions(repo) | ||||
opts.NewBranch = "develop" | opts.NewBranch = "develop" | ||||
fileResponse, err := DeleteRepoFile(repo, doer, opts) | |||||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | assert.Nil(t, fileResponse) | ||||
assert.Error(t, err) | assert.Error(t, err) | ||||
expectedError := "branch already exists [name: " + opts.NewBranch + "]" | expectedError := "branch already exists [name: " + opts.NewBranch + "]" | ||||
t.Run("TreePath is empty:", func(t *testing.T) { | t.Run("TreePath is empty:", func(t *testing.T) { | ||||
opts := getDeleteRepoFileOptions(repo) | opts := getDeleteRepoFileOptions(repo) | ||||
opts.TreePath = "" | opts.TreePath = "" | ||||
fileResponse, err := DeleteRepoFile(repo, doer, opts) | |||||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | assert.Nil(t, fileResponse) | ||||
assert.Error(t, err) | assert.Error(t, err) | ||||
expectedError := "path contains a malformed path component [path: ]" | expectedError := "path contains a malformed path component [path: ]" | ||||
t.Run("TreePath is a git directory:", func(t *testing.T) { | t.Run("TreePath is a git directory:", func(t *testing.T) { | ||||
opts := getDeleteRepoFileOptions(repo) | opts := getDeleteRepoFileOptions(repo) | ||||
opts.TreePath = ".git" | opts.TreePath = ".git" | ||||
fileResponse, err := DeleteRepoFile(repo, doer, opts) | |||||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | assert.Nil(t, fileResponse) | ||||
assert.Error(t, err) | assert.Error(t, err) | ||||
expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]" | expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]" |
// Copyright 2019 The Gitea Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package integrations | |||||
import ( | |||||
"net/url" | |||||
"testing" | |||||
"time" | |||||
"code.gitea.io/gitea/models" | |||||
"code.gitea.io/gitea/modules/git" | |||||
"code.gitea.io/gitea/modules/repofiles" | |||||
"code.gitea.io/gitea/modules/setting" | |||||
api "code.gitea.io/gitea/modules/structs" | |||||
"code.gitea.io/gitea/modules/test" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
func getCreateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFileOptions { | |||||
return &repofiles.UpdateRepoFileOptions{ | |||||
OldBranch: repo.DefaultBranch, | |||||
NewBranch: repo.DefaultBranch, | |||||
TreePath: "new/file.txt", | |||||
Message: "Creates new/file.txt", | |||||
Content: "This is a NEW file", | |||||
IsNewFile: true, | |||||
Author: nil, | |||||
Committer: nil, | |||||
} | |||||
} | |||||
func getUpdateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFileOptions { | |||||
return &repofiles.UpdateRepoFileOptions{ | |||||
OldBranch: repo.DefaultBranch, | |||||
NewBranch: repo.DefaultBranch, | |||||
TreePath: "README.md", | |||||
Message: "Updates README.md", | |||||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", | |||||
Content: "This is UPDATED content for the README file", | |||||
IsNewFile: false, | |||||
Author: nil, | |||||
Committer: nil, | |||||
} | |||||
} | |||||
func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileResponse { | |||||
return &api.FileResponse{ | |||||
Content: &api.FileContentResponse{ | |||||
Name: "file.txt", | |||||
Path: "new/file.txt", | |||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", | |||||
Size: 18, | |||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt", | |||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt", | |||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", | |||||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/new/file.txt", | |||||
Type: "blob", | |||||
Links: &api.FileLinksResponse{ | |||||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt", | |||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", | |||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt", | |||||
}, | |||||
}, | |||||
Commit: &api.FileCommitResponse{ | |||||
CommitMeta: api.CommitMeta{ | |||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID, | |||||
SHA: commitID, | |||||
}, | |||||
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID, | |||||
Author: &api.CommitUser{ | |||||
Identity: api.Identity{ | |||||
Name: "User Two", | |||||
Email: "user2@noreply.example.org", | |||||
}, | |||||
Date: time.Now().UTC().Format(time.RFC3339), | |||||
}, | |||||
Committer: &api.CommitUser{ | |||||
Identity: api.Identity{ | |||||
Name: "User Two", | |||||
Email: "user2@noreply.example.org", | |||||
}, | |||||
Date: time.Now().UTC().Format(time.RFC3339), | |||||
}, | |||||
Parents: []*api.CommitMeta{ | |||||
{ | |||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
}, | |||||
}, | |||||
Message: "Updates README.md\n", | |||||
Tree: &api.CommitMeta{ | |||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", | |||||
SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc", | |||||
}, | |||||
}, | |||||
Verification: &api.PayloadCommitVerification{ | |||||
Verified: false, | |||||
Reason: "unsigned", | |||||
Signature: "", | |||||
Payload: "", | |||||
}, | |||||
} | |||||
} | |||||
func getExpectedFileResponseForRepofilesUpdate(commitID string) *api.FileResponse { | |||||
return &api.FileResponse{ | |||||
Content: &api.FileContentResponse{ | |||||
Name: "README.md", | |||||
Path: "README.md", | |||||
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", | |||||
Size: 43, | |||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md", | |||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md", | |||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", | |||||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/README.md", | |||||
Type: "blob", | |||||
Links: &api.FileLinksResponse{ | |||||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md", | |||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", | |||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md", | |||||
}, | |||||
}, | |||||
Commit: &api.FileCommitResponse{ | |||||
CommitMeta: api.CommitMeta{ | |||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID, | |||||
SHA: commitID, | |||||
}, | |||||
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID, | |||||
Author: &api.CommitUser{ | |||||
Identity: api.Identity{ | |||||
Name: "User Two", | |||||
Email: "user2@noreply.example.org", | |||||
}, | |||||
Date: time.Now().UTC().Format(time.RFC3339), | |||||
}, | |||||
Committer: &api.CommitUser{ | |||||
Identity: api.Identity{ | |||||
Name: "User Two", | |||||
Email: "user2@noreply.example.org", | |||||
}, | |||||
Date: time.Now().UTC().Format(time.RFC3339), | |||||
}, | |||||
Parents: []*api.CommitMeta{ | |||||
{ | |||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
}, | |||||
}, | |||||
Message: "Updates README.md\n", | |||||
Tree: &api.CommitMeta{ | |||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", | |||||
SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc", | |||||
}, | |||||
}, | |||||
Verification: &api.PayloadCommitVerification{ | |||||
Verified: false, | |||||
Reason: "unsigned", | |||||
Signature: "", | |||||
Payload: "", | |||||
}, | |||||
} | |||||
} | |||||
func TestCreateOrUpdateRepoFileForCreate(t *testing.T) { | |||||
// setup | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
opts := getCreateRepoFileOptions(repo) | |||||
// test | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
// asserts | |||||
assert.Nil(t, err) | |||||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) | |||||
expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID) | |||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) | |||||
}) | |||||
} | |||||
func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) { | |||||
// setup | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
// test | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
// asserts | |||||
assert.Nil(t, err) | |||||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) | |||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID) | |||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) | |||||
}) | |||||
} | |||||
func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { | |||||
// setup | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
suffix := "_new" | |||||
opts.FromTreePath = "README.md" | |||||
opts.TreePath = "README.md" + suffix // new file name, README.md_new | |||||
// test | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
// asserts | |||||
assert.Nil(t, err) | |||||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) | |||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String()) | |||||
// assert that the old file no longer exists in the last commit of the branch | |||||
fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath) | |||||
toEntry, err := commit.GetTreeEntryByPath(opts.TreePath) | |||||
assert.Nil(t, fromEntry) // Should no longer exist here | |||||
assert.NotNil(t, toEntry) // Should exist here | |||||
// assert SHA has remained the same but paths use the new file name | |||||
assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Content.Name+suffix, fileResponse.Content.Name) | |||||
assert.EqualValues(t, expectedFileResponse.Content.Path+suffix, fileResponse.Content.Path) | |||||
assert.EqualValues(t, expectedFileResponse.Content.URL+suffix, fileResponse.Content.URL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
}) | |||||
} | |||||
// Test opts with branch names removed, should get same results as above test | |||||
func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) { | |||||
// setup | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.OldBranch = "" | |||||
opts.NewBranch = "" | |||||
// test | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
// asserts | |||||
assert.Nil(t, err) | |||||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch) | |||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID) | |||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
}) | |||||
} | |||||
func TestCreateOrUpdateRepoFileErrors(t *testing.T) { | |||||
// setup | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
t.Run("bad branch", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.OldBranch = "bad_branch" | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Error(t, err) | |||||
assert.Nil(t, fileResponse) | |||||
expectedError := "branch does not exist [name: " + opts.OldBranch + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("bad SHA", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
origSHA := opts.SHA | |||||
opts.SHA = "bad_sha" | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("new branch already exists", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.NewBranch = "develop" | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "branch already exists [name: " + opts.NewBranch + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("treePath is empty:", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.TreePath = "" | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "path contains a malformed path component [path: ]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("treePath is a git directory:", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.TreePath = ".git" | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("create file that already exists", func(t *testing.T) { | |||||
opts := getCreateRepoFileOptions(repo) | |||||
opts.TreePath = "README.md" //already exists | |||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "repository file already exists [path: " + opts.TreePath + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
}) | |||||
} |
// Copyright 2019 The Gitea Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package models | |||||
import ( | |||||
"fmt" | |||||
"os" | |||||
"path" | |||||
"path/filepath" | |||||
"time" | |||||
"code.gitea.io/gitea/modules/log" | |||||
"code.gitea.io/gitea/modules/setting" | |||||
"github.com/Unknwon/com" | |||||
) | |||||
// LocalCopyPath returns the local repository temporary copy path. | |||||
func LocalCopyPath() string { | |||||
if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) { | |||||
return setting.Repository.Local.LocalCopyPath | |||||
} | |||||
return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath) | |||||
} | |||||
// CreateTemporaryPath creates a temporary path | |||||
func CreateTemporaryPath(prefix string) (string, error) { | |||||
timeStr := com.ToStr(time.Now().Nanosecond()) // SHOULD USE SOMETHING UNIQUE | |||||
basePath := path.Join(LocalCopyPath(), prefix+"-"+timeStr+".git") | |||||
if err := os.MkdirAll(filepath.Dir(basePath), os.ModePerm); err != nil { | |||||
log.Error("Unable to create temporary directory: %s (%v)", basePath, err) | |||||
return "", fmt.Errorf("Failed to create dir %s: %v", basePath, err) | |||||
} | |||||
return basePath, nil | |||||
} | |||||
// RemoveTemporaryPath removes the temporary path | |||||
func RemoveTemporaryPath(basePath string) error { | |||||
if _, err := os.Stat(basePath); !os.IsNotExist(err) { | |||||
return os.RemoveAll(basePath) | |||||
} | |||||
return nil | |||||
} |
// Copyright 2019 The Gitea Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package models | |||||
import ( | |||||
"fmt" | |||||
"os" | |||||
"strings" | |||||
) | |||||
// PushingEnvironment returns an os environment to allow hooks to work on push | |||||
func PushingEnvironment(doer *User, repo *Repository) []string { | |||||
isWiki := "false" | |||||
if strings.HasSuffix(repo.Name, ".wiki") { | |||||
isWiki = "true" | |||||
} | |||||
sig := doer.NewGitSig() | |||||
return append(os.Environ(), | |||||
"GIT_AUTHOR_NAME="+sig.Name, | |||||
"GIT_AUTHOR_EMAIL="+sig.Email, | |||||
"GIT_COMMITTER_NAME="+sig.Name, | |||||
"GIT_COMMITTER_EMAIL="+sig.Email, | |||||
EnvRepoName+"="+repo.Name, | |||||
EnvRepoUsername+"="+repo.OwnerName, | |||||
EnvRepoIsWiki+"="+isWiki, | |||||
EnvPusherName+"="+doer.Name, | |||||
EnvPusherID+"="+fmt.Sprintf("%d", doer.ID), | |||||
ProtectedBranchRepoID+"="+fmt.Sprintf("%d", repo.ID), | |||||
"SSH_ORIGINAL_COMMAND=gitea-internal", | |||||
) | |||||
} |
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false) | go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false) | ||||
}() | }() | ||||
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name) | |||||
// Clone base repo. | // Clone base repo. | ||||
tmpBasePath := path.Join(LocalCopyPath(), "merge-"+com.ToStr(time.Now().Nanosecond())+".git") | |||||
if err := os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm); err != nil { | |||||
return fmt.Errorf("Failed to create dir %s: %v", tmpBasePath, err) | |||||
tmpBasePath, err := CreateTemporaryPath("merge") | |||||
if err != nil { | |||||
return err | |||||
} | } | ||||
defer RemoveTemporaryPath(tmpBasePath) | |||||
defer os.RemoveAll(tmpBasePath) | |||||
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name) | |||||
var stderr string | |||||
if _, stderr, err = process.GetManager().ExecTimeout(5*time.Minute, | |||||
fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath), | |||||
"git", "clone", "-s", "--no-checkout", "-b", pr.BaseBranch, baseGitRepo.Path, tmpBasePath); err != nil { | |||||
return fmt.Errorf("git clone: %s", stderr) | |||||
if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{ | |||||
Shared: true, | |||||
NoCheckout: true, | |||||
Branch: pr.BaseBranch, | |||||
}); err != nil { | |||||
return fmt.Errorf("git clone: %v", err) | |||||
} | } | ||||
remoteRepoName := "head_repo" | remoteRepoName := "head_repo" | ||||
if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil { | if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil { | ||||
return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err) | return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err) | ||||
} | } | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath), | ||||
"git", "remote", "add", remoteRepoName, headRepoPath); err != nil { | "git", "remote", "add", remoteRepoName, headRepoPath); err != nil { | ||||
return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | ||||
} | } | ||||
// Fetch head branch | // Fetch head branch | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath), | ||||
"git", "fetch", remoteRepoName); err != nil { | "git", "fetch", remoteRepoName); err != nil { | ||||
return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | ||||
return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err) | return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err) | ||||
} | } | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git config): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git config): %s", tmpBasePath), | ||||
"git", "config", "--local", "core.sparseCheckout", "true"); err != nil { | "git", "config", "--local", "core.sparseCheckout", "true"); err != nil { | ||||
return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", stderr) | return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", stderr) | ||||
} | } | ||||
// Read base branch index | // Read base branch index | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git read-tree): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git read-tree): %s", tmpBasePath), | ||||
"git", "read-tree", "HEAD"); err != nil { | "git", "read-tree", "HEAD"); err != nil { | ||||
return fmt.Errorf("git read-tree HEAD: %s", stderr) | return fmt.Errorf("git read-tree HEAD: %s", stderr) | ||||
// Merge commits. | // Merge commits. | ||||
switch mergeStyle { | switch mergeStyle { | ||||
case MergeStyleMerge: | case MergeStyleMerge: | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath), | ||||
"git", "merge", "--no-ff", "--no-commit", trackingBranch); err != nil { | "git", "merge", "--no-ff", "--no-commit", trackingBranch); err != nil { | ||||
return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr) | return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr) | ||||
} | } | ||||
sig := doer.NewGitSig() | sig := doer.NewGitSig() | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), | ||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), | "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), | ||||
"-m", message); err != nil { | "-m", message); err != nil { | ||||
} | } | ||||
case MergeStyleRebase: | case MergeStyleRebase: | ||||
// Checkout head branch | // Checkout head branch | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), | ||||
"git", "checkout", "-b", stagingBranch, trackingBranch); err != nil { | "git", "checkout", "-b", stagingBranch, trackingBranch); err != nil { | ||||
return fmt.Errorf("git checkout: %s", stderr) | return fmt.Errorf("git checkout: %s", stderr) | ||||
} | } | ||||
// Rebase before merging | // Rebase before merging | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), | ||||
"git", "rebase", "-q", pr.BaseBranch); err != nil { | "git", "rebase", "-q", pr.BaseBranch); err != nil { | ||||
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | ||||
} | } | ||||
// Checkout base branch again | // Checkout base branch again | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), | ||||
"git", "checkout", pr.BaseBranch); err != nil { | "git", "checkout", pr.BaseBranch); err != nil { | ||||
return fmt.Errorf("git checkout: %s", stderr) | return fmt.Errorf("git checkout: %s", stderr) | ||||
} | } | ||||
// Merge fast forward | // Merge fast forward | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), | ||||
"git", "merge", "--ff-only", "-q", stagingBranch); err != nil { | "git", "merge", "--ff-only", "-q", stagingBranch); err != nil { | ||||
return fmt.Errorf("git merge --ff-only [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | return fmt.Errorf("git merge --ff-only [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | ||||
} | } | ||||
case MergeStyleRebaseMerge: | case MergeStyleRebaseMerge: | ||||
// Checkout head branch | // Checkout head branch | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), | ||||
"git", "checkout", "-b", stagingBranch, trackingBranch); err != nil { | "git", "checkout", "-b", stagingBranch, trackingBranch); err != nil { | ||||
return fmt.Errorf("git checkout: %s", stderr) | return fmt.Errorf("git checkout: %s", stderr) | ||||
} | } | ||||
// Rebase before merging | // Rebase before merging | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), | ||||
"git", "rebase", "-q", pr.BaseBranch); err != nil { | "git", "rebase", "-q", pr.BaseBranch); err != nil { | ||||
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | ||||
} | } | ||||
// Checkout base branch again | // Checkout base branch again | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), | ||||
"git", "checkout", pr.BaseBranch); err != nil { | "git", "checkout", pr.BaseBranch); err != nil { | ||||
return fmt.Errorf("git checkout: %s", stderr) | return fmt.Errorf("git checkout: %s", stderr) | ||||
} | } | ||||
// Prepare merge with commit | // Prepare merge with commit | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), | ||||
"git", "merge", "--no-ff", "--no-commit", "-q", stagingBranch); err != nil { | "git", "merge", "--no-ff", "--no-commit", "-q", stagingBranch); err != nil { | ||||
return fmt.Errorf("git merge --no-ff [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | return fmt.Errorf("git merge --no-ff [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | ||||
// Set custom message and author and create merge commit | // Set custom message and author and create merge commit | ||||
sig := doer.NewGitSig() | sig := doer.NewGitSig() | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git commit): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git commit): %s", tmpBasePath), | ||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), | "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), | ||||
"-m", message); err != nil { | "-m", message); err != nil { | ||||
case MergeStyleSquash: | case MergeStyleSquash: | ||||
// Merge with squash | // Merge with squash | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath), | ||||
"git", "merge", "-q", "--squash", trackingBranch); err != nil { | "git", "merge", "-q", "--squash", trackingBranch); err != nil { | ||||
return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) | ||||
} | } | ||||
sig := pr.Issue.Poster.NewGitSig() | sig := pr.Issue.Poster.NewGitSig() | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath), | ||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), | "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), | ||||
"-m", message); err != nil { | "-m", message); err != nil { | ||||
return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle} | return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle} | ||||
} | } | ||||
env := PushingEnvironment(doer, pr.BaseRepo) | |||||
// Push back to upstream. | // Push back to upstream. | ||||
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, | |||||
if _, stderr, err := process.GetManager().ExecDirEnv(-1, tmpBasePath, | |||||
fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath), | ||||
"git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { | |||||
env, "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { | |||||
return fmt.Errorf("git push: %s", stderr) | return fmt.Errorf("git push: %s", stderr) | ||||
} | } | ||||
} | } | ||||
func (repo *Repository) deleteWiki(e Engine) error { | func (repo *Repository) deleteWiki(e Engine) error { | ||||
wikiPaths := []string{repo.WikiPath(), repo.LocalWikiPath()} | |||||
wikiPaths := []string{repo.WikiPath()} | |||||
for _, wikiPath := range wikiPaths { | for _, wikiPath := range wikiPaths { | ||||
removeAllWithNotice(e, "Delete repository wiki", wikiPath) | removeAllWithNotice(e, "Delete repository wiki", wikiPath) | ||||
} | } | ||||
return template.HTML(markup.Sanitize(string(desc))) | return template.HTML(markup.Sanitize(string(desc))) | ||||
} | } | ||||
// LocalCopyPath returns the local repository copy path. | |||||
func LocalCopyPath() string { | |||||
if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) { | |||||
return setting.Repository.Local.LocalCopyPath | |||||
} | |||||
return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath) | |||||
} | |||||
// LocalCopyPath returns the local repository copy path for the given repo. | |||||
func (repo *Repository) LocalCopyPath() string { | |||||
return path.Join(LocalCopyPath(), com.ToStr(repo.ID)) | |||||
} | |||||
// UpdateLocalCopyBranch pulls latest changes of given branch from repoPath to localPath. | |||||
// It creates a new clone if local copy does not exist. | |||||
// This function checks out target branch by default, it is safe to assume subsequent | |||||
// operations are operating against target branch when caller has confidence for no race condition. | |||||
func UpdateLocalCopyBranch(repoPath, localPath, branch string) error { | |||||
if !com.IsExist(localPath) { | |||||
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{ | |||||
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second, | |||||
Branch: branch, | |||||
}); err != nil { | |||||
return fmt.Errorf("git clone %s: %v", branch, err) | |||||
} | |||||
} else { | |||||
_, err := git.NewCommand("fetch", "origin").RunInDir(localPath) | |||||
if err != nil { | |||||
return fmt.Errorf("git fetch origin: %v", err) | |||||
} | |||||
if len(branch) > 0 { | |||||
if err := git.Checkout(localPath, git.CheckoutOptions{ | |||||
Branch: branch, | |||||
}); err != nil { | |||||
return fmt.Errorf("git checkout %s: %v", branch, err) | |||||
} | |||||
if err := git.ResetHEAD(localPath, true, "origin/"+branch); err != nil { | |||||
return fmt.Errorf("git reset --hard origin/%s: %v", branch, err) | |||||
} | |||||
} | |||||
} | |||||
return nil | |||||
} | |||||
// UpdateLocalCopyBranch makes sure local copy of repository in given branch is up-to-date. | |||||
func (repo *Repository) UpdateLocalCopyBranch(branch string) error { | |||||
return UpdateLocalCopyBranch(repo.RepoPath(), repo.LocalCopyPath(), branch) | |||||
} | |||||
// PatchPath returns corresponding patch file path of repository by given issue ID. | // PatchPath returns corresponding patch file path of repository by given issue ID. | ||||
func (repo *Repository) PatchPath(index int64) (string, error) { | func (repo *Repository) PatchPath(index int64) (string, error) { | ||||
return repo.patchPath(x, index) | return repo.patchPath(x, index) | ||||
if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { | if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { | ||||
return fmt.Errorf("rename repository directory: %v", err) | return fmt.Errorf("rename repository directory: %v", err) | ||||
} | } | ||||
removeAllWithNotice(sess, "Delete repository local copy", repo.LocalCopyPath()) | |||||
// Rename remote wiki repository to new path and delete local copy. | // Rename remote wiki repository to new path and delete local copy. | ||||
wikiPath := WikiPath(owner.Name, repo.Name) | wikiPath := WikiPath(owner.Name, repo.Name) | ||||
if com.IsExist(wikiPath) { | if com.IsExist(wikiPath) { | ||||
removeAllWithNotice(sess, "Delete repository wiki local copy", repo.LocalWikiPath()) | |||||
if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { | if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { | ||||
return fmt.Errorf("rename repository wiki: %v", err) | return fmt.Errorf("rename repository wiki: %v", err) | ||||
} | } | ||||
return fmt.Errorf("rename repository directory: %v", err) | return fmt.Errorf("rename repository directory: %v", err) | ||||
} | } | ||||
localPath := repo.LocalCopyPath() | |||||
if com.IsExist(localPath) { | |||||
_, err := git.NewCommand("remote", "set-url", "origin", newRepoPath).RunInDir(localPath) | |||||
if err != nil { | |||||
return fmt.Errorf("git remote set-url origin %s: %v", newRepoPath, err) | |||||
} | |||||
} | |||||
wikiPath := repo.WikiPath() | wikiPath := repo.WikiPath() | ||||
if com.IsExist(wikiPath) { | if com.IsExist(wikiPath) { | ||||
if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil { | if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil { | ||||
return fmt.Errorf("rename repository wiki: %v", err) | return fmt.Errorf("rename repository wiki: %v", err) | ||||
} | } | ||||
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath()) | |||||
} | } | ||||
sess := x.NewSession() | sess := x.NewSession() |
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"time" | |||||
"code.gitea.io/gitea/modules/git" | "code.gitea.io/gitea/modules/git" | ||||
"code.gitea.io/gitea/modules/setting" | |||||
"github.com/Unknwon/com" | |||||
"code.gitea.io/gitea/modules/log" | |||||
) | ) | ||||
// discardLocalRepoBranchChanges discards local commits/changes of | |||||
// given branch to make sure it is even to remote branch. | |||||
func discardLocalRepoBranchChanges(localPath, branch string) error { | |||||
if !com.IsExist(localPath) { | |||||
return nil | |||||
} | |||||
// No need to check if nothing in the repository. | |||||
if !git.IsBranchExist(localPath, branch) { | |||||
return nil | |||||
} | |||||
refName := "origin/" + branch | |||||
if err := git.ResetHEAD(localPath, true, refName); err != nil { | |||||
return fmt.Errorf("git reset --hard %s: %v", refName, err) | |||||
} | |||||
return nil | |||||
} | |||||
// DiscardLocalRepoBranchChanges discards the local repository branch changes | |||||
func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error { | |||||
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch) | |||||
} | |||||
// checkoutNewBranch checks out to a new branch from the a branch name. | |||||
func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error { | |||||
if err := git.Checkout(localPath, git.CheckoutOptions{ | |||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, | |||||
Branch: newBranch, | |||||
OldBranch: oldBranch, | |||||
}); err != nil { | |||||
return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err) | |||||
} | |||||
return nil | |||||
} | |||||
// CheckoutNewBranch checks out a new branch | |||||
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error { | |||||
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch) | |||||
} | |||||
// deleteLocalBranch deletes a branch from a local repo cache | |||||
// First checks out default branch to avoid trying to delete the currently checked out branch | |||||
func deleteLocalBranch(localPath, defaultBranch, deleteBranch string) error { | |||||
if !com.IsExist(localPath) { | |||||
return nil | |||||
} | |||||
if !git.IsBranchExist(localPath, deleteBranch) { | |||||
return nil | |||||
} | |||||
// Must NOT have branch currently checked out | |||||
// Checkout default branch first | |||||
if err := git.Checkout(localPath, git.CheckoutOptions{ | |||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, | |||||
Branch: defaultBranch, | |||||
}); err != nil { | |||||
return fmt.Errorf("git checkout %s: %v", defaultBranch, err) | |||||
} | |||||
cmd := git.NewCommand("branch") | |||||
cmd.AddArguments("-D") | |||||
cmd.AddArguments(deleteBranch) | |||||
_, err := cmd.RunInDir(localPath) | |||||
return err | |||||
} | |||||
// DeleteLocalBranch deletes a branch from the local repo | |||||
func (repo *Repository) DeleteLocalBranch(branchName string) error { | |||||
return deleteLocalBranch(repo.LocalCopyPath(), repo.DefaultBranch, branchName) | |||||
} | |||||
// CanCreateBranch returns true if repository meets the requirements for creating new branches. | // CanCreateBranch returns true if repository meets the requirements for creating new branches. | ||||
func (repo *Repository) CanCreateBranch() bool { | func (repo *Repository) CanCreateBranch() bool { | ||||
return !repo.IsMirror | return !repo.IsMirror | ||||
// CreateNewBranch creates a new repository branch | // CreateNewBranch creates a new repository branch | ||||
func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) { | func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) { | ||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | |||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | |||||
// Check if branch name can be used | // Check if branch name can be used | ||||
if err := repo.CheckBranchName(branchName); err != nil { | if err := repo.CheckBranchName(branchName); err != nil { | ||||
return err | return err | ||||
} | } | ||||
localPath := repo.LocalCopyPath() | |||||
if err = discardLocalRepoBranchChanges(localPath, oldBranchName); err != nil { | |||||
return fmt.Errorf("discardLocalRepoChanges: %v", err) | |||||
} else if err = repo.UpdateLocalCopyBranch(oldBranchName); err != nil { | |||||
return fmt.Errorf("UpdateLocalCopyBranch: %v", err) | |||||
if !git.IsBranchExist(repo.RepoPath(), oldBranchName) { | |||||
return fmt.Errorf("OldBranch: %s does not exist. Cannot create new branch from this", oldBranchName) | |||||
} | } | ||||
if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil { | |||||
return fmt.Errorf("CreateNewBranch: %v", err) | |||||
basePath, err := CreateTemporaryPath("branch-maker") | |||||
if err != nil { | |||||
return err | |||||
} | } | ||||
defer RemoveTemporaryPath(basePath) | |||||
if err = git.Push(localPath, git.PushOptions{ | |||||
Remote: "origin", | |||||
Branch: branchName, | |||||
if err := git.Clone(repo.RepoPath(), basePath, git.CloneRepoOptions{ | |||||
Bare: true, | |||||
Shared: true, | |||||
}); err != nil { | }); err != nil { | ||||
return fmt.Errorf("Push: %v", err) | |||||
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
} | } | ||||
return nil | |||||
} | |||||
gitRepo, err := git.OpenRepository(basePath) | |||||
if err != nil { | |||||
log.Error("Unable to open temporary repository: %s (%v)", basePath, err) | |||||
return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) | |||||
} | |||||
// updateLocalCopyToCommit pulls latest changes of given commit from repoPath to localPath. | |||||
// It creates a new clone if local copy does not exist. | |||||
// This function checks out target commit by default, it is safe to assume subsequent | |||||
// operations are operating against target commit when caller has confidence for no race condition. | |||||
func updateLocalCopyToCommit(repoPath, localPath, commit string) error { | |||||
if !com.IsExist(localPath) { | |||||
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{ | |||||
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second, | |||||
}); err != nil { | |||||
return fmt.Errorf("git clone: %v", err) | |||||
} | |||||
} else { | |||||
_, err := git.NewCommand("fetch", "origin").RunInDir(localPath) | |||||
if err != nil { | |||||
return fmt.Errorf("git fetch origin: %v", err) | |||||
} | |||||
if err := git.ResetHEAD(localPath, true, "HEAD"); err != nil { | |||||
return fmt.Errorf("git reset --hard HEAD: %v", err) | |||||
} | |||||
if err = gitRepo.CreateBranch(branchName, oldBranchName); err != nil { | |||||
log.Error("Unable to create branch: %s from %s. (%v)", branchName, oldBranchName, err) | |||||
return fmt.Errorf("Unable to create branch: %s from %s. (%v)", branchName, oldBranchName, err) | |||||
} | } | ||||
if err := git.Checkout(localPath, git.CheckoutOptions{ | |||||
Branch: commit, | |||||
if err = git.Push(basePath, git.PushOptions{ | |||||
Remote: "origin", | |||||
Branch: branchName, | |||||
Env: PushingEnvironment(doer, repo), | |||||
}); err != nil { | }); err != nil { | ||||
return fmt.Errorf("git checkout %s: %v", commit, err) | |||||
return fmt.Errorf("Push: %v", err) | |||||
} | } | ||||
return nil | |||||
} | |||||
// updateLocalCopyToCommit makes sure local copy of repository is at given commit. | |||||
func (repo *Repository) updateLocalCopyToCommit(commit string) error { | |||||
return updateLocalCopyToCommit(repo.RepoPath(), repo.LocalCopyPath(), commit) | |||||
return nil | |||||
} | } | ||||
// CreateNewBranchFromCommit creates a new repository branch | // CreateNewBranchFromCommit creates a new repository branch | ||||
func (repo *Repository) CreateNewBranchFromCommit(doer *User, commit, branchName string) (err error) { | func (repo *Repository) CreateNewBranchFromCommit(doer *User, commit, branchName string) (err error) { | ||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | |||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | |||||
// Check if branch name can be used | // Check if branch name can be used | ||||
if err := repo.CheckBranchName(branchName); err != nil { | if err := repo.CheckBranchName(branchName); err != nil { | ||||
return err | return err | ||||
} | } | ||||
basePath, err := CreateTemporaryPath("branch-maker") | |||||
if err != nil { | |||||
return err | |||||
} | |||||
defer RemoveTemporaryPath(basePath) | |||||
localPath := repo.LocalCopyPath() | |||||
if err := git.Clone(repo.RepoPath(), basePath, git.CloneRepoOptions{ | |||||
Bare: true, | |||||
Shared: true, | |||||
}); err != nil { | |||||
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
} | |||||
if err = repo.updateLocalCopyToCommit(commit); err != nil { | |||||
return fmt.Errorf("UpdateLocalCopyBranch: %v", err) | |||||
gitRepo, err := git.OpenRepository(basePath) | |||||
if err != nil { | |||||
log.Error("Unable to open temporary repository: %s (%v)", basePath, err) | |||||
return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) | |||||
} | } | ||||
if err = repo.CheckoutNewBranch(commit, branchName); err != nil { | |||||
return fmt.Errorf("CheckoutNewBranch: %v", err) | |||||
if err = gitRepo.CreateBranch(branchName, commit); err != nil { | |||||
log.Error("Unable to create branch: %s from %s. (%v)", branchName, commit, err) | |||||
return fmt.Errorf("Unable to create branch: %s from %s. (%v)", branchName, commit, err) | |||||
} | } | ||||
if err = git.Push(localPath, git.PushOptions{ | |||||
if err = git.Push(basePath, git.PushOptions{ | |||||
Remote: "origin", | Remote: "origin", | ||||
Branch: branchName, | Branch: branchName, | ||||
Env: PushingEnvironment(doer, repo), | |||||
}); err != nil { | }); err != nil { | ||||
return fmt.Errorf("Push: %v", err) | return fmt.Errorf("Push: %v", err) | ||||
} | } |
package models | package models | ||||
import ( | import ( | ||||
"path" | |||||
"testing" | "testing" | ||||
"code.gitea.io/gitea/modules/markup" | "code.gitea.io/gitea/modules/markup" | ||||
"code.gitea.io/gitea/modules/setting" | |||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/stretchr/testify/assert" | "github.com/stretchr/testify/assert" | ||||
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL()) | assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL()) | ||||
} | } | ||||
func TestRepoLocalCopyPath(t *testing.T) { | |||||
assert.NoError(t, PrepareTestDatabase()) | |||||
repo, err := GetRepositoryByID(10) | |||||
assert.NoError(t, err) | |||||
assert.NotNil(t, repo) | |||||
// test default | |||||
repoID := com.ToStr(repo.ID) | |||||
expected := path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath, repoID) | |||||
assert.Equal(t, expected, repo.LocalCopyPath()) | |||||
// test absolute setting | |||||
tempPath := "/tmp/gitea/local-copy-path" | |||||
expected = path.Join(tempPath, repoID) | |||||
setting.Repository.Local.LocalCopyPath = tempPath | |||||
assert.Equal(t, expected, repo.LocalCopyPath()) | |||||
} | |||||
func TestTransferOwnership(t *testing.T) { | func TestTransferOwnership(t *testing.T) { | ||||
assert.NoError(t, PrepareTestDatabase()) | assert.NoError(t, PrepareTestDatabase()) | ||||
return fmt.Errorf("ChangeUsernameInPullRequests: %v", err) | return fmt.Errorf("ChangeUsernameInPullRequests: %v", err) | ||||
} | } | ||||
// Delete all local copies of repository wiki that user owns. | |||||
if err = x.BufferSize(setting.IterateBufferSize). | |||||
Where("owner_id=?", u.ID). | |||||
Iterate(new(Repository), func(idx int, bean interface{}) error { | |||||
repo := bean.(*Repository) | |||||
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath()) | |||||
return nil | |||||
}); err != nil { | |||||
return fmt.Errorf("Delete repository wiki local copy: %v", err) | |||||
} | |||||
// Do not fail if directory does not exist | // Do not fail if directory does not exist | ||||
if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { | if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { | ||||
return fmt.Errorf("Rename user directory: %v", err) | return fmt.Errorf("Rename user directory: %v", err) |
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"io/ioutil" | |||||
"net/url" | "net/url" | ||||
"os" | "os" | ||||
"path" | |||||
"path/filepath" | "path/filepath" | ||||
"strings" | "strings" | ||||
"code.gitea.io/gitea/modules/git" | "code.gitea.io/gitea/modules/git" | ||||
"code.gitea.io/gitea/modules/setting" | |||||
"code.gitea.io/gitea/modules/log" | |||||
"code.gitea.io/gitea/modules/sync" | "code.gitea.io/gitea/modules/sync" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
return nil | return nil | ||||
} | } | ||||
// LocalWikiPath returns the local wiki repository copy path. | |||||
func LocalWikiPath() string { | |||||
if filepath.IsAbs(setting.Repository.Local.LocalWikiPath) { | |||||
return setting.Repository.Local.LocalWikiPath | |||||
} | |||||
return path.Join(setting.AppDataPath, setting.Repository.Local.LocalWikiPath) | |||||
} | |||||
// LocalWikiPath returns the path to the local wiki repository (?). | |||||
func (repo *Repository) LocalWikiPath() string { | |||||
return path.Join(LocalWikiPath(), com.ToStr(repo.ID)) | |||||
} | |||||
// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date. | |||||
func (repo *Repository) updateLocalWiki() error { | |||||
// Don't pass branch name here because it fails to clone and | |||||
// checkout to a specific branch when wiki is an empty repository. | |||||
var branch = "" | |||||
if com.IsExist(repo.LocalWikiPath()) { | |||||
branch = "master" | |||||
} | |||||
return UpdateLocalCopyBranch(repo.WikiPath(), repo.LocalWikiPath(), branch) | |||||
} | |||||
func discardLocalWikiChanges(localPath string) error { | |||||
return discardLocalRepoBranchChanges(localPath, "master") | |||||
} | |||||
// nameAllowed checks if a wiki name is allowed | // nameAllowed checks if a wiki name is allowed | ||||
func nameAllowed(name string) error { | func nameAllowed(name string) error { | ||||
for _, reservedName := range reservedWikiNames { | for _, reservedName := range reservedWikiNames { | ||||
if err = nameAllowed(newWikiName); err != nil { | if err = nameAllowed(newWikiName); err != nil { | ||||
return err | return err | ||||
} | } | ||||
wikiWorkingPool.CheckIn(com.ToStr(repo.ID)) | wikiWorkingPool.CheckIn(com.ToStr(repo.ID)) | ||||
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID)) | defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID)) | ||||
return fmt.Errorf("InitWiki: %v", err) | return fmt.Errorf("InitWiki: %v", err) | ||||
} | } | ||||
localPath := repo.LocalWikiPath() | |||||
if err = discardLocalWikiChanges(localPath); err != nil { | |||||
return fmt.Errorf("discardLocalWikiChanges: %v", err) | |||||
} else if err = repo.updateLocalWiki(); err != nil { | |||||
return fmt.Errorf("UpdateLocalWiki: %v", err) | |||||
hasMasterBranch := git.IsBranchExist(repo.WikiPath(), "master") | |||||
basePath, err := CreateTemporaryPath("update-wiki") | |||||
if err != nil { | |||||
return err | |||||
} | } | ||||
defer RemoveTemporaryPath(basePath) | |||||
newWikiPath := path.Join(localPath, WikiNameToFilename(newWikiName)) | |||||
cloneOpts := git.CloneRepoOptions{ | |||||
Bare: true, | |||||
Shared: true, | |||||
} | |||||
if hasMasterBranch { | |||||
cloneOpts.Branch = "master" | |||||
} | |||||
// If not a new file, show perform update not create. | |||||
if err := git.Clone(repo.WikiPath(), basePath, cloneOpts); err != nil { | |||||
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
} | |||||
gitRepo, err := git.OpenRepository(basePath) | |||||
if err != nil { | |||||
log.Error("Unable to open temporary repository: %s (%v)", basePath, err) | |||||
return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) | |||||
} | |||||
if hasMasterBranch { | |||||
if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil { | |||||
log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err) | |||||
return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err) | |||||
} | |||||
} | |||||
newWikiPath := WikiNameToFilename(newWikiName) | |||||
if isNew { | if isNew { | ||||
if com.IsExist(newWikiPath) { | |||||
return ErrWikiAlreadyExist{newWikiPath} | |||||
filesInIndex, err := gitRepo.LsFiles(newWikiPath) | |||||
if err != nil { | |||||
log.Error("%v", err) | |||||
return err | |||||
} | |||||
for _, file := range filesInIndex { | |||||
if file == newWikiPath { | |||||
return ErrWikiAlreadyExist{newWikiPath} | |||||
} | |||||
} | } | ||||
} else { | } else { | ||||
oldWikiPath := path.Join(localPath, WikiNameToFilename(oldWikiName)) | |||||
if err := os.Remove(oldWikiPath); err != nil { | |||||
return fmt.Errorf("Failed to remove %s: %v", oldWikiPath, err) | |||||
oldWikiPath := WikiNameToFilename(oldWikiName) | |||||
filesInIndex, err := gitRepo.LsFiles(oldWikiPath) | |||||
if err != nil { | |||||
log.Error("%v", err) | |||||
return err | |||||
} | |||||
found := false | |||||
for _, file := range filesInIndex { | |||||
if file == oldWikiPath { | |||||
found = true | |||||
break | |||||
} | |||||
} | |||||
if found { | |||||
err := gitRepo.RemoveFilesFromIndex(oldWikiPath) | |||||
if err != nil { | |||||
log.Error("%v", err) | |||||
return err | |||||
} | |||||
} | } | ||||
} | } | ||||
// SECURITY: if new file is a symlink to non-exist critical file, | |||||
// attack content can be written to the target file (e.g. authorized_keys2) | |||||
// as a new page operation. | |||||
// So we want to make sure the symlink is removed before write anything. | |||||
// The new file we created will be in normal text format. | |||||
if err = os.RemoveAll(newWikiPath); err != nil { | |||||
// FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here | |||||
objectHash, err := gitRepo.HashObject(strings.NewReader(content)) | |||||
if err != nil { | |||||
log.Error("%v", err) | |||||
return err | return err | ||||
} | } | ||||
if err = ioutil.WriteFile(newWikiPath, []byte(content), 0666); err != nil { | |||||
return fmt.Errorf("WriteFile: %v", err) | |||||
if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil { | |||||
log.Error("%v", err) | |||||
return err | |||||
} | } | ||||
if len(message) == 0 { | |||||
message = "Update page '" + newWikiName + "'" | |||||
tree, err := gitRepo.WriteTree() | |||||
if err != nil { | |||||
log.Error("%v", err) | |||||
return err | |||||
} | } | ||||
if err = git.AddChanges(localPath, true); err != nil { | |||||
return fmt.Errorf("AddChanges: %v", err) | |||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ | |||||
Committer: doer.NewGitSig(), | |||||
Message: message, | |||||
}); err != nil { | |||||
return fmt.Errorf("CommitChanges: %v", err) | |||||
} else if err = git.Push(localPath, git.PushOptions{ | |||||
commitTreeOpts := git.CommitTreeOpts{ | |||||
Message: message, | |||||
} | |||||
if hasMasterBranch { | |||||
commitTreeOpts.Parents = []string{"HEAD"} | |||||
} | |||||
commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, commitTreeOpts) | |||||
if err != nil { | |||||
log.Error("%v", err) | |||||
return err | |||||
} | |||||
if err := git.Push(basePath, git.PushOptions{ | |||||
Remote: "origin", | Remote: "origin", | ||||
Branch: "master", | |||||
Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"), | |||||
Env: PushingEnvironment(doer, repo), | |||||
}); err != nil { | }); err != nil { | ||||
log.Error("%v", err) | |||||
return fmt.Errorf("Push: %v", err) | return fmt.Errorf("Push: %v", err) | ||||
} | } | ||||
wikiWorkingPool.CheckIn(com.ToStr(repo.ID)) | wikiWorkingPool.CheckIn(com.ToStr(repo.ID)) | ||||
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID)) | defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID)) | ||||
localPath := repo.LocalWikiPath() | |||||
if err = discardLocalWikiChanges(localPath); err != nil { | |||||
return fmt.Errorf("discardLocalWikiChanges: %v", err) | |||||
} else if err = repo.updateLocalWiki(); err != nil { | |||||
return fmt.Errorf("UpdateLocalWiki: %v", err) | |||||
if err = repo.InitWiki(); err != nil { | |||||
return fmt.Errorf("InitWiki: %v", err) | |||||
} | |||||
basePath, err := CreateTemporaryPath("update-wiki") | |||||
if err != nil { | |||||
return err | |||||
} | } | ||||
defer RemoveTemporaryPath(basePath) | |||||
filename := path.Join(localPath, WikiNameToFilename(wikiName)) | |||||
if err := git.Clone(repo.WikiPath(), basePath, git.CloneRepoOptions{ | |||||
Bare: true, | |||||
Shared: true, | |||||
Branch: "master", | |||||
}); err != nil { | |||||
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
} | |||||
if err := os.Remove(filename); err != nil { | |||||
return fmt.Errorf("Failed to remove %s: %v", filename, err) | |||||
gitRepo, err := git.OpenRepository(basePath) | |||||
if err != nil { | |||||
log.Error("Unable to open temporary repository: %s (%v)", basePath, err) | |||||
return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) | |||||
} | } | ||||
if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil { | |||||
log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err) | |||||
return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err) | |||||
} | |||||
wikiPath := WikiNameToFilename(wikiName) | |||||
filesInIndex, err := gitRepo.LsFiles(wikiPath) | |||||
found := false | |||||
for _, file := range filesInIndex { | |||||
if file == wikiPath { | |||||
found = true | |||||
break | |||||
} | |||||
} | |||||
if found { | |||||
err := gitRepo.RemoveFilesFromIndex(wikiPath) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
} else { | |||||
return os.ErrNotExist | |||||
} | |||||
// FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here | |||||
tree, err := gitRepo.WriteTree() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
message := "Delete page '" + wikiName + "'" | message := "Delete page '" + wikiName + "'" | ||||
if err = git.AddChanges(localPath, true); err != nil { | |||||
return fmt.Errorf("AddChanges: %v", err) | |||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ | |||||
Committer: doer.NewGitSig(), | |||||
Message: message, | |||||
}); err != nil { | |||||
return fmt.Errorf("CommitChanges: %v", err) | |||||
} else if err = git.Push(localPath, git.PushOptions{ | |||||
commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, git.CommitTreeOpts{ | |||||
Message: message, | |||||
Parents: []string{"HEAD"}, | |||||
}) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if err := git.Push(basePath, git.PushOptions{ | |||||
Remote: "origin", | Remote: "origin", | ||||
Branch: "master", | |||||
Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"), | |||||
Env: PushingEnvironment(doer, repo), | |||||
}); err != nil { | }); err != nil { | ||||
return fmt.Errorf("Push: %v", err) | return fmt.Errorf("Push: %v", err) | ||||
} | } |
package models | package models | ||||
import ( | import ( | ||||
"path" | |||||
"path/filepath" | "path/filepath" | ||||
"testing" | "testing" | ||||
"code.gitea.io/gitea/modules/git" | |||||
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"github.com/Unknwon/com" | |||||
"github.com/stretchr/testify/assert" | "github.com/stretchr/testify/assert" | ||||
) | ) | ||||
assert.True(t, repo2.HasWiki()) | assert.True(t, repo2.HasWiki()) | ||||
} | } | ||||
func TestRepository_LocalWikiPath(t *testing.T) { | |||||
PrepareTestEnv(t) | |||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) | |||||
expected := filepath.Join(setting.AppDataPath, setting.Repository.Local.LocalWikiPath, "1") | |||||
assert.Equal(t, expected, repo.LocalWikiPath()) | |||||
} | |||||
func TestRepository_AddWikiPage(t *testing.T) { | func TestRepository_AddWikiPage(t *testing.T) { | ||||
assert.NoError(t, PrepareTestDatabase()) | assert.NoError(t, PrepareTestDatabase()) | ||||
const wikiContent = "This is the wiki content" | const wikiContent = "This is the wiki content" | ||||
t.Run("test wiki exist: "+wikiName, func(t *testing.T) { | t.Run("test wiki exist: "+wikiName, func(t *testing.T) { | ||||
t.Parallel() | t.Parallel() | ||||
assert.NoError(t, repo.AddWikiPage(doer, wikiName, wikiContent, commitMsg)) | assert.NoError(t, repo.AddWikiPage(doer, wikiName, wikiContent, commitMsg)) | ||||
expectedPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(wikiName)) | |||||
assert.True(t, com.IsExist(expectedPath)) | |||||
// Now need to show that the page has been added: | |||||
gitRepo, err := git.OpenRepository(repo.WikiPath()) | |||||
assert.NoError(t, err) | |||||
masterTree, err := gitRepo.GetTree("master") | |||||
assert.NoError(t, err) | |||||
wikiPath := WikiNameToFilename(wikiName) | |||||
entry, err := masterTree.GetTreeEntryByPath(wikiPath) | |||||
assert.NoError(t, err) | |||||
assert.Equal(t, wikiPath, entry.Name(), "%s not addded correctly", wikiName) | |||||
}) | }) | ||||
} | } | ||||
} { | } { | ||||
PrepareTestEnv(t) | PrepareTestEnv(t) | ||||
assert.NoError(t, repo.EditWikiPage(doer, "Home", newWikiName, newWikiContent, commitMsg)) | assert.NoError(t, repo.EditWikiPage(doer, "Home", newWikiName, newWikiContent, commitMsg)) | ||||
newPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(newWikiName)) | |||||
assert.True(t, com.IsExist(newPath)) | |||||
// Now need to show that the page has been added: | |||||
gitRepo, err := git.OpenRepository(repo.WikiPath()) | |||||
assert.NoError(t, err) | |||||
masterTree, err := gitRepo.GetTree("master") | |||||
assert.NoError(t, err) | |||||
wikiPath := WikiNameToFilename(newWikiName) | |||||
entry, err := masterTree.GetTreeEntryByPath(wikiPath) | |||||
assert.NoError(t, err) | |||||
assert.Equal(t, wikiPath, entry.Name(), "%s not editted correctly", newWikiName) | |||||
if newWikiName != "Home" { | if newWikiName != "Home" { | ||||
oldPath := path.Join(repo.LocalWikiPath(), "Home.md") | |||||
assert.False(t, com.IsExist(oldPath)) | |||||
_, err := masterTree.GetTreeEntryByPath("Home.md") | |||||
assert.Error(t, err) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) | repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) | ||||
doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||
assert.NoError(t, repo.DeleteWikiPage(doer, "Home")) | assert.NoError(t, repo.DeleteWikiPage(doer, "Home")) | ||||
wikiPath := path.Join(repo.LocalWikiPath(), "Home.md") | |||||
assert.False(t, com.IsExist(wikiPath)) | |||||
// Now need to show that the page has been added: | |||||
gitRepo, err := git.OpenRepository(repo.WikiPath()) | |||||
assert.NoError(t, err) | |||||
masterTree, err := gitRepo.GetTree("master") | |||||
assert.NoError(t, err) | |||||
wikiPath := WikiNameToFilename("Home") | |||||
_, err = masterTree.GetTreeEntryByPath(wikiPath) | |||||
assert.Error(t, err) | |||||
} | } |
return c | return c | ||||
} | } | ||||
// RunInDirTimeoutPipeline executes the command in given directory with given timeout, | |||||
// RunInDirTimeoutEnvPipeline executes the command in given directory with given timeout, | |||||
// it pipes stdout and stderr to given io.Writer. | // it pipes stdout and stderr to given io.Writer. | ||||
func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error { | |||||
func (c *Command) RunInDirTimeoutEnvPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer) error { | |||||
return c.RunInDirTimeoutEnvFullPipeline(env, timeout, dir, stdout, stderr, nil) | |||||
} | |||||
// RunInDirTimeoutEnvFullPipeline executes the command in given directory with given timeout, | |||||
// it pipes stdout and stderr to given io.Writer and passes in an io.Reader as stdin. | |||||
func (c *Command) RunInDirTimeoutEnvFullPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error { | |||||
if timeout == -1 { | if timeout == -1 { | ||||
timeout = DefaultCommandExecutionTimeout | timeout = DefaultCommandExecutionTimeout | ||||
} | } | ||||
defer cancel() | defer cancel() | ||||
cmd := exec.CommandContext(ctx, c.name, c.args...) | cmd := exec.CommandContext(ctx, c.name, c.args...) | ||||
cmd.Env = env | |||||
cmd.Dir = dir | cmd.Dir = dir | ||||
cmd.Stdout = stdout | cmd.Stdout = stdout | ||||
cmd.Stderr = stderr | cmd.Stderr = stderr | ||||
cmd.Stdin = stdin | |||||
if err := cmd.Start(); err != nil { | if err := cmd.Start(); err != nil { | ||||
return err | return err | ||||
} | } | ||||
return ctx.Err() | return ctx.Err() | ||||
} | } | ||||
// RunInDirTimeoutPipeline executes the command in given directory with given timeout, | |||||
// it pipes stdout and stderr to given io.Writer. | |||||
func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error { | |||||
return c.RunInDirTimeoutEnvPipeline(nil, timeout, dir, stdout, stderr) | |||||
} | |||||
// RunInDirTimeoutFullPipeline executes the command in given directory with given timeout, | |||||
// it pipes stdout and stderr to given io.Writer, and stdin from the given io.Reader | |||||
func (c *Command) RunInDirTimeoutFullPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error { | |||||
return c.RunInDirTimeoutEnvFullPipeline(nil, timeout, dir, stdout, stderr, stdin) | |||||
} | |||||
// RunInDirTimeout executes the command in given directory with given timeout, | // RunInDirTimeout executes the command in given directory with given timeout, | ||||
// and returns stdout in []byte and error (combined with stderr). | // and returns stdout in []byte and error (combined with stderr). | ||||
func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) { | func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) { | ||||
return c.RunInDirTimeoutEnv(nil, timeout, dir) | |||||
} | |||||
// RunInDirTimeoutEnv executes the command in given directory with given timeout, | |||||
// and returns stdout in []byte and error (combined with stderr). | |||||
func (c *Command) RunInDirTimeoutEnv(env []string, timeout time.Duration, dir string) ([]byte, error) { | |||||
stdout := new(bytes.Buffer) | stdout := new(bytes.Buffer) | ||||
stderr := new(bytes.Buffer) | stderr := new(bytes.Buffer) | ||||
if err := c.RunInDirTimeoutPipeline(timeout, dir, stdout, stderr); err != nil { | |||||
if err := c.RunInDirTimeoutEnvPipeline(env, timeout, dir, stdout, stderr); err != nil { | |||||
return nil, concatenateError(err, stderr.String()) | return nil, concatenateError(err, stderr.String()) | ||||
} | } | ||||
// RunInDirPipeline executes the command in given directory, | // RunInDirPipeline executes the command in given directory, | ||||
// it pipes stdout and stderr to given io.Writer. | // it pipes stdout and stderr to given io.Writer. | ||||
func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error { | func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error { | ||||
return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr) | |||||
return c.RunInDirFullPipeline(dir, stdout, stderr, nil) | |||||
} | |||||
// RunInDirFullPipeline executes the command in given directory, | |||||
// it pipes stdout and stderr to given io.Writer. | |||||
func (c *Command) RunInDirFullPipeline(dir string, stdout, stderr io.Writer, stdin io.Reader) error { | |||||
return c.RunInDirTimeoutFullPipeline(-1, dir, stdout, stderr, stdin) | |||||
} | } | ||||
// RunInDirBytes executes the command in given directory | // RunInDirBytes executes the command in given directory | ||||
// RunInDir executes the command in given directory | // RunInDir executes the command in given directory | ||||
// and returns stdout in string and error (combined with stderr). | // and returns stdout in string and error (combined with stderr). | ||||
func (c *Command) RunInDir(dir string) (string, error) { | func (c *Command) RunInDir(dir string) (string, error) { | ||||
stdout, err := c.RunInDirTimeout(-1, dir) | |||||
return c.RunInDirWithEnv(dir, nil) | |||||
} | |||||
// RunInDirWithEnv executes the command in given directory | |||||
// and returns stdout in string and error (combined with stderr). | |||||
func (c *Command) RunInDirWithEnv(dir string, env []string) (string, error) { | |||||
stdout, err := c.RunInDirTimeoutEnv(env, -1, dir) | |||||
if err != nil { | if err != nil { | ||||
return "", err | return "", err | ||||
} | } |
// CloneRepoOptions options when clone a repository | // CloneRepoOptions options when clone a repository | ||||
type CloneRepoOptions struct { | type CloneRepoOptions struct { | ||||
Timeout time.Duration | |||||
Mirror bool | |||||
Bare bool | |||||
Quiet bool | |||||
Branch string | |||||
Timeout time.Duration | |||||
Mirror bool | |||||
Bare bool | |||||
Quiet bool | |||||
Branch string | |||||
Shared bool | |||||
NoCheckout bool | |||||
} | } | ||||
// Clone clones original repository to target path. | // Clone clones original repository to target path. | ||||
if opts.Quiet { | if opts.Quiet { | ||||
cmd.AddArguments("--quiet") | cmd.AddArguments("--quiet") | ||||
} | } | ||||
if opts.Shared { | |||||
cmd.AddArguments("-s") | |||||
} | |||||
if opts.NoCheckout { | |||||
cmd.AddArguments("--no-checkout") | |||||
} | |||||
if len(opts.Branch) > 0 { | if len(opts.Branch) > 0 { | ||||
cmd.AddArguments("-b", opts.Branch) | cmd.AddArguments("-b", opts.Branch) | ||||
} | } | ||||
cmd.AddArguments(from, to) | |||||
cmd.AddArguments("--", from, to) | |||||
if opts.Timeout <= 0 { | if opts.Timeout <= 0 { | ||||
opts.Timeout = -1 | opts.Timeout = -1 | ||||
Remote string | Remote string | ||||
Branch string | Branch string | ||||
Force bool | Force bool | ||||
Env []string | |||||
} | } | ||||
// Push pushs local commits to given remote branch. | // Push pushs local commits to given remote branch. | ||||
cmd.AddArguments("-f") | cmd.AddArguments("-f") | ||||
} | } | ||||
cmd.AddArguments(opts.Remote, opts.Branch) | cmd.AddArguments(opts.Remote, opts.Branch) | ||||
_, err := cmd.RunInDir(repoPath) | |||||
_, err := cmd.RunInDirWithEnv(repoPath, opts.Env) | |||||
return err | return err | ||||
} | } | ||||
// IsReferenceExist returns true if given reference exists in the repository. | // IsReferenceExist returns true if given reference exists in the repository. | ||||
func IsReferenceExist(repoPath, name string) bool { | func IsReferenceExist(repoPath, name string) bool { | ||||
_, err := NewCommand("show-ref", "--verify", name).RunInDir(repoPath) | |||||
_, err := NewCommand("show-ref", "--verify", "--", name).RunInDir(repoPath) | |||||
return err == nil | return err == nil | ||||
} | } | ||||
} | } | ||||
// CreateBranch create a new branch | // CreateBranch create a new branch | ||||
func (repo *Repository) CreateBranch(branch, newBranch string) error { | |||||
func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { | |||||
cmd := NewCommand("branch") | cmd := NewCommand("branch") | ||||
cmd.AddArguments(branch, newBranch) | |||||
cmd.AddArguments("--", branch, oldbranchOrCommit) | |||||
_, err := cmd.RunInDir(repo.Path) | _, err := cmd.RunInDir(repo.Path) | ||||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package git | |||||
import ( | |||||
"bytes" | |||||
"strings" | |||||
) | |||||
// ReadTreeToIndex reads a treeish to the index | |||||
func (repo *Repository) ReadTreeToIndex(treeish string) error { | |||||
if len(treeish) != 40 { | |||||
res, err := NewCommand("rev-parse", treeish).RunInDir(repo.Path) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if len(res) > 0 { | |||||
treeish = res[:len(res)-1] | |||||
} | |||||
} | |||||
id, err := NewIDFromString(treeish) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return repo.readTreeToIndex(id) | |||||
} | |||||
func (repo *Repository) readTreeToIndex(id SHA1) error { | |||||
_, err := NewCommand("read-tree", id.String()).RunInDir(repo.Path) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
// EmptyIndex empties the index | |||||
func (repo *Repository) EmptyIndex() error { | |||||
_, err := NewCommand("read-tree", "--empty").RunInDir(repo.Path) | |||||
return err | |||||
} | |||||
// LsFiles checks if the given filenames are in the index | |||||
func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { | |||||
cmd := NewCommand("ls-files", "-z", "--") | |||||
for _, arg := range filenames { | |||||
if arg != "" { | |||||
cmd.AddArguments(arg) | |||||
} | |||||
} | |||||
res, err := cmd.RunInDirBytes(repo.Path) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
filelist := make([]string, 0, len(filenames)) | |||||
for _, line := range bytes.Split(res, []byte{'\000'}) { | |||||
filelist = append(filelist, string(line)) | |||||
} | |||||
return filelist, err | |||||
} | |||||
// RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present. | |||||
func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { | |||||
cmd := NewCommand("update-index", "--remove", "-z", "--index-info") | |||||
stdout := new(bytes.Buffer) | |||||
stderr := new(bytes.Buffer) | |||||
buffer := new(bytes.Buffer) | |||||
for _, file := range filenames { | |||||
if file != "" { | |||||
buffer.WriteString("0 0000000000000000000000000000000000000000\t") | |||||
buffer.WriteString(file) | |||||
buffer.WriteByte('\000') | |||||
} | |||||
} | |||||
return cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, bytes.NewReader(buffer.Bytes())) | |||||
} | |||||
// AddObjectToIndex adds the provided object hash to the index at the provided filename | |||||
func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error { | |||||
cmd := NewCommand("update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename) | |||||
_, err := cmd.RunInDir(repo.Path) | |||||
return err | |||||
} | |||||
// WriteTree writes the current index as a tree to the object db and returns its hash | |||||
func (repo *Repository) WriteTree() (*Tree, error) { | |||||
res, err := NewCommand("write-tree").RunInDir(repo.Path) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
id, err := NewIDFromString(strings.TrimSpace(res)) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return NewTree(repo, id), nil | |||||
} |
package git | package git | ||||
import ( | |||||
"bytes" | |||||
"io" | |||||
"strings" | |||||
) | |||||
// ObjectType git object type | // ObjectType git object type | ||||
type ObjectType string | type ObjectType string | ||||
// ObjectTag tag object type | // ObjectTag tag object type | ||||
ObjectTag ObjectType = "tag" | ObjectTag ObjectType = "tag" | ||||
) | ) | ||||
// HashObject takes a reader and returns SHA1 hash for that reader | |||||
func (repo *Repository) HashObject(reader io.Reader) (SHA1, error) { | |||||
idStr, err := repo.hashObject(reader) | |||||
if err != nil { | |||||
return SHA1{}, err | |||||
} | |||||
return NewIDFromString(idStr) | |||||
} | |||||
func (repo *Repository) hashObject(reader io.Reader) (string, error) { | |||||
cmd := NewCommand("hash-object", "-w", "--stdin") | |||||
stdout := new(bytes.Buffer) | |||||
stderr := new(bytes.Buffer) | |||||
err := cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, reader) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
return strings.TrimSpace(stdout.String()), nil | |||||
} |
package git | package git | ||||
import ( | import ( | ||||
"fmt" | |||||
"os" | |||||
"strings" | |||||
"time" | |||||
"gopkg.in/src-d/go-git.v4/plumbing" | "gopkg.in/src-d/go-git.v4/plumbing" | ||||
) | ) | ||||
treeObject.ResolvedID = resolvedID | treeObject.ResolvedID = resolvedID | ||||
return treeObject, nil | return treeObject, nil | ||||
} | } | ||||
// CommitTreeOpts represents the possible options to CommitTree | |||||
type CommitTreeOpts struct { | |||||
Parents []string | |||||
Message string | |||||
KeyID string | |||||
NoGPGSign bool | |||||
} | |||||
// 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) { | |||||
commitTimeStr := time.Now().Format(time.UnixDate) | |||||
// Because this may call hooks we should pass in the environment | |||||
env := append(os.Environ(), | |||||
"GIT_AUTHOR_NAME="+sig.Name, | |||||
"GIT_AUTHOR_EMAIL="+sig.Email, | |||||
"GIT_AUTHOR_DATE="+commitTimeStr, | |||||
"GIT_COMMITTER_NAME="+sig.Name, | |||||
"GIT_COMMITTER_EMAIL="+sig.Email, | |||||
"GIT_COMMITTER_DATE="+commitTimeStr, | |||||
) | |||||
cmd := NewCommand("commit-tree", tree.ID.String()) | |||||
for _, parent := range opts.Parents { | |||||
cmd.AddArguments("-p", parent) | |||||
} | |||||
cmd.AddArguments("-m", opts.Message) | |||||
if opts.KeyID != "" { | |||||
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID)) | |||||
} | |||||
if opts.NoGPGSign { | |||||
cmd.AddArguments("--no-gpg-sign") | |||||
} | |||||
res, err := cmd.RunInDirWithEnv(repo.Path, env) | |||||
if err != nil { | |||||
return SHA1{}, err | |||||
} | |||||
return NewIDFromString(strings.TrimSpace(res)) | |||||
} |
"io" | "io" | ||||
"os" | "os" | ||||
"os/exec" | "os/exec" | ||||
"path" | |||||
"regexp" | "regexp" | ||||
"strings" | "strings" | ||||
"time" | "time" | ||||
"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/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/Unknwon/com" | |||||
) | ) | ||||
// 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 | ||||
// NewTemporaryUploadRepository creates a new temporary upload repository | // NewTemporaryUploadRepository creates a new temporary upload repository | ||||
func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepository, error) { | func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepository, error) { | ||||
timeStr := com.ToStr(time.Now().Nanosecond()) // SHOULD USE SOMETHING UNIQUE | |||||
basePath := path.Join(models.LocalCopyPath(), "upload-"+timeStr+".git") | |||||
if err := os.MkdirAll(path.Dir(basePath), os.ModePerm); err != nil { | |||||
return nil, fmt.Errorf("failed to create dir %s: %v", basePath, err) | |||||
} | |||||
if repo.RepoPath() == "" { | |||||
return nil, fmt.Errorf("no path to repository on system") | |||||
basePath, err := models.CreateTemporaryPath("upload") | |||||
if err != nil { | |||||
return nil, err | |||||
} | } | ||||
t := &TemporaryUploadRepository{repo: repo, basePath: basePath} | t := &TemporaryUploadRepository{repo: repo, basePath: basePath} | ||||
return t, nil | return t, nil | ||||
// Close the repository cleaning up all files | // Close the repository cleaning up all files | ||||
func (t *TemporaryUploadRepository) Close() { | func (t *TemporaryUploadRepository) Close() { | ||||
if _, err := os.Stat(t.basePath); !os.IsNotExist(err) { | |||||
os.RemoveAll(t.basePath) | |||||
if err := models.RemoveTemporaryPath(t.basePath); err != nil { | |||||
log.Error("Failed to remove temporary path %s: %v", t.basePath, err) | |||||
} | } | ||||
} | } | ||||
// Push the provided commitHash to the repository branch by the provided user | // Push the provided commitHash to the repository branch by the provided user | ||||
func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error { | func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error { | ||||
isWiki := "false" | |||||
if strings.HasSuffix(t.repo.Name, ".wiki") { | |||||
isWiki = "true" | |||||
} | |||||
sig := doer.NewGitSig() | |||||
// FIXME: Should we add SSH_ORIGINAL_COMMAND to this | |||||
// Because calls hooks we need to pass in the environment | // Because calls hooks we need to pass in the environment | ||||
env := append(os.Environ(), | |||||
"GIT_AUTHOR_NAME="+sig.Name, | |||||
"GIT_AUTHOR_EMAIL="+sig.Email, | |||||
"GIT_COMMITTER_NAME="+sig.Name, | |||||
"GIT_COMMITTER_EMAIL="+sig.Email, | |||||
models.EnvRepoName+"="+t.repo.Name, | |||||
models.EnvRepoUsername+"="+t.repo.OwnerName, | |||||
models.EnvRepoIsWiki+"="+isWiki, | |||||
models.EnvPusherName+"="+doer.Name, | |||||
models.EnvPusherID+"="+fmt.Sprintf("%d", doer.ID), | |||||
models.ProtectedBranchRepoID+"="+fmt.Sprintf("%d", t.repo.ID), | |||||
) | |||||
env := models.PushingEnvironment(doer, t.repo) | |||||
if _, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute, | if _, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute, | ||||
t.basePath, | t.basePath, |
// Copyright 2019 The Gitea Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package repofiles | |||||
import ( | |||||
"testing" | |||||
"time" | |||||
"code.gitea.io/gitea/models" | |||||
"code.gitea.io/gitea/modules/git" | |||||
api "code.gitea.io/gitea/modules/structs" | |||||
"code.gitea.io/gitea/modules/test" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
func getCreateRepoFileOptions(repo *models.Repository) *UpdateRepoFileOptions { | |||||
return &UpdateRepoFileOptions{ | |||||
OldBranch: repo.DefaultBranch, | |||||
NewBranch: repo.DefaultBranch, | |||||
TreePath: "new/file.txt", | |||||
Message: "Creates new/file.txt", | |||||
Content: "This is a NEW file", | |||||
IsNewFile: true, | |||||
Author: nil, | |||||
Committer: nil, | |||||
} | |||||
} | |||||
func getUpdateRepoFileOptions(repo *models.Repository) *UpdateRepoFileOptions { | |||||
return &UpdateRepoFileOptions{ | |||||
OldBranch: repo.DefaultBranch, | |||||
NewBranch: repo.DefaultBranch, | |||||
TreePath: "README.md", | |||||
Message: "Updates README.md", | |||||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", | |||||
Content: "This is UPDATED content for the README file", | |||||
IsNewFile: false, | |||||
Author: nil, | |||||
Committer: nil, | |||||
} | |||||
} | |||||
func getExpectedFileResponseForCreate(commitID string) *api.FileResponse { | |||||
return &api.FileResponse{ | |||||
Content: &api.FileContentResponse{ | |||||
Name: "file.txt", | |||||
Path: "new/file.txt", | |||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", | |||||
Size: 18, | |||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/new/file.txt", | |||||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/new/file.txt", | |||||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", | |||||
DownloadURL: "https://try.gitea.io/user2/repo1/raw/branch/master/new/file.txt", | |||||
Type: "blob", | |||||
Links: &api.FileLinksResponse{ | |||||
Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/new/file.txt", | |||||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", | |||||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/new/file.txt", | |||||
}, | |||||
}, | |||||
Commit: &api.FileCommitResponse{ | |||||
CommitMeta: api.CommitMeta{ | |||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/" + commitID, | |||||
SHA: commitID, | |||||
}, | |||||
HTMLURL: "https://try.gitea.io/user2/repo1/commit/" + commitID, | |||||
Author: &api.CommitUser{ | |||||
Identity: api.Identity{ | |||||
Name: "User Two", | |||||
Email: "user2@", | |||||
}, | |||||
Date: time.Now().UTC().Format(time.RFC3339), | |||||
}, | |||||
Committer: &api.CommitUser{ | |||||
Identity: api.Identity{ | |||||
Name: "User Two", | |||||
Email: "user2@", | |||||
}, | |||||
Date: time.Now().UTC().Format(time.RFC3339), | |||||
}, | |||||
Parents: []*api.CommitMeta{ | |||||
{ | |||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
}, | |||||
}, | |||||
Message: "Updates README.md\n", | |||||
Tree: &api.CommitMeta{ | |||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", | |||||
SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc", | |||||
}, | |||||
}, | |||||
Verification: &api.PayloadCommitVerification{ | |||||
Verified: false, | |||||
Reason: "unsigned", | |||||
Signature: "", | |||||
Payload: "", | |||||
}, | |||||
} | |||||
} | |||||
func getExpectedFileResponseForUpdate(commitID string) *api.FileResponse { | |||||
return &api.FileResponse{ | |||||
Content: &api.FileContentResponse{ | |||||
Name: "README.md", | |||||
Path: "README.md", | |||||
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", | |||||
Size: 43, | |||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md", | |||||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md", | |||||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", | |||||
DownloadURL: "https://try.gitea.io/user2/repo1/raw/branch/master/README.md", | |||||
Type: "blob", | |||||
Links: &api.FileLinksResponse{ | |||||
Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md", | |||||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", | |||||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md", | |||||
}, | |||||
}, | |||||
Commit: &api.FileCommitResponse{ | |||||
CommitMeta: api.CommitMeta{ | |||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/" + commitID, | |||||
SHA: commitID, | |||||
}, | |||||
HTMLURL: "https://try.gitea.io/user2/repo1/commit/" + commitID, | |||||
Author: &api.CommitUser{ | |||||
Identity: api.Identity{ | |||||
Name: "User Two", | |||||
Email: "user2@", | |||||
}, | |||||
Date: time.Now().UTC().Format(time.RFC3339), | |||||
}, | |||||
Committer: &api.CommitUser{ | |||||
Identity: api.Identity{ | |||||
Name: "User Two", | |||||
Email: "user2@", | |||||
}, | |||||
Date: time.Now().UTC().Format(time.RFC3339), | |||||
}, | |||||
Parents: []*api.CommitMeta{ | |||||
{ | |||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
}, | |||||
}, | |||||
Message: "Updates README.md\n", | |||||
Tree: &api.CommitMeta{ | |||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", | |||||
SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc", | |||||
}, | |||||
}, | |||||
Verification: &api.PayloadCommitVerification{ | |||||
Verified: false, | |||||
Reason: "unsigned", | |||||
Signature: "", | |||||
Payload: "", | |||||
}, | |||||
} | |||||
} | |||||
func TestCreateOrUpdateRepoFileForCreate(t *testing.T) { | |||||
// setup | |||||
models.PrepareTestEnv(t) | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
opts := getCreateRepoFileOptions(repo) | |||||
// test | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
// asserts | |||||
assert.Nil(t, err) | |||||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) | |||||
expectedFileResponse := getExpectedFileResponseForCreate(commitID) | |||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) | |||||
} | |||||
func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) { | |||||
// setup | |||||
models.PrepareTestEnv(t) | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
// test | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
// asserts | |||||
assert.Nil(t, err) | |||||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) | |||||
expectedFileResponse := getExpectedFileResponseForUpdate(commitID) | |||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) | |||||
} | |||||
func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { | |||||
// setup | |||||
models.PrepareTestEnv(t) | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
suffix := "_new" | |||||
opts.FromTreePath = "README.md" | |||||
opts.TreePath = "README.md" + suffix // new file name, README.md_new | |||||
// test | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
// asserts | |||||
assert.Nil(t, err) | |||||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) | |||||
expectedFileResponse := getExpectedFileResponseForUpdate(commit.ID.String()) | |||||
// assert that the old file no longer exists in the last commit of the branch | |||||
fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath) | |||||
toEntry, err := commit.GetTreeEntryByPath(opts.TreePath) | |||||
assert.Nil(t, fromEntry) // Should no longer exist here | |||||
assert.NotNil(t, toEntry) // Should exist here | |||||
// assert SHA has remained the same but paths use the new file name | |||||
assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Content.Name+suffix, fileResponse.Content.Name) | |||||
assert.EqualValues(t, expectedFileResponse.Content.Path+suffix, fileResponse.Content.Path) | |||||
assert.EqualValues(t, expectedFileResponse.Content.URL+suffix, fileResponse.Content.URL) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||||
} | |||||
// Test opts with branch names removed, should get same results as above test | |||||
func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) { | |||||
// setup | |||||
models.PrepareTestEnv(t) | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.OldBranch = "" | |||||
opts.NewBranch = "" | |||||
// test | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
// asserts | |||||
assert.Nil(t, err) | |||||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch) | |||||
expectedFileResponse := getExpectedFileResponseForUpdate(commitID) | |||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||||
} | |||||
func TestCreateOrUpdateRepoFileErrors(t *testing.T) { | |||||
// setup | |||||
models.PrepareTestEnv(t) | |||||
ctx := test.MockContext(t, "user2/repo1") | |||||
ctx.SetParams(":id", "1") | |||||
test.LoadRepo(t, ctx, 1) | |||||
test.LoadRepoCommit(t, ctx) | |||||
test.LoadUser(t, ctx, 2) | |||||
test.LoadGitRepo(t, ctx) | |||||
repo := ctx.Repo.Repository | |||||
doer := ctx.User | |||||
t.Run("bad branch", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.OldBranch = "bad_branch" | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Error(t, err) | |||||
assert.Nil(t, fileResponse) | |||||
expectedError := "branch does not exist [name: " + opts.OldBranch + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("bad SHA", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
origSHA := opts.SHA | |||||
opts.SHA = "bad_sha" | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("new branch already exists", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.NewBranch = "develop" | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "branch already exists [name: " + opts.NewBranch + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("treePath is empty:", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.TreePath = "" | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "path contains a malformed path component [path: ]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("treePath is a git directory:", func(t *testing.T) { | |||||
opts := getUpdateRepoFileOptions(repo) | |||||
opts.TreePath = ".git" | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
t.Run("create file that already exists", func(t *testing.T) { | |||||
opts := getCreateRepoFileOptions(repo) | |||||
opts.TreePath = "README.md" //already exists | |||||
fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) | |||||
assert.Nil(t, fileResponse) | |||||
assert.Error(t, err) | |||||
expectedError := "repository file already exists [path: " + opts.TreePath + "]" | |||||
assert.EqualError(t, err, expectedError) | |||||
}) | |||||
} |
// Repository local settings | // Repository local settings | ||||
Local struct { | Local struct { | ||||
LocalCopyPath string | LocalCopyPath string | ||||
LocalWikiPath string | |||||
} `ini:"-"` | } `ini:"-"` | ||||
// Pull request settings | // Pull request settings | ||||
// Repository local settings | // Repository local settings | ||||
Local: struct { | Local: struct { | ||||
LocalCopyPath string | LocalCopyPath string | ||||
LocalWikiPath string | |||||
}{ | }{ | ||||
LocalCopyPath: "tmp/local-repo", | LocalCopyPath: "tmp/local-repo", | ||||
LocalWikiPath: "tmp/local-wiki", | |||||
}, | }, | ||||
// Pull request settings | // Pull request settings |
return | return | ||||
} | } | ||||
// Delete branch in local copy if it exists | |||||
if err := ctx.Repo.Repository.DeleteLocalBranch(branchName); err != nil { | |||||
ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName)) | |||||
return | |||||
} | |||||
ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", branchName)) | ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", branchName)) | ||||
} | } | ||||