feat: replace repo based server-side hooks with centralised hooks (#10397)

This PR is replacing repository based hooks hooks with centralised files, this way the files don't need to be copied into every repository, only one line of config need to be added in the repository.

Closes: #3523

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10397
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
Gabor Pihaj 2026-04-27 22:34:46 +02:00 committed by Gusted
parent f05ff7ec5b
commit 73b30acbd0
26 changed files with 418 additions and 439 deletions

0
tests/install.ini.tmpl Normal file
View file

View file

@ -426,11 +426,11 @@ func TestAPICron(t *testing.T) {
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "31", resp.Header().Get("X-Total-Count"))
assert.Equal(t, "30", resp.Header().Get("X-Total-Count"))
var crons []api.Cron
DecodeJSON(t, resp, &crons)
assert.Len(t, crons, 31)
assert.Len(t, crons, 30)
})
t.Run("Execute", func(t *testing.T) {

View file

@ -7,6 +7,7 @@ package integration
import (
"fmt"
"net/http"
"net/url"
"testing"
repo_model "forgejo.org/models/repo"
@ -44,169 +45,169 @@ func getIssueConfig(t *testing.T, owner, repo string) api.IssueConfig {
}
func TestAPIRepoGetIssueConfig(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onApplicationRun(t, func(t *testing.T, _ *url.URL) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
t.Run("Default", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Default", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
assert.True(t, issueConfig.BlankIssuesEnabled)
assert.Empty(t, issueConfig.ContactLinks)
})
assert.True(t, issueConfig.BlankIssuesEnabled)
assert.Empty(t, issueConfig.ContactLinks)
})
t.Run("DisableBlankIssues", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("DisableBlankIssues", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
config := make(map[string]any)
config["blank_issues_enabled"] = false
config := make(map[string]any)
config["blank_issues_enabled"] = false
createIssueConfig(t, owner, repo, config)
createIssueConfig(t, owner, repo, config)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
assert.False(t, issueConfig.BlankIssuesEnabled)
assert.Empty(t, issueConfig.ContactLinks)
})
assert.False(t, issueConfig.BlankIssuesEnabled)
assert.Empty(t, issueConfig.ContactLinks)
})
t.Run("ContactLinks", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("ContactLinks", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
contactLink := make(map[string]string)
contactLink["name"] = "TestName"
contactLink["url"] = "https://example.com"
contactLink["about"] = "TestAbout"
contactLink := make(map[string]string)
contactLink["name"] = "TestName"
contactLink["url"] = "https://example.com"
contactLink["about"] = "TestAbout"
config := make(map[string]any)
config["contact_links"] = []map[string]string{contactLink}
config := make(map[string]any)
config["contact_links"] = []map[string]string{contactLink}
createIssueConfig(t, owner, repo, config)
createIssueConfig(t, owner, repo, config)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
assert.True(t, issueConfig.BlankIssuesEnabled)
assert.Len(t, issueConfig.ContactLinks, 1)
assert.True(t, issueConfig.BlankIssuesEnabled)
assert.Len(t, issueConfig.ContactLinks, 1)
assert.Equal(t, "TestName", issueConfig.ContactLinks[0].Name)
assert.Equal(t, "https://example.com", issueConfig.ContactLinks[0].URL)
assert.Equal(t, "TestAbout", issueConfig.ContactLinks[0].About)
})
assert.Equal(t, "TestName", issueConfig.ContactLinks[0].Name)
assert.Equal(t, "https://example.com", issueConfig.ContactLinks[0].URL)
assert.Equal(t, "TestAbout", issueConfig.ContactLinks[0].About)
})
t.Run("Full", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Full", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
contactLink := make(map[string]string)
contactLink["name"] = "TestName"
contactLink["url"] = "https://example.com"
contactLink["about"] = "TestAbout"
contactLink := make(map[string]string)
contactLink["name"] = "TestName"
contactLink["url"] = "https://example.com"
contactLink["about"] = "TestAbout"
config := make(map[string]any)
config["blank_issues_enabled"] = false
config["contact_links"] = []map[string]string{contactLink}
config := make(map[string]any)
config["blank_issues_enabled"] = false
config["contact_links"] = []map[string]string{contactLink}
createIssueConfig(t, owner, repo, config)
createIssueConfig(t, owner, repo, config)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
assert.False(t, issueConfig.BlankIssuesEnabled)
assert.Len(t, issueConfig.ContactLinks, 1)
assert.False(t, issueConfig.BlankIssuesEnabled)
assert.Len(t, issueConfig.ContactLinks, 1)
assert.Equal(t, "TestName", issueConfig.ContactLinks[0].Name)
assert.Equal(t, "https://example.com", issueConfig.ContactLinks[0].URL)
assert.Equal(t, "TestAbout", issueConfig.ContactLinks[0].About)
assert.Equal(t, "TestName", issueConfig.ContactLinks[0].Name)
assert.Equal(t, "https://example.com", issueConfig.ContactLinks[0].URL)
assert.Equal(t, "TestAbout", issueConfig.ContactLinks[0].About)
})
})
}
func TestAPIRepoIssueConfigPaths(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onApplicationRun(t, func(t *testing.T, _ *url.URL) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
templateConfigCandidates := []string{
".forgejo/ISSUE_TEMPLATE/config",
".forgejo/issue_template/config",
".gitea/ISSUE_TEMPLATE/config",
".gitea/issue_template/config",
".github/ISSUE_TEMPLATE/config",
".github/issue_template/config",
"docs/issue_template/config",
}
for _, candidate := range templateConfigCandidates {
for _, extension := range []string{".yaml", ".yml"} {
fullPath := candidate + extension
t.Run(fullPath, func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
configMap := make(map[string]any)
configMap["blank_issues_enabled"] = false
configData, err := yaml.Marshal(configMap)
require.NoError(t, err)
_, err = createFileInBranch(owner, repo, fullPath, repo.DefaultBranch, string(configData))
require.NoError(t, err)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
assert.False(t, issueConfig.BlankIssuesEnabled)
assert.Empty(t, issueConfig.ContactLinks)
err = deleteFileInBranch(owner, repo, fullPath, repo.DefaultBranch)
require.NoError(t, err)
})
templateConfigCandidates := []string{
".forgejo/ISSUE_TEMPLATE/config",
".forgejo/issue_template/config",
".gitea/ISSUE_TEMPLATE/config",
".gitea/issue_template/config",
".github/ISSUE_TEMPLATE/config",
".github/issue_template/config",
"docs/issue_template/config",
}
}
for _, candidate := range templateConfigCandidates {
for _, extension := range []string{".yaml", ".yml"} {
fullPath := candidate + extension
t.Run(fullPath, func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
configMap := make(map[string]any)
configMap["blank_issues_enabled"] = false
configData, err := yaml.Marshal(configMap)
require.NoError(t, err)
_, err = createFileInBranch(owner, repo, fullPath, repo.DefaultBranch, string(configData))
require.NoError(t, err)
issueConfig := getIssueConfig(t, owner.Name, repo.Name)
assert.False(t, issueConfig.BlankIssuesEnabled)
assert.Empty(t, issueConfig.ContactLinks)
err = deleteFileInBranch(owner, repo, fullPath, repo.DefaultBranch)
require.NoError(t, err)
})
}
}
})
}
func TestAPIRepoValidateIssueConfig(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onApplicationRun(t, func(t *testing.T, _ *url.URL) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issue_config/validate", owner.Name, repo.Name)
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issue_config/validate", owner.Name, repo.Name)
t.Run("Valid", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Valid", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", urlStr)
resp := MakeRequest(t, req, http.StatusOK)
req := NewRequest(t, "GET", urlStr)
resp := MakeRequest(t, req, http.StatusOK)
var issueConfigValidation api.IssueConfigValidation
DecodeJSON(t, resp, &issueConfigValidation)
var issueConfigValidation api.IssueConfigValidation
DecodeJSON(t, resp, &issueConfigValidation)
assert.True(t, issueConfigValidation.Valid)
assert.Empty(t, issueConfigValidation.Message)
})
assert.True(t, issueConfigValidation.Valid)
assert.Empty(t, issueConfigValidation.Message)
})
t.Run("Invalid", func(t *testing.T) {
dirs := []string{".gitea", ".forgejo", "docs"}
for _, dir := range dirs {
t.Run(dir, func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
defer func() {
deleteFileInBranch(owner, repo, fmt.Sprintf("%s/ISSUE_TEMPLATE/config.yaml", dir), repo.DefaultBranch)
}()
t.Run("Invalid", func(t *testing.T) {
dirs := []string{".gitea", ".forgejo", "docs"}
for _, dir := range dirs {
t.Run(dir, func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
defer func() {
deleteFileInBranch(owner, repo, fmt.Sprintf("%s/ISSUE_TEMPLATE/config.yaml", dir), repo.DefaultBranch)
}()
config := make(map[string]any)
config["blank_issues_enabled"] = "Test"
config := make(map[string]any)
config["blank_issues_enabled"] = "Test"
createIssueConfigInDirectory(t, owner, repo, dir, config)
createIssueConfigInDirectory(t, owner, repo, dir, config)
req := NewRequest(t, "GET", urlStr)
resp := MakeRequest(t, req, http.StatusOK)
req := NewRequest(t, "GET", urlStr)
resp := MakeRequest(t, req, http.StatusOK)
var issueConfigValidation api.IssueConfigValidation
DecodeJSON(t, resp, &issueConfigValidation)
var issueConfigValidation api.IssueConfigValidation
DecodeJSON(t, resp, &issueConfigValidation)
assert.False(t, issueConfigValidation.Valid)
assert.NotEmpty(t, issueConfigValidation.Message)
})
}
assert.False(t, issueConfigValidation.Valid)
assert.NotEmpty(t, issueConfigValidation.Message)
})
}
})
})
}

View file

@ -10,6 +10,7 @@ import (
"io"
"mime/multipart"
"net/http"
"net/url"
"testing"
auth_model "forgejo.org/models/auth"
@ -44,96 +45,96 @@ func TestEmptyRepo(t *testing.T) {
}
func TestEmptyRepoAddFile(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onApplicationRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user30")
req := NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body).Find(`input[name="commit_choice"]`)
assert.Empty(t, doc.AttrOr("checked", "_no_"))
req = NewRequestWithValues(t, "POST", "/user30/empty/_new/"+setting.Repository.DefaultBranch, map[string]string{
"commit_choice": "direct",
"tree_path": "test-file.md",
"content": "newly-added-test-file",
"commit_mail_id": "32",
})
session := loginUser(t, "user30")
req := NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body).Find(`input[name="commit_choice"]`)
assert.Empty(t, doc.AttrOr("checked", "_no_"))
req = NewRequestWithValues(t, "POST", "/user30/empty/_new/"+setting.Repository.DefaultBranch, map[string]string{
"commit_choice": "direct",
"tree_path": "test-file.md",
"content": "newly-added-test-file",
"commit_mail_id": "32",
resp = session.MakeRequest(t, req, http.StatusSeeOther)
redirect := test.RedirectURL(resp)
assert.Equal(t, "/user30/empty/src/branch/"+setting.Repository.DefaultBranch+"/test-file.md", redirect)
req = NewRequest(t, "GET", redirect)
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "newly-added-test-file")
})
resp = session.MakeRequest(t, req, http.StatusSeeOther)
redirect := test.RedirectURL(resp)
assert.Equal(t, "/user30/empty/src/branch/"+setting.Repository.DefaultBranch+"/test-file.md", redirect)
req = NewRequest(t, "GET", redirect)
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "newly-added-test-file")
}
func TestEmptyRepoUploadFile(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onApplicationRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user30")
req := NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body).Find(`input[name="commit_choice"]`)
assert.Empty(t, doc.AttrOr("checked", "_no_"))
session := loginUser(t, "user30")
req := NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body).Find(`input[name="commit_choice"]`)
assert.Empty(t, doc.AttrOr("checked", "_no_"))
body := &bytes.Buffer{}
mpForm := multipart.NewWriter(body)
file, _ := mpForm.CreateFormFile("file", "uploaded-file.txt")
_, _ = io.Copy(file, bytes.NewBufferString("newly-uploaded-test-file"))
_ = mpForm.Close()
body := &bytes.Buffer{}
mpForm := multipart.NewWriter(body)
file, _ := mpForm.CreateFormFile("file", "uploaded-file.txt")
_, _ = io.Copy(file, bytes.NewBufferString("newly-uploaded-test-file"))
_ = mpForm.Close()
req = NewRequestWithBody(t, "POST", "/user30/empty/upload-file", body)
req.Header.Add("Content-Type", mpForm.FormDataContentType())
resp = session.MakeRequest(t, req, http.StatusOK)
respMap := map[string]string{}
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &respMap))
filesFullpathKey := fmt.Sprintf("files_fullpath[%s]", respMap["uuid"])
req = NewRequestWithValues(t, "POST", "/user30/empty/_upload/"+setting.Repository.DefaultBranch, map[string]string{
"commit_choice": "direct",
"files": respMap["uuid"],
filesFullpathKey: "uploaded-file.txt",
"tree_path": "",
"commit_mail_id": "-1",
})
resp = session.MakeRequest(t, req, http.StatusSeeOther)
redirect := test.RedirectURL(resp)
assert.Equal(t, "/user30/empty/src/branch/"+setting.Repository.DefaultBranch+"/", redirect)
req = NewRequestWithBody(t, "POST", "/user30/empty/upload-file", body)
req.Header.Add("Content-Type", mpForm.FormDataContentType())
resp = session.MakeRequest(t, req, http.StatusOK)
respMap := map[string]string{}
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &respMap))
filesFullpathKey := fmt.Sprintf("files_fullpath[%s]", respMap["uuid"])
req = NewRequestWithValues(t, "POST", "/user30/empty/_upload/"+setting.Repository.DefaultBranch, map[string]string{
"commit_choice": "direct",
"files": respMap["uuid"],
filesFullpathKey: "uploaded-file.txt",
"tree_path": "",
"commit_mail_id": "-1",
req = NewRequest(t, "GET", redirect)
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "uploaded-file.txt")
})
resp = session.MakeRequest(t, req, http.StatusSeeOther)
redirect := test.RedirectURL(resp)
assert.Equal(t, "/user30/empty/src/branch/"+setting.Repository.DefaultBranch+"/", redirect)
req = NewRequest(t, "GET", redirect)
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "uploaded-file.txt")
}
func TestEmptyRepoAddFileByAPI(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onApplicationRun(t, func(t *testing.T, _ *url.URL) {
session := loginUser(t, "user30")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
session := loginUser(t, "user30")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user30/empty/contents/new-file.txt", &api.CreateFileOptions{
FileOptions: api.FileOptions{
NewBranchName: "new_branch",
Message: "init",
},
ContentBase64: base64.StdEncoding.EncodeToString([]byte("newly-added-api-file")),
}).AddTokenAuth(token)
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user30/empty/contents/new-file.txt", &api.CreateFileOptions{
FileOptions: api.FileOptions{
NewBranchName: "new_branch",
Message: "init",
},
ContentBase64: base64.StdEncoding.EncodeToString([]byte("newly-added-api-file")),
}).AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusCreated)
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
expectedHTMLURL := setting.AppURL + "user30/empty/src/branch/new_branch/new-file.txt"
assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
resp := MakeRequest(t, req, http.StatusCreated)
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
expectedHTMLURL := setting.AppURL + "user30/empty/src/branch/new_branch/new-file.txt"
assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
req = NewRequest(t, "GET", "/user30/empty/src/branch/new_branch/new-file.txt")
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "newly-added-api-file")
req = NewRequest(t, "GET", "/user30/empty/src/branch/new_branch/new-file.txt")
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "newly-added-api-file")
req = NewRequest(t, "GET", "/api/v1/repos/user30/empty").
AddTokenAuth(token)
resp = session.MakeRequest(t, req, http.StatusOK)
var apiRepo api.Repository
DecodeJSON(t, resp, &apiRepo)
assert.Equal(t, "new_branch", apiRepo.DefaultBranch)
req = NewRequest(t, "GET", "/api/v1/repos/user30/empty").
AddTokenAuth(token)
resp = session.MakeRequest(t, req, http.StatusOK)
var apiRepo api.Repository
DecodeJSON(t, resp, &apiRepo)
assert.Equal(t, "new_branch", apiRepo.DefaultBranch)
})
}
func TestEmptyRepoAPIRequestsReturn404(t *testing.T) {

View file

@ -0,0 +1,99 @@
// Copyright 2026 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"fmt"
"net/url"
"os"
"path"
"testing"
"forgejo.org/models/auth"
repo_model "forgejo.org/models/repo"
"forgejo.org/models/unittest"
user_model "forgejo.org/models/user"
"forgejo.org/modules/git"
"github.com/stretchr/testify/require"
)
func TestCustomGitHooks(t *testing.T) {
onApplicationRun(t, func(t *testing.T, u *url.URL) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
httpContext := NewAPITestContext(t, owner.Name, repo.Name, auth.AccessTokenScopeReadRepository)
dstPath := t.TempDir()
u.Path = httpContext.GitPath()
u.User = url.UserPassword(owner.Name, userPassword)
doGitClone(dstPath, u)(t)
customHooksDir := path.Join(repo.RepoPath(), "hooks")
hookNames := []string{"pre-receive", "update", "post-receive"}
for _, hookName := range hookNames {
customPath := path.Join(customHooksDir, hookName+".d")
err := os.MkdirAll(customPath, 0x755)
require.NoError(t, err)
err = os.WriteFile(path.Join(customPath, "append-proof"), customGitHookTpl(hookName), 0x755)
require.NoError(t, err)
// The legacy, already existing gitea script might be there in the hooks directory in old installations,
// here it's ensured that these scripts filtered out when custom hooks run
err = os.WriteFile(path.Join(customPath, "gitea"), customGitHookGiteaTpl(), 0x755)
require.NoError(t, err)
}
fd, err := os.Create(path.Join(dstPath, "hooks-test.txt"))
require.NoError(t, err)
err = fd.Close()
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "checkout", "master").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
err = os.WriteFile(path.Join(dstPath, "hooks-test.txt"), []byte("test"), 0x644)
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "add", "hooks-test.txt").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "commit", "-m", "Add hooks-test.txt").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "master").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
data, err := os.ReadFile(path.Join(customHooksDir, "hooks-proof.txt"))
require.NoError(t, err)
require.Equal(t, `pre-receive
update
post-receive
`, string(data))
})
}
func customGitHookTpl(hookName string) []byte {
hookStr := fmt.Sprintf(`#!/usr/bin/env sh
echo "%s" >> $(dirname $0)/../hooks-proof.txt
`, hookName)
return []byte(hookStr)
}
func customGitHookGiteaTpl() []byte {
hookStr := `#!/usr/bin/env sh
echo "legacy gitea script shouldn't be called!"
exit 1
`
return []byte(hookStr)
}

View file

@ -201,6 +201,7 @@ func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string
func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
t.Run("LFS", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
defer git.NewCommand(git.DefaultContext, "lfs").AddArguments("uninstall").Run(&git.RunOpts{Dir: dstPath})
prefix := "lfs-data-file-"
err := git.NewCommand(git.DefaultContext, "lfs").AddArguments("install").Run(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)

View file

@ -5,29 +5,29 @@ package integration
import (
"net/http"
"net/url"
"testing"
"forgejo.org/tests"
"github.com/stretchr/testify/assert"
)
func TestRepoMergeCommitRevert(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
onApplicationRun(t, func(t *testing.T, _ *url.URL) {
session := loginUser(t, "user2")
req := NewRequestWithValues(t, "POST", "/user2/test_commit_revert/_cherrypick/deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7/main", map[string]string{
"last_commit": "deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7",
"page_has_posted": "true",
"revert": "true",
"commit_summary": "reverting test commit",
"commit_message": "test message",
"commit_choice": "direct",
"new_branch_name": "test-revert-branch-1",
"commit_mail_id": "-1",
req := NewRequestWithValues(t, "POST", "/user2/test_commit_revert/_cherrypick/deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7/main", map[string]string{
"last_commit": "deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7",
"page_has_posted": "true",
"revert": "true",
"commit_summary": "reverting test commit",
"commit_message": "test message",
"commit_choice": "direct",
"new_branch_name": "test-revert-branch-1",
"commit_mail_id": "-1",
})
resp := session.MakeRequest(t, req, http.StatusSeeOther)
// A successful revert redirects to the main branch
assert.Equal(t, "/user2/test_commit_revert/src/branch/main", resp.Header().Get("Location"))
})
resp := session.MakeRequest(t, req, http.StatusSeeOther)
// A successful revert redirects to the main branch
assert.Equal(t, "/user2/test_commit_revert/src/branch/main", resp.Header().Get("Location"))
}

2
tests/unittest.ini.tmpl Normal file
View file

@ -0,0 +1,2 @@
[security]
INSTALL_LOCK = true