Move Git hook logic to Opengist (#213)

This commit is contained in:
Thomas Miceli
2024-01-23 20:24:01 +01:00
parent dfe70dc4cf
commit 7a75c5ecfa
16 changed files with 407 additions and 193 deletions

View File

@ -24,6 +24,7 @@ var (
)
const truncateLimit = 2 << 18
const BaseHash = "0000000000000000000000000000000000000000"
type RevisionNotFoundError struct{}
@ -80,16 +81,6 @@ func InitRepository(user string, gist string) error {
return CreateDotGitFiles(user, gist)
}
func InitRepositoryViaInit(user string, gist string, ctx echo.Context) error {
repositoryPath := RepositoryPath(user, gist)
if err := InitRepository(user, gist); err != nil {
return err
}
repositoryUrl := RepositoryUrl(ctx, user, gist)
return createDotGitHookFile(repositoryPath, "post-receive", fmt.Sprintf(postReceive, repositoryUrl, repositoryUrl))
}
func CountCommits(user string, gist string) (string, error) {
repositoryPath := RepositoryPath(user, gist)
@ -424,7 +415,6 @@ func Push(gistTmpId string) error {
if err != nil {
return err
}
return os.RemoveAll(tmpRepositoryPath)
}
@ -534,8 +524,12 @@ func CreateDotGitFiles(user string, gist string) error {
}
defer f1.Close()
if err = createDotGitHookFile(repositoryPath, "pre-receive", preReceive); err != nil {
return err
if os.Getenv("OPENGIST_SKIP_GIT_HOOKS") != "1" {
for _, hook := range []string{"pre-receive", "post-receive"} {
if err = createDotGitHookFile(repositoryPath, hook, fmt.Sprintf(hookTemplate, hook)); err != nil {
return err
}
}
}
return nil
@ -570,57 +564,6 @@ func removeFilesExceptGit(dir string) error {
})
}
const preReceive = `#!/bin/sh
disallowed_files=""
while read -r old_rev new_rev ref
do
if [ "$old_rev" = "0000000000000000000000000000000000000000" ]; then
# This is the first commit, so we check all the files in that commit
changed_files=$(git ls-tree -r --name-only "$new_rev")
else
# This is not the first commit, so we compare it with its predecessor
changed_files=$(git diff --name-only "$old_rev" "$new_rev")
fi
while IFS= read -r file
do
case $file in
*/*)
disallowed_files="${disallowed_files}${file} "
;;
esac
done <<EOF
$changed_files
EOF
done
if [ -n "$disallowed_files" ]; then
echo ""
echo "Pushing files in folders is not allowed:"
for file in $disallowed_files; do
echo " $file"
done
echo ""
exit 1
fi
`
const postReceive = `#!/bin/sh
while read oldrev newrev refname; do
if ! git rev-parse --verify --quiet HEAD &>/dev/null; then
git symbolic-ref HEAD "$refname"
fi
done
echo ""
echo "Your new repository has been created here: %s"
echo ""
echo "If you want to keep working with your gist, you could set the remote URL via:"
echo "git remote set-url origin %s"
echo ""
rm -f $0
const hookTemplate = `#!/bin/sh
"$OG_OPENGIST_HOME_INTERNAL/opengist-bin" hook %s
`

View File

@ -1,11 +1,11 @@
package git
import (
"github.com/labstack/echo/v4"
"bytes"
"fmt"
"github.com/stretchr/testify/require"
"github.com/thomiceli/opengist/internal/config"
"net/http"
"net/http/httptest"
"github.com/thomiceli/opengist/internal/hooks"
"os"
"os/exec"
"path"
@ -15,6 +15,8 @@ import (
)
func setup(t *testing.T) {
_ = os.Setenv("OPENGIST_SKIP_GIT_HOOKS", "1")
err := config.InitConfig("")
require.NoError(t, err, "Could not init config")
@ -30,7 +32,7 @@ func setup(t *testing.T) {
}
func teardown(t *testing.T) {
err := os.RemoveAll(path.Join(config.C.OpengistHome, "tests"))
err := os.RemoveAll(path.Join(config.GetHomeDir(), "tests"))
require.NoError(t, err, "Could not remove repos directory")
}
@ -44,9 +46,6 @@ func TestInitDeleteRepository(t *testing.T) {
require.NoError(t, err, "Could not run git command")
require.Equal(t, "true", strings.TrimSpace(string(out)), "Repository is not bare")
_, err = os.Stat(path.Join(RepositoryPath("thomas", "gist1"), "hooks", "pre-receive"))
require.NoError(t, err, "pre-receive hook not found")
_, err = os.Stat(path.Join(RepositoryPath("thomas", "gist1"), "git-daemon-export-ok"))
require.NoError(t, err, "git-daemon-export-ok file not found")
@ -247,30 +246,6 @@ func TestTruncate(t *testing.T) {
require.Equal(t, 2, len(content), "Content size is not correct")
}
func TestInitViaGitInit(t *testing.T) {
setup(t)
defer teardown(t)
e := echo.New()
// Create a mock HTTP request
req := httptest.NewRequest(http.MethodPost, "/", nil)
// Create a mock HTTP response recorder
rec := httptest.NewRecorder()
// Create a new Echo context
c := e.NewContext(req, rec)
// Define your user and gist
user := "testUser"
gist := "testGist"
err := InitRepositoryViaInit(user, gist, c)
require.NoError(t, err)
}
func TestGitInitBranchNames(t *testing.T) {
setup(t)
defer teardown(t)
@ -292,21 +267,67 @@ func TestGitInitBranchNames(t *testing.T) {
require.Equal(t, "refs/heads/main", strings.TrimSpace(string(out)), "Repository should have main branch as default")
}
func TestPreReceiveHook(t *testing.T) {
setup(t)
defer teardown(t)
var lastCommitHash string
err := os.Chdir(RepositoryPath("thomas", "gist1"))
require.NoError(t, err, "Could not change directory")
commitToBare(t, "thomas", "gist1", map[string]string{
"my_file.txt": "some allowed file",
"my_file2.txt": "some allowed file\nagain",
})
lastCommitHash = lastHashOfCommit(t, "thomas", "gist1")
err = hooks.PreReceive(bytes.NewBufferString(fmt.Sprintf("%s %s %s", BaseHash, lastCommitHash, "refs/heads/master")), os.Stdout, os.Stderr)
require.NoError(t, err, "Should not have an error on pre-receive hook for commit+push 1")
commitToBare(t, "thomas", "gist1", map[string]string{
"my_file.txt": "some allowed file",
"dir/my_file.txt": "some disallowed file suddenly",
})
lastCommitHash = lastHashOfCommit(t, "thomas", "gist1")
err = hooks.PreReceive(bytes.NewBufferString(fmt.Sprintf("%s %s %s", BaseHash, lastCommitHash, "refs/heads/master")), os.Stdout, os.Stderr)
require.Error(t, err, "Should have an error on pre-receive hook for commit+push 2")
require.Equal(t, "pushing files in directories is not allowed: [dir/my_file.txt]", err.Error(), "Error message is not correct")
commitToBare(t, "thomas", "gist1", map[string]string{
"my_file.txt": "some allowed file",
"dir/ok/afileagain.txt": "some disallowed file\nagain",
})
lastCommitHash = lastHashOfCommit(t, "thomas", "gist1")
err = hooks.PreReceive(bytes.NewBufferString(fmt.Sprintf("%s %s %s", BaseHash, lastCommitHash, "refs/heads/master")), os.Stdout, os.Stderr)
require.Error(t, err, "Should have an error on pre-receive hook for commit+push 3")
require.Equal(t, "pushing files in directories is not allowed: [dir/ok/afileagain.txt dir/my_file.txt]", err.Error(), "Error message is not correct")
commitToBare(t, "thomas", "gist1", map[string]string{
"allowedfile.txt": "some allowed file only",
})
lastCommitHash = lastHashOfCommit(t, "thomas", "gist1")
err = hooks.PreReceive(bytes.NewBufferString(fmt.Sprintf("%s %s %s", BaseHash, lastCommitHash, "refs/heads/master")), os.Stdout, os.Stderr)
require.Error(t, err, "Should have an error on pre-receive hook for commit+push 4")
require.Equal(t, "pushing files in directories is not allowed: [dir/ok/afileagain.txt dir/my_file.txt]", err.Error(), "Error message is not correct")
_ = os.Chdir(os.TempDir()) // Leave the current dir to avoid errors on teardown
}
func commitToBare(t *testing.T, user string, gist string, files map[string]string) {
err := CloneTmp(user, gist, gist, "thomas@mail.com", true)
require.NoError(t, err, "Could not commit to repository")
require.NoError(t, err, "Could not clone repository")
if len(files) > 0 {
for filename, content := range files {
if err := SetFileContent(gist, filename, content); err != nil {
require.NoError(t, err, "Could not commit to repository")
if strings.Contains(filename, "/") {
dir := filepath.Dir(filename)
err := os.MkdirAll(filepath.Join(TmpRepositoryPath(gist), dir), os.ModePerm)
require.NoError(t, err, "Could not create directory")
}
_ = os.WriteFile(filepath.Join(TmpRepositoryPath(gist), filename), []byte(content), 0644)
if err := AddAll(gist); err != nil {
require.NoError(t, err, "Could not commit to repository")
require.NoError(t, err, "Could not add all to repository")
}
}
}
if err := CommitRepository(gist, user, "thomas@mail.com"); err != nil {
@ -314,6 +335,14 @@ func commitToBare(t *testing.T, user string, gist string, files map[string]strin
}
if err := Push(gist); err != nil {
require.NoError(t, err, "Could not commit to repository")
require.NoError(t, err, "Could not push to repository")
}
}
func lastHashOfCommit(t *testing.T, user string, gist string) string {
cmd := exec.Command("git", "rev-parse", "HEAD")
cmd.Dir = RepositoryPath(user, gist)
out, err := cmd.Output()
require.NoError(t, err, "Could not run git command")
return strings.TrimSpace(string(out))
}