diff options
author | James Fargher <jfargher@gitlab.com> | 2023-04-19 05:17:48 +0300 |
---|---|---|
committer | James Fargher <jfargher@gitlab.com> | 2023-04-26 00:13:51 +0300 |
commit | c9086b48fbb7c3da74847d2588ff7a8380a7a5a9 (patch) | |
tree | 43d18e3432a2c35e69fc8c262e83c544912ebb28 | |
parent | a300ab96e4354a35c49bc773710b419cbfa239eb (diff) |
repoutil: Move ExtractHook helper
This helper used to live in the "internal/gitaly/hook" package but this
package is primarily concerned with running hooks of all kinds rather
than repository management. So we move this helper function to group all
custom hook management together.
-rw-r--r-- | internal/gitaly/hook/extract_hooks.go | 61 | ||||
-rw-r--r-- | internal/gitaly/hook/extract_hooks_test.go | 134 | ||||
-rw-r--r-- | internal/gitaly/repoutil/custom_hooks.go | 57 | ||||
-rw-r--r-- | internal/gitaly/repoutil/custom_hooks_test.go | 122 | ||||
-rw-r--r-- | internal/gitaly/service/repository/replicate_test.go | 7 | ||||
-rw-r--r-- | internal/gitaly/service/repository/set_custom_hooks.go | 10 | ||||
-rw-r--r-- | internal/gitaly/service/repository/set_custom_hooks_test.go | 8 | ||||
-rw-r--r-- | internal/gitaly/transaction_manager.go | 4 |
8 files changed, 191 insertions, 212 deletions
diff --git a/internal/gitaly/hook/extract_hooks.go b/internal/gitaly/hook/extract_hooks.go deleted file mode 100644 index b4b586c2b..000000000 --- a/internal/gitaly/hook/extract_hooks.go +++ /dev/null @@ -1,61 +0,0 @@ -package hook - -import ( - "bufio" - "context" - "fmt" - "io" - "strings" - - "gitlab.com/gitlab-org/gitaly/v15/internal/command" - "gitlab.com/gitlab-org/gitaly/v15/internal/structerr" -) - -// CustomHooksDir is the directory in which the custom hooks are stored in the repository. -// It's also the directory where the hooks are stored in the TAR archive containing the hooks. -const CustomHooksDir = "custom_hooks" - -// ExtractHooks unpacks a tar file containing custom hooks into a `custom_hooks` -// directory at the specified path. If stripPrefix is set, the hooks are extracted directly -// to the target directory instead of in a `custom_hooks` directory in the target directory. -func ExtractHooks(ctx context.Context, reader io.Reader, path string, stripPrefix bool) error { - // GNU tar does not accept an empty file as a valid tar archive and produces - // an error. Since an empty hooks tar is symbolic of a repository having no - // hooks, the reader is peeked to check if there is any data present. - buf := bufio.NewReader(reader) - if _, err := buf.Peek(1); err == io.EOF { - return nil - } - - stripComponents := "0" - if stripPrefix { - stripComponents = "1" - } - - cmdArgs := []string{"-xf", "-", "-C", path, "--strip-components", stripComponents, CustomHooksDir} - - var stderrBuilder strings.Builder - cmd, err := command.New(ctx, append([]string{"tar"}, cmdArgs...), - command.WithStdin(buf), - command.WithStderr(&stderrBuilder)) - if err != nil { - return fmt.Errorf("executing tar command: %w", err) - } - - if err := cmd.Wait(); err != nil { - stderr := stderrBuilder.String() - - // GNU and BSD tar versions have differing errors when attempting to - // extract specified members from a valid tar archive. If the tar - // archive is valid the errors for GNU and BSD tar should have the - // same prefix, which can be checked to validate whether the expected - // content is present in the archive for extraction. - if strings.HasPrefix(stderr, "tar: custom_hooks: Not found in archive") { - return nil - } - - return structerr.New("waiting for tar command completion: %w", err).WithMetadata("stderr", stderr) - } - - return nil -} diff --git a/internal/gitaly/hook/extract_hooks_test.go b/internal/gitaly/hook/extract_hooks_test.go deleted file mode 100644 index f9755fa8f..000000000 --- a/internal/gitaly/hook/extract_hooks_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package hook - -import ( - "archive/tar" - "bytes" - "io" - "io/fs" - "strings" - "testing" - - "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/gitaly/v15/internal/helper/perm" - "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper" -) - -func TestExtractHooks(t *testing.T) { - umask := perm.GetUmask() - - writeFile := func(writer *tar.Writer, path string, mode fs.FileMode, content string) { - require.NoError(t, writer.WriteHeader(&tar.Header{ - Name: path, - Mode: int64(mode), - Size: int64(len(content)), - })) - _, err := writer.Write([]byte(content)) - require.NoError(t, err) - } - - validArchive := func() io.Reader { - var buffer bytes.Buffer - writer := tar.NewWriter(&buffer) - writeFile(writer, "custom_hooks/pre-receive", fs.ModePerm, "pre-receive content") - require.NoError(t, writer.WriteHeader(&tar.Header{ - Name: "custom_hooks/subdirectory/", - Mode: int64(perm.PrivateDir), - })) - writeFile(writer, "custom_hooks/subdirectory/supporting-file", perm.PrivateFile, "supporting-file content") - writeFile(writer, "ignored_file", fs.ModePerm, "ignored content") - writeFile(writer, "ignored_directory/ignored_file", fs.ModePerm, "ignored content") - defer testhelper.MustClose(t, writer) - return &buffer - } - - for _, tc := range []struct { - desc string - archive io.Reader - stripPrefix bool - expectedState testhelper.DirectoryState - expectedErrorMessage string - }{ - { - desc: "empty reader", - archive: strings.NewReader(""), - expectedState: testhelper.DirectoryState{ - "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, - }, - }, - { - desc: "empty archive", - archive: func() io.Reader { - var buffer bytes.Buffer - writer := tar.NewWriter(&buffer) - defer testhelper.MustClose(t, writer) - return &buffer - }(), - expectedState: testhelper.DirectoryState{ - "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, - }, - }, - { - desc: "just custom_hooks directory", - archive: func() io.Reader { - var buffer bytes.Buffer - writer := tar.NewWriter(&buffer) - require.NoError(t, writer.WriteHeader(&tar.Header{ - Name: "custom_hooks/", - Mode: int64(fs.ModePerm), - })) - defer testhelper.MustClose(t, writer) - return &buffer - }(), - expectedState: testhelper.DirectoryState{ - "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, - "/custom_hooks": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, - }, - }, - { - desc: "custom_hooks dir extracted", - archive: validArchive(), - expectedState: testhelper.DirectoryState{ - "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, - "/custom_hooks": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, - "/custom_hooks/pre-receive": {Mode: umask.Mask(fs.ModePerm), Content: []byte("pre-receive content")}, - "/custom_hooks/subdirectory": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)}, - "/custom_hooks/subdirectory/supporting-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("supporting-file content")}, - }, - }, - { - desc: "custom_hooks dir extracted with prefix stripped", - archive: validArchive(), - stripPrefix: true, - expectedState: testhelper.DirectoryState{ - "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, - "/pre-receive": {Mode: umask.Mask(fs.ModePerm), Content: []byte("pre-receive content")}, - "/subdirectory": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)}, - "/subdirectory/supporting-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("supporting-file content")}, - }, - }, - { - desc: "corrupted archive", - archive: strings.NewReader("invalid tar content"), - expectedErrorMessage: "waiting for tar command completion: exit status", - expectedState: testhelper.DirectoryState{ - "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, - }, - }, - } { - tc := tc - t.Run(tc.desc, func(t *testing.T) { - t.Parallel() - - ctx := testhelper.Context(t) - - tmpDir := t.TempDir() - err := ExtractHooks(ctx, tc.archive, tmpDir, tc.stripPrefix) - if tc.expectedErrorMessage != "" { - require.ErrorContains(t, err, tc.expectedErrorMessage) - } else { - require.NoError(t, err) - } - testhelper.RequireDirectoryState(t, tmpDir, "", tc.expectedState) - }) - } -} diff --git a/internal/gitaly/repoutil/custom_hooks.go b/internal/gitaly/repoutil/custom_hooks.go index de8546ecb..48e8785c2 100644 --- a/internal/gitaly/repoutil/custom_hooks.go +++ b/internal/gitaly/repoutil/custom_hooks.go @@ -1,19 +1,25 @@ package repoutil import ( + "bufio" "context" "fmt" "io" "os" "path/filepath" + "strings" "gitlab.com/gitlab-org/gitaly/v15/internal/archive" + "gitlab.com/gitlab-org/gitaly/v15/internal/command" "gitlab.com/gitlab-org/gitaly/v15/internal/git/repository" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/hook" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/storage" "gitlab.com/gitlab-org/gitaly/v15/internal/structerr" ) +// CustomHooksDir is the directory in which the custom hooks are stored in the repository. +// It's also the directory where the hooks are stored in the TAR archive containing the hooks. +const CustomHooksDir = "custom_hooks" + // GetCustomHooks fetches the git hooks for a repository. The hooks are written // to writer as a tar archive containing a `custom_hooks` directory. If no // hooks are present in the repository, the response will have no data. @@ -28,13 +34,58 @@ func GetCustomHooks( return fmt.Errorf("getting repo path: %w", err) } - if _, err := os.Lstat(filepath.Join(repoPath, hook.CustomHooksDir)); os.IsNotExist(err) { + if _, err := os.Lstat(filepath.Join(repoPath, CustomHooksDir)); os.IsNotExist(err) { return nil } - if err := archive.WriteTarball(ctx, writer, repoPath, hook.CustomHooksDir); err != nil { + if err := archive.WriteTarball(ctx, writer, repoPath, CustomHooksDir); err != nil { return structerr.NewInternal("archiving hooks: %w", err) } return nil } + +// ExtractHooks unpacks a tar file containing custom hooks into a `custom_hooks` +// directory at the specified path. If stripPrefix is set, the hooks are extracted directly +// to the target directory instead of in a `custom_hooks` directory in the target directory. +func ExtractHooks(ctx context.Context, reader io.Reader, path string, stripPrefix bool) error { + // GNU tar does not accept an empty file as a valid tar archive and produces + // an error. Since an empty hooks tar is symbolic of a repository having no + // hooks, the reader is peeked to check if there is any data present. + buf := bufio.NewReader(reader) + if _, err := buf.Peek(1); err == io.EOF { + return nil + } + + stripComponents := "0" + if stripPrefix { + stripComponents = "1" + } + + cmdArgs := []string{"-xf", "-", "-C", path, "--strip-components", stripComponents, CustomHooksDir} + + var stderrBuilder strings.Builder + cmd, err := command.New(ctx, append([]string{"tar"}, cmdArgs...), + command.WithStdin(buf), + command.WithStderr(&stderrBuilder)) + if err != nil { + return fmt.Errorf("executing tar command: %w", err) + } + + if err := cmd.Wait(); err != nil { + stderr := stderrBuilder.String() + + // GNU and BSD tar versions have differing errors when attempting to + // extract specified members from a valid tar archive. If the tar + // archive is valid the errors for GNU and BSD tar should have the + // same prefix, which can be checked to validate whether the expected + // content is present in the archive for extraction. + if strings.HasPrefix(stderr, "tar: custom_hooks: Not found in archive") { + return nil + } + + return structerr.New("waiting for tar command completion: %w", err).WithMetadata("stderr", stderr) + } + + return nil +} diff --git a/internal/gitaly/repoutil/custom_hooks_test.go b/internal/gitaly/repoutil/custom_hooks_test.go index a06567427..00adfee53 100644 --- a/internal/gitaly/repoutil/custom_hooks_test.go +++ b/internal/gitaly/repoutil/custom_hooks_test.go @@ -5,8 +5,10 @@ import ( "bytes" "fmt" "io" + "io/fs" "os" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/require" @@ -103,3 +105,123 @@ func TestGetCustomHooks_nonexistentHooks(t *testing.T) { require.Empty(t, buf.String(), "Returned stream should be empty") } + +func TestExtractHooks(t *testing.T) { + umask := perm.GetUmask() + + writeFile := func(writer *tar.Writer, path string, mode fs.FileMode, content string) { + require.NoError(t, writer.WriteHeader(&tar.Header{ + Name: path, + Mode: int64(mode), + Size: int64(len(content)), + })) + _, err := writer.Write([]byte(content)) + require.NoError(t, err) + } + + validArchive := func() io.Reader { + var buffer bytes.Buffer + writer := tar.NewWriter(&buffer) + writeFile(writer, "custom_hooks/pre-receive", fs.ModePerm, "pre-receive content") + require.NoError(t, writer.WriteHeader(&tar.Header{ + Name: "custom_hooks/subdirectory/", + Mode: int64(perm.PrivateDir), + })) + writeFile(writer, "custom_hooks/subdirectory/supporting-file", perm.PrivateFile, "supporting-file content") + writeFile(writer, "ignored_file", fs.ModePerm, "ignored content") + writeFile(writer, "ignored_directory/ignored_file", fs.ModePerm, "ignored content") + defer testhelper.MustClose(t, writer) + return &buffer + } + + for _, tc := range []struct { + desc string + archive io.Reader + stripPrefix bool + expectedState testhelper.DirectoryState + expectedErrorMessage string + }{ + { + desc: "empty reader", + archive: strings.NewReader(""), + expectedState: testhelper.DirectoryState{ + "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, + }, + }, + { + desc: "empty archive", + archive: func() io.Reader { + var buffer bytes.Buffer + writer := tar.NewWriter(&buffer) + defer testhelper.MustClose(t, writer) + return &buffer + }(), + expectedState: testhelper.DirectoryState{ + "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, + }, + }, + { + desc: "just custom_hooks directory", + archive: func() io.Reader { + var buffer bytes.Buffer + writer := tar.NewWriter(&buffer) + require.NoError(t, writer.WriteHeader(&tar.Header{ + Name: "custom_hooks/", + Mode: int64(fs.ModePerm), + })) + defer testhelper.MustClose(t, writer) + return &buffer + }(), + expectedState: testhelper.DirectoryState{ + "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, + "/custom_hooks": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, + }, + }, + { + desc: "custom_hooks dir extracted", + archive: validArchive(), + expectedState: testhelper.DirectoryState{ + "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, + "/custom_hooks": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, + "/custom_hooks/pre-receive": {Mode: umask.Mask(fs.ModePerm), Content: []byte("pre-receive content")}, + "/custom_hooks/subdirectory": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)}, + "/custom_hooks/subdirectory/supporting-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("supporting-file content")}, + }, + }, + { + desc: "custom_hooks dir extracted with prefix stripped", + archive: validArchive(), + stripPrefix: true, + expectedState: testhelper.DirectoryState{ + "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, + "/pre-receive": {Mode: umask.Mask(fs.ModePerm), Content: []byte("pre-receive content")}, + "/subdirectory": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)}, + "/subdirectory/supporting-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("supporting-file content")}, + }, + }, + { + desc: "corrupted archive", + archive: strings.NewReader("invalid tar content"), + expectedErrorMessage: "waiting for tar command completion: exit status", + expectedState: testhelper.DirectoryState{ + "/": {Mode: umask.Mask(fs.ModeDir | fs.ModePerm)}, + }, + }, + } { + tc := tc + t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + + ctx := testhelper.Context(t) + + tmpDir := t.TempDir() + err := ExtractHooks(ctx, tc.archive, tmpDir, tc.stripPrefix) + if tc.expectedErrorMessage != "" { + require.ErrorContains(t, err, tc.expectedErrorMessage) + } else { + require.NoError(t, err) + } + testhelper.RequireDirectoryState(t, tmpDir, "", tc.expectedState) + }) + } +} diff --git a/internal/gitaly/service/repository/replicate_test.go b/internal/gitaly/service/repository/replicate_test.go index f3d9d8412..9e9f7f11b 100644 --- a/internal/gitaly/service/repository/replicate_test.go +++ b/internal/gitaly/service/repository/replicate_test.go @@ -23,6 +23,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v15/internal/git/localrepo" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config" gitalyhook "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/hook" + "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/repoutil" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/storage" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/transaction" "gitlab.com/gitlab-org/gitaly/v15/internal/helper/perm" @@ -678,12 +679,12 @@ func TestReplicateRepository_hooks(t *testing.T) { archivePath := mustCreateCustomHooksArchive(t, ctx, []testFile{ {name: "pre-commit.sample", content: "foo", mode: 0o755}, {name: "pre-push.sample", content: "bar", mode: 0o755}, - }, gitalyhook.CustomHooksDir) + }, repoutil.CustomHooksDir) hooks, err := os.Open(archivePath) require.NoError(t, err) - err = gitalyhook.ExtractHooks(ctx, hooks, sourceRepoPath, false) + err = repoutil.ExtractHooks(ctx, hooks, sourceRepoPath, false) require.NoError(t, err) targetRepo := proto.Clone(sourceRepo).(*gitalypb.Repository) @@ -698,7 +699,7 @@ func TestReplicateRepository_hooks(t *testing.T) { require.NoError(t, err) targetRepoPath := filepath.Join(cfg.Storages[1].Path, gittest.GetReplicaPath(t, ctx, cfg, targetRepo)) - targetHooksPath := filepath.Join(targetRepoPath, gitalyhook.CustomHooksDir) + targetHooksPath := filepath.Join(targetRepoPath, repoutil.CustomHooksDir) // Make sure target repo contains replicated custom hooks from source repository. require.FileExists(t, filepath.Join(targetHooksPath, "pre-push.sample")) diff --git a/internal/gitaly/service/repository/set_custom_hooks.go b/internal/gitaly/service/repository/set_custom_hooks.go index 3616b8136..8d445b346 100644 --- a/internal/gitaly/service/repository/set_custom_hooks.go +++ b/internal/gitaly/service/repository/set_custom_hooks.go @@ -12,7 +12,7 @@ import ( "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" "gitlab.com/gitlab-org/gitaly/v15/internal/git/repository" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/hook" + "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/repoutil" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/service" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/transaction" "gitlab.com/gitlab-org/gitaly/v15/internal/helper/perm" @@ -104,7 +104,7 @@ func (s *server) setCustomHooks(ctx context.Context, reader io.Reader, repo repo // The `custom_hooks` directory in the repository is locked to prevent // concurrent modification of hooks. - hooksLock, err := safe.NewLockingDirectory(repoPath, hook.CustomHooksDir) + hooksLock, err := safe.NewLockingDirectory(repoPath, repoutil.CustomHooksDir) if err != nil { return fmt.Errorf("creating hooks lock: %w", err) } @@ -136,11 +136,11 @@ func (s *server) setCustomHooks(ctx context.Context, reader io.Reader, repo repo } }() - if err := hook.ExtractHooks(ctx, reader, tmpDir.Path(), false); err != nil { + if err := repoutil.ExtractHooks(ctx, reader, tmpDir.Path(), false); err != nil { return fmt.Errorf("extracting hooks: %w", err) } - tempHooksPath := filepath.Join(tmpDir.Path(), hook.CustomHooksDir) + tempHooksPath := filepath.Join(tmpDir.Path(), repoutil.CustomHooksDir) // No hooks will be extracted if the tar archive is empty. If this happens // it means the repository should be set with an empty `custom_hooks` @@ -161,7 +161,7 @@ func (s *server) setCustomHooks(ctx context.Context, reader io.Reader, repo repo return fmt.Errorf("casting prepared vote: %w", err) } - repoHooksPath := filepath.Join(repoPath, hook.CustomHooksDir) + repoHooksPath := filepath.Join(repoPath, repoutil.CustomHooksDir) prevHooksPath := filepath.Join(tmpDir.Path(), "previous_hooks") // If the `custom_hooks` directory exists in the repository, move the diff --git a/internal/gitaly/service/repository/set_custom_hooks_test.go b/internal/gitaly/service/repository/set_custom_hooks_test.go index 15c9df6e4..c1b0f652b 100644 --- a/internal/gitaly/service/repository/set_custom_hooks_test.go +++ b/internal/gitaly/service/repository/set_custom_hooks_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v15/internal/archive" "gitlab.com/gitlab-org/gitaly/v15/internal/git/gittest" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/hook" + "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/repoutil" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/transaction" "gitlab.com/gitlab-org/gitaly/v15/internal/helper/perm" "gitlab.com/gitlab-org/gitaly/v15/internal/metadata" @@ -113,7 +113,7 @@ func TestSetCustomHooksRequest_success(t *testing.T) { archivePath := mustCreateCustomHooksArchive(t, ctx, []testFile{ {name: "pre-commit.sample", content: "foo", mode: 0o755}, {name: "pre-push.sample", content: "bar", mode: 0o755}, - }, hook.CustomHooksDir) + }, repoutil.CustomHooksDir) file, err := os.Open(archivePath) require.NoError(t, err) @@ -122,7 +122,7 @@ func TestSetCustomHooksRequest_success(t *testing.T) { require.NoError(t, err) closeStream() - voteHash, err := newDirectoryVote(filepath.Join(repoPath, hook.CustomHooksDir)) + voteHash, err := newDirectoryVote(filepath.Join(repoPath, repoutil.CustomHooksDir)) require.NoError(t, err) testhelper.MustClose(t, file) @@ -321,7 +321,7 @@ func TestNewDirectoryVote(t *testing.T) { }, } { t.Run(tc.desc, func(t *testing.T) { - path := mustWriteCustomHookDirectory(t, tc.files, hook.CustomHooksDir) + path := mustWriteCustomHookDirectory(t, tc.files, repoutil.CustomHooksDir) voteHash, err := newDirectoryVote(path) require.NoError(t, err) diff --git a/internal/gitaly/transaction_manager.go b/internal/gitaly/transaction_manager.go index a05d1a7a2..d63c3c326 100644 --- a/internal/gitaly/transaction_manager.go +++ b/internal/gitaly/transaction_manager.go @@ -18,7 +18,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v15/internal/git" "gitlab.com/gitlab-org/gitaly/v15/internal/git/localrepo" "gitlab.com/gitlab-org/gitaly/v15/internal/git/updateref" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/hook" + "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/repoutil" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/transaction" "gitlab.com/gitlab-org/gitaly/v15/internal/safe" "gitlab.com/gitlab-org/gitaly/v15/proto/go/gitalypb" @@ -816,7 +816,7 @@ func (mgr *TransactionManager) applyCustomHooks(ctx context.Context, logIndex Lo } } - if err := hook.ExtractHooks(ctx, bytes.NewReader(update.CustomHooksTar), targetDirectory, true); err != nil { + if err := repoutil.ExtractHooks(ctx, bytes.NewReader(update.CustomHooksTar), targetDirectory, true); err != nil { return fmt.Errorf("extract hooks: %w", err) } |